summaryrefslogtreecommitdiff
path: root/gettext-tools/src
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-12-24 07:38:37 +0000
committer <>2015-02-02 12:02:29 +0000
commit482840e61f86ca321838a91e902c41d40c098bbb (patch)
tree01ea2e242fd2792d19fe192476601587901db794 /gettext-tools/src
downloadgettext-tarball-482840e61f86ca321838a91e902c41d40c098bbb.tar.gz
Imported from /home/lorry/working-area/delta_gettext-tarball/gettext-0.19.4.tar.xz.gettext-0.19.4
Diffstat (limited to 'gettext-tools/src')
-rw-r--r--gettext-tools/src/ChangeLog7833
-rw-r--r--gettext-tools/src/ChangeLog.05247
-rw-r--r--gettext-tools/src/FILES363
-rw-r--r--gettext-tools/src/Makefile.am621
-rw-r--r--gettext-tools/src/Makefile.in3511
-rw-r--r--gettext-tools/src/color.c445
-rw-r--r--gettext-tools/src/color.h57
-rw-r--r--gettext-tools/src/dir-list.c85
-rw-r--r--gettext-tools/src/dir-list.h51
-rw-r--r--gettext-tools/src/file-list.c92
-rw-r--r--gettext-tools/src/file-list.h40
-rw-r--r--gettext-tools/src/filter-quote.c224
-rw-r--r--gettext-tools/src/filter-sr-latin.c400
-rw-r--r--gettext-tools/src/filters.h51
-rw-r--r--gettext-tools/src/format-awk.c663
-rw-r--r--gettext-tools/src/format-boost.c772
-rw-r--r--gettext-tools/src/format-c-parse.h854
-rw-r--r--gettext-tools/src/format-c.c376
-rw-r--r--gettext-tools/src/format-csharp.c293
-rw-r--r--gettext-tools/src/format-elisp.c502
-rw-r--r--gettext-tools/src/format-gcc-internal.c852
-rw-r--r--gettext-tools/src/format-gfc-internal.c504
-rw-r--r--gettext-tools/src/format-invalid.h40
-rw-r--r--gettext-tools/src/format-java.c893
-rw-r--r--gettext-tools/src/format-javascript.c333
-rw-r--r--gettext-tools/src/format-kde.c349
-rw-r--r--gettext-tools/src/format-librep.c463
-rw-r--r--gettext-tools/src/format-lisp.c3658
-rw-r--r--gettext-tools/src/format-lua.c348
-rw-r--r--gettext-tools/src/format-pascal.c548
-rw-r--r--gettext-tools/src/format-perl-brace.c294
-rw-r--r--gettext-tools/src/format-perl.c768
-rw-r--r--gettext-tools/src/format-php.c501
-rw-r--r--gettext-tools/src/format-python-brace.c542
-rw-r--r--gettext-tools/src/format-python.c695
-rw-r--r--gettext-tools/src/format-qt-plural.c194
-rw-r--r--gettext-tools/src/format-qt.c266
-rw-r--r--gettext-tools/src/format-scheme.c3581
-rw-r--r--gettext-tools/src/format-sh.c403
-rw-r--r--gettext-tools/src/format-tcl.c550
-rw-r--r--gettext-tools/src/format-ycp.c250
-rw-r--r--gettext-tools/src/format.c197
-rw-r--r--gettext-tools/src/format.h175
-rw-r--r--gettext-tools/src/gnu/gettext/DumpResource.java236
-rw-r--r--gettext-tools/src/gnu/gettext/GetURL.java81
-rw-r--r--gettext-tools/src/hostname.c389
-rw-r--r--gettext-tools/src/lang-table.c315
-rw-r--r--gettext-tools/src/lang-table.h35
-rw-r--r--gettext-tools/src/libexpat-compat.c326
-rw-r--r--gettext-tools/src/libexpat-compat.h94
-rw-r--r--gettext-tools/src/message.c914
-rw-r--r--gettext-tools/src/message.h370
-rw-r--r--gettext-tools/src/msgattrib.c683
-rw-r--r--gettext-tools/src/msgcat.c485
-rw-r--r--gettext-tools/src/msgcmp.c555
-rw-r--r--gettext-tools/src/msgcomm.c471
-rw-r--r--gettext-tools/src/msgconv.c401
-rw-r--r--gettext-tools/src/msgen.c397
-rw-r--r--gettext-tools/src/msgexec.c468
-rw-r--r--gettext-tools/src/msgfilter.c753
-rw-r--r--gettext-tools/src/msgfmt.c1473
-rw-r--r--gettext-tools/src/msgfmt.cs119
-rw-r--r--gettext-tools/src/msgfmt.h27
-rw-r--r--gettext-tools/src/msggrep.c860
-rw-r--r--gettext-tools/src/msginit.c1776
-rw-r--r--gettext-tools/src/msgl-ascii.c112
-rw-r--r--gettext-tools/src/msgl-ascii.h48
-rw-r--r--gettext-tools/src/msgl-cat.c799
-rw-r--r--gettext-tools/src/msgl-cat.h60
-rw-r--r--gettext-tools/src/msgl-charset.c133
-rw-r--r--gettext-tools/src/msgl-charset.h38
-rw-r--r--gettext-tools/src/msgl-check.c914
-rw-r--r--gettext-tools/src/msgl-check.h68
-rw-r--r--gettext-tools/src/msgl-english.c70
-rw-r--r--gettext-tools/src/msgl-english.h38
-rw-r--r--gettext-tools/src/msgl-equal.c250
-rw-r--r--gettext-tools/src/msgl-equal.h56
-rw-r--r--gettext-tools/src/msgl-fsearch.c668
-rw-r--r--gettext-tools/src/msgl-fsearch.h69
-rw-r--r--gettext-tools/src/msgl-header.c170
-rw-r--r--gettext-tools/src/msgl-header.h43
-rw-r--r--gettext-tools/src/msgl-iconv.c597
-rw-r--r--gettext-tools/src/msgl-iconv.h87
-rw-r--r--gettext-tools/src/msgmerge.c2061
-rw-r--r--gettext-tools/src/msgunfmt.c555
-rw-r--r--gettext-tools/src/msgunfmt.cs241
-rw-r--r--gettext-tools/src/msgunfmt.h24
-rw-r--r--gettext-tools/src/msgunfmt.tcl82
-rw-r--r--gettext-tools/src/msguniq.c434
-rw-r--r--gettext-tools/src/open-catalog.c128
-rw-r--r--gettext-tools/src/open-catalog.h43
-rw-r--r--gettext-tools/src/plural-count.c38
-rw-r--r--gettext-tools/src/plural-count.h29
-rw-r--r--gettext-tools/src/plural-distrib.h57
-rw-r--r--gettext-tools/src/plural-eval.c93
-rw-r--r--gettext-tools/src/plural-eval.h67
-rw-r--r--gettext-tools/src/plural-exp.c21
-rw-r--r--gettext-tools/src/plural-table.c67
-rw-r--r--gettext-tools/src/plural-table.h33
-rw-r--r--gettext-tools/src/po-charset.c662
-rw-r--r--gettext-tools/src/po-charset.h93
-rw-r--r--gettext-tools/src/po-error.c42
-rw-r--r--gettext-tools/src/po-error.h76
-rw-r--r--gettext-tools/src/po-gram-gen.c1922
-rw-r--r--gettext-tools/src/po-gram-gen.h106
-rw-r--r--gettext-tools/src/po-gram-gen.y450
-rw-r--r--gettext-tools/src/po-gram-gen2.h106
-rw-r--r--gettext-tools/src/po-gram.h32
-rw-r--r--gettext-tools/src/po-lex.c1151
-rw-r--r--gettext-tools/src/po-lex.h103
-rw-r--r--gettext-tools/src/po-time.c75
-rw-r--r--gettext-tools/src/po-time.h38
-rw-r--r--gettext-tools/src/po-xerror.c199
-rw-r--r--gettext-tools/src/po-xerror.h82
-rw-r--r--gettext-tools/src/pos.h32
-rwxr-xr-xgettext-tools/src/project-id86
-rw-r--r--gettext-tools/src/read-catalog-abstract.c748
-rw-r--r--gettext-tools/src/read-catalog-abstract.h195
-rw-r--r--gettext-tools/src/read-catalog.c494
-rw-r--r--gettext-tools/src/read-catalog.h192
-rw-r--r--gettext-tools/src/read-csharp.c168
-rw-r--r--gettext-tools/src/read-csharp.h30
-rw-r--r--gettext-tools/src/read-desktop.c645
-rw-r--r--gettext-tools/src/read-desktop.h121
-rw-r--r--gettext-tools/src/read-java.c138
-rw-r--r--gettext-tools/src/read-java.h29
-rw-r--r--gettext-tools/src/read-mo.c467
-rw-r--r--gettext-tools/src/read-mo.h26
-rw-r--r--gettext-tools/src/read-po.c48
-rw-r--r--gettext-tools/src/read-po.h26
-rw-r--r--gettext-tools/src/read-properties.c560
-rw-r--r--gettext-tools/src/read-properties.h26
-rw-r--r--gettext-tools/src/read-resources.c138
-rw-r--r--gettext-tools/src/read-resources.h27
-rw-r--r--gettext-tools/src/read-stringtable.c963
-rw-r--r--gettext-tools/src/read-stringtable.h26
-rw-r--r--gettext-tools/src/read-tcl.c157
-rw-r--r--gettext-tools/src/read-tcl.h28
-rw-r--r--gettext-tools/src/recode-sr-latin.c395
-rw-r--r--gettext-tools/src/str-list.c236
-rw-r--r--gettext-tools/src/str-list.h88
-rw-r--r--gettext-tools/src/urlget.c448
-rw-r--r--gettext-tools/src/user-email.sh.in435
-rw-r--r--gettext-tools/src/write-catalog.c469
-rw-r--r--gettext-tools/src/write-catalog.h91
-rw-r--r--gettext-tools/src/write-csharp.c783
-rw-r--r--gettext-tools/src/write-csharp.h35
-rw-r--r--gettext-tools/src/write-desktop.c225
-rw-r--r--gettext-tools/src/write-desktop.h51
-rw-r--r--gettext-tools/src/write-java.c1234
-rw-r--r--gettext-tools/src/write-java.h39
-rw-r--r--gettext-tools/src/write-mo.c817
-rw-r--r--gettext-tools/src/write-mo.h43
-rw-r--r--gettext-tools/src/write-po.c1620
-rw-r--r--gettext-tools/src/write-po.h87
-rw-r--r--gettext-tools/src/write-properties.c305
-rw-r--r--gettext-tools/src/write-properties.h26
-rw-r--r--gettext-tools/src/write-qt.c754
-rw-r--r--gettext-tools/src/write-qt.h30
-rw-r--r--gettext-tools/src/write-resources.c194
-rw-r--r--gettext-tools/src/write-resources.h31
-rw-r--r--gettext-tools/src/write-stringtable.c335
-rw-r--r--gettext-tools/src/write-stringtable.h26
-rw-r--r--gettext-tools/src/write-tcl.c229
-rw-r--r--gettext-tools/src/write-tcl.h32
-rw-r--r--gettext-tools/src/x-awk.c887
-rw-r--r--gettext-tools/src/x-awk.h51
-rw-r--r--gettext-tools/src/x-c.c2174
-rw-r--r--gettext-tools/src/x-c.h92
-rw-r--r--gettext-tools/src/x-csharp.c2147
-rw-r--r--gettext-tools/src/x-csharp.h50
-rw-r--r--gettext-tools/src/x-desktop.c193
-rw-r--r--gettext-tools/src/x-desktop.h47
-rw-r--r--gettext-tools/src/x-elisp.c1255
-rw-r--r--gettext-tools/src/x-elisp.h54
-rw-r--r--gettext-tools/src/x-glade.c612
-rw-r--r--gettext-tools/src/x-glade.h53
-rw-r--r--gettext-tools/src/x-gsettings.c386
-rw-r--r--gettext-tools/src/x-gsettings.h45
-rw-r--r--gettext-tools/src/x-java.c1497
-rw-r--r--gettext-tools/src/x-java.h50
-rw-r--r--gettext-tools/src/x-javascript.c1673
-rw-r--r--gettext-tools/src/x-javascript.h52
-rw-r--r--gettext-tools/src/x-librep.c1133
-rw-r--r--gettext-tools/src/x-librep.h54
-rw-r--r--gettext-tools/src/x-lisp.c1425
-rw-r--r--gettext-tools/src/x-lisp.h54
-rw-r--r--gettext-tools/src/x-lua.c1215
-rw-r--r--gettext-tools/src/x-lua.h48
-rw-r--r--gettext-tools/src/x-perl.c3592
-rw-r--r--gettext-tools/src/x-perl.h55
-rw-r--r--gettext-tools/src/x-php.c1605
-rw-r--r--gettext-tools/src/x-php.h53
-rw-r--r--gettext-tools/src/x-po.c240
-rw-r--r--gettext-tools/src/x-po.h46
-rw-r--r--gettext-tools/src/x-properties.h45
-rw-r--r--gettext-tools/src/x-python.c1779
-rw-r--r--gettext-tools/src/x-python.h51
-rw-r--r--gettext-tools/src/x-rst.c236
-rw-r--r--gettext-tools/src/x-rst.h46
-rw-r--r--gettext-tools/src/x-scheme.c1339
-rw-r--r--gettext-tools/src/x-scheme.h54
-rw-r--r--gettext-tools/src/x-sh.c1360
-rw-r--r--gettext-tools/src/x-sh.h52
-rw-r--r--gettext-tools/src/x-smalltalk.c598
-rw-r--r--gettext-tools/src/x-smalltalk.h46
-rw-r--r--gettext-tools/src/x-stringtable.h45
-rw-r--r--gettext-tools/src/x-tcl.c999
-rw-r--r--gettext-tools/src/x-tcl.h54
-rw-r--r--gettext-tools/src/x-vala.c1250
-rw-r--r--gettext-tools/src/x-vala.h50
-rw-r--r--gettext-tools/src/x-ycp.c788
-rw-r--r--gettext-tools/src/x-ycp.h48
-rw-r--r--gettext-tools/src/xgettext.c3701
-rw-r--r--gettext-tools/src/xgettext.h416
215 files changed, 111665 insertions, 0 deletions
diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog
new file mode 100644
index 0000000..fbfa43f
--- /dev/null
+++ b/gettext-tools/src/ChangeLog
@@ -0,0 +1,7833 @@
+2014-12-24 Daiki Ueno <ueno@gnu.org>
+
+ * gettext 0.19.4 released.
+
+2014-12-18 Daiki Ueno <ueno@gnu.org>
+
+ * x-sh.c (read_word): Use phase1 instead of phase2 for Bash ANSI-C
+ escape sequences. Also handle '\"' and '\E'.
+
+2014-12-17 Daiki Ueno <ueno@gnu.org>
+
+ * x-sh.c (phase2_getc): Fix typo: debackslahificication ->
+ debackslashification.
+
+2014-12-12 Daiki Ueno <ueno@gnu.org>
+
+ * read-desktop.c (desktop_parse): Check and ignore
+ token_type_other.
+
+2014-12-12 Daiki Ueno <ueno@gnu.org>
+
+ * format-lisp.c (make_intersected_list): Don't dereference
+ potentially released memory. 'append_repeated_to_initial' may
+ release the LIST->element. Spotted by clang-analyzer.
+ * format-scheme.c (make_intersected_list): Likewise.
+
+2014-12-10 Daiki Ueno <ueno@gnu.org>
+
+ * msgl-check.c (check_header_entry): Don't declare unused variable
+ 'nrequiredfields'.
+
+2014-12-09 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (arglist_parser_done): Avoid potential
+ null-dereference. Spotted by clang-analyzer.
+
+2014-12-09 Daiki Ueno <ueno@gnu.org>
+
+ * msgfmt.c (msgfmt_desktop_bulk): Don't dereference potentially
+ uninitialized value. Spotted by clang-analyzer.
+
+2014-12-09 Daiki Ueno <ueno@gnu.org>
+
+ * x-vala.c (phase3_get): Factor out the buffer allocation as a
+ macro.
+
+2014-12-09 Daiki Ueno <ueno@gnu.org>
+
+ * read-desktop.c (desktop_lex): Undef the APPEND macro before
+ defining.
+
+2014-12-09 Daiki Ueno <ueno@gnu.org>
+
+ desktop: Simplify the parsing logic
+ * read-desktop.h (desktop_reader_class_ty): Rename 'handle_text'
+ to 'handle_blank'.
+ (desktop_reader_handle_blank): Rename from
+ 'desktop_reader_handle_text'.
+ * read-desktop.c (SIZEOF): New macro.
+ (desktop_reader_handle_blank): Rename from
+ 'desktop_reader_handle_blank'.
+ (read_until_newline, read_group_name, read_key_name): Remove.
+ Merge into...
+ (desktop_lex): ...here.
+ (desktop_parse): Call 'desktop_lex' instead of read_*. Don't
+ normalize whitespaces.
+ (enum token_type_ty): New enum.
+ (struct token_ty): New struct.
+ (free_token): New function.
+ * write-desktop.c (msgfmt_desktop_handle_blank): Rename from
+ 'msgfmt_desktop_handle_text'.
+ * x-desktop.c: Include "c-ctype.h".
+ (extract_desktop_handle_comment): Normalize whitespaces here.
+ (extract_desktop_handle_blank): Rename from
+ 'extract_desktop_handle_text'.
+
+2014-12-07 Daiki Ueno <ueno@gnu.org>
+
+ vala: Make regex literal handling robuster
+ * x-vala.c (token_type_ty): New enumeration values
+ 'token_type_arithmetic_operator' and 'token_type_question'.
+ Remove 'token_type_minus'.
+ (phase3_get): Rewrite regex literal handling right after
+ arithmetic assignment operators.
+
+2014-12-05 Daiki Ueno <ueno@gnu.org>
+
+ msgunfmt: Avoid integer overflow using xsize
+ * read-mo.c (get_uint32, get_sysdep_string): Use xsum to avoid
+ integer overflow, when checking length and offset fields.
+ Reported by Jakub Wilk at:
+ <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772088>.
+
+2014-12-04 Daiki Ueno <ueno@gnu.org>
+
+ libgettextsrc: Follow plural.c -> pluralx.c file name change
+ * plural-exp.c: Include "../intl/pluralx.c" instead of
+ "../intl/plural.c". Adjust to commit d2d04ba9.
+
+2014-12-03 Daiki Ueno <ueno@gnu.org>
+
+ intl: Work around LCOV relative base directory resolution
+ * plural-exp.c: Include "../intl/plural.c" instead of
+ "../../gettext-runtime/intl/plural.c".
+
+2014-12-02 Daiki Ueno <ueno@gnu.org>
+
+ c: Minor cleanup of the previous commit
+ * x-c.c (phase5_get): Remove redundant check of is_prefix; fit
+ lines to 80 column.
+
+2014-12-02 Daiki Ueno <ueno@gnu.org>
+
+ c: Support C++11 string literals
+ * x-c.c (phase5_get): Recognize C++ string literals, defined in
+ ISO/IEC 9899:2011. Reported at:
+ <https://savannah.gnu.org/bugs/?39499>.
+
+2014-12-01 Daiki Ueno <ueno@gnu.org>
+
+ c#: Recognize Unicode surrogate character pair
+ * x-csharp.c (accumulate_escaped): Change the first argument type
+ from 'struct string_buffer *' to 'struct mixed_string_buffer *',
+ for Unicode surrogate character pair handling; all callers
+ changed. Reported by Petr Kadlec at:
+ <https://savannah.gnu.org/bugs/?32505>.
+
+2014-11-28 Daiki Ueno <ueno@gnu.org>
+
+ * msgfilter.c (prepare_read): Simplify the last commit 06e206f5,
+ by always adding 1 to the buffer size.
+
+2014-11-28 Daiki Ueno <ueno@gnu.org>
+
+ msgfilter: Fix read buffer allocation for empty input
+ * msgfilter.c (prepare_read): Increase allocated buffer size even
+ if the original size is < 2.
+ Reported by Robin McCorkell at:
+ <https://savannah.gnu.org/bugs/?43720>.
+
+2014-11-27 Daiki Ueno <ueno@gnu.org>
+
+ javascript: Simplify Unicode character escape handling
+ ECMA-262 only supports "\uXXXX" style Unicode character escape and
+ we don't need extra space for character names.
+ * x-javascript.c: Don't include "uniname.h".
+ (phase2_pushback): Decrease to 5. Don't refer to UNINAME_MAX.
+
+2014-11-18 Daiki Ueno <ueno@gnu.org>
+
+ * read-mo.c: Include "xsize.h".
+ (get_string): Use xsum3 to avoid overflow, when checking length
+ and offset fields.
+ Reported by Jakub Wilk at:
+ <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=769901>.
+
+2014-10-28 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Allow plural extraction from a single argument function
+ The commit 8137d2b4 was a wrong fix since both singular/plural msgids
+ may point to the same address for Qt4 plural forms. This reverts
+ the commit and fix the original double-free problem in the right
+ way. Thanks to Jesper Fehrlund for suggestions.
+ * xgettext.c (arglist_parser_remember_literal): Don't ignore
+ plural argument even if ARGNUM1 equals to ARGNUM2.
+ (arglist_parser_done): Make a copy of best_cp->msgid_plural when
+ passing it to remember_a_message_plural, if it equals to
+ best_cp->msgid. Also move code conversion logic earlier taking
+ into account of the ownership transfer of best_cp->msgid.
+
+2014-10-28 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Fix double-free in singular/plural argument extraction
+ After commit 6aa7b7ed in 2009, xgettext assumed that ARGNUM1 and
+ ARGNUM2 of -k are different. That could cause an double-free in
+ exceptional cases.
+ Reported by Johan Liljegren in:
+ <https://lists.gnu.org/archive/html/bug-gettext/2014-10/msg00028.html>.
+ * xgettext.c (arglist_parser_remember_literal): Don't assume that
+ ARGNUM1 and ARGNUM2 are different.
+
+2014-10-15 Daiki Ueno <ueno@gnu.org>
+
+ * gettext 0.19.3 released.
+
+2014-10-08 Daiki Ueno <ueno@gnu.org>
+
+ * write-po.c (wrap): Report error on incomplete multibyte sequence
+ at the end of input bytes.
+ Reported by Jakub Wilk at:
+ <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=763820>.
+
+2014-09-30 Daiki Ueno <ueno@gnu.org>
+
+ * x-c.c (literalstring_parse): Fix octal character escape handling.
+ Reported by Kjartan Maraas at:
+ <https://bugzilla.redhat.com/show_bug.cgi?id=1147535>.
+
+2014-09-24 Daiki Ueno <ueno@gnu.org>
+
+ * x-python.c (x_python_lex): Move 'token3' variable declaration
+ out of the internal block.
+
+2014-09-24 Daiki Ueno <ueno@gnu.org>
+
+ * filter-quote.c (BOLD_START, BOLD_END): Don't use non-portable
+ character escape "\e".
+
+2014-08-28 Jonas 'Sortie' Termansen <sortie@maxsi.org> (tiny change)
+
+ * msginit.c: Include <stdint.h>.
+ (get_user_pwd): Cast uid_t value into uintmax_t and print it with
+ '%ju' format directive.
+
+2014-08-27 Jonas 'Sortie' Termansen <sortie@maxsi.org> (tiny change)
+
+ * msgfilter.c (process_message): Use proper format directive for
+ printing size_t.
+ * msgexec.c (process_message): Likewise.
+
+2014-08-27 Jonas 'Sortie' Termansen <sortie@maxsi.org> (tiny change)
+
+ * hostname.c: Add guard around #include <sys/param.h>.
+
+2014-07-14 Daiki Ueno <ueno@gnu.org>
+
+ * gettext 0.19.2 released.
+
+2014-07-14 Daiki Ueno <ueno@gnu.org>
+
+ vala: Fix empty string literal handling
+ Similar to the commit 7b2d8d61 on July 11.
+ * x-vala.c (phase3_get): Add missing memory allocation for empty
+ string literal.
+
+2014-07-14 Daiki Ueno <ueno@gnu.org>
+
+ build: Fix race in post-install removal of *.a
+ There was an implicit dependency between the prerequisites of
+ install-exec-local, which may have caused error with make -jN.
+ Use install-exec-hook to remove the dependency.
+ Reported by Christian Weisgerber in:
+ <https://lists.gnu.org/archive/html/bug-gettext/2014-07/msg00008.html>.
+ * Makefile.am (install-exec-local): Remove.
+ (install-exec-hook): New rule, depend on install-exec-clean.
+
+2014-07-11 Daiki Ueno <ueno@gnu.org>
+
+ c: Fix empty string literal handling
+ Problem reported by Bernhard Voelker in:
+ <http://lists.gnu.org/archive/html/bug-gnulib/2014-07/msg00059.html>.
+ * x-c.c (phase5_get): Add missing memory allocation for empty
+ string literal.
+
+2014-06-10 Daiki Ueno <ueno@gnu.org>
+
+ * gettext 0.19.1 released.
+
+2014-06-09 Daiki Ueno <ueno@gnu.org>
+
+ msgmerge: Disable --color option in --update mode
+ Suggested by 林V字龍 at:
+ <https://lists.gnu.org/archive/html/bug-gettext/2014-06/msg00017.html>.
+ * msgmerge.c (main): Error out when --color and --update are
+ specified at the same time.
+
+2014-06-07 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Fix misrecognition of character literals in C and Vala
+ Problem reported by Paul Eggert at
+ <http://lists.gnu.org/archive/html/bug-gettext/2014-06/msg00015.html>.
+ * x-c.c (phase5_get): Make sure to skip contents of character constant.
+ * x-vala.c (phase3_get): Likewise.
+
+2014-06-03 Daiki Ueno <ueno@gnu.org>
+
+ desktop: Use logical filename as msgid location
+ * read-desktop.c (desktop_parse): Pass logical filename instead of
+ real filename to desktop_reader_handle_pair.
+
+2014-06-03 Daiki Ueno <ueno@gnu.org>
+
+ desktop: Read LINGUAS file even if LINGUAS envvar is set
+ * msgfmt.c (get_languages): If the LINGUAS envvar is set, use it
+ to restrict the languages list read from the LINGUAS file, not to
+ extend the list.
+ (add_languages): Take an optional DESIRED_LANGUAGES argument.
+
+2014-06-03 Daiki Ueno <ueno@gnu.org>
+
+ vala: Don't elide comments while parsing string literal
+ * x-vala.c (phase3_get): Use phase1_getc to read characters inside
+ a string literal.
+
+2014-06-02 Daiki Ueno <ueno@gnu.org>
+
+ msgl-check: Treat missing header errors as warnings
+ Problem reported by Richard Hughes at
+ <https://lists.fedoraproject.org/pipermail/devel/2014-June/199539.html>.
+ After <https://savannah.gnu.org/bugs/?40262>, "msgfmt -c" reports
+ error on missing PO headers. However, it turned out to be too strict
+ for the projects using Launchpad Translations. Let's relax the check
+ for the moment and wait for one or two release cycles.
+ * msgl-check.c (check_header_entry): Report missing
+ required headers as warning, instead of error.
+
+2014-06-02 Daiki Ueno <ueno@gnu.org>
+
+ * gettext 0.19 released.
+
+2014-05-31 Daiki Ueno <ueno@gnu.org>
+
+ msgexec: Pass previous msgid to the child process
+ Suggested by Pavel Kharitonov in:
+ <http://lists.gnu.org/archive/html/bug-gettext/2014-05/msg00022.html>.
+ * msgexec.c (process_string): Set MSGEXEC_PREV_* envvar.
+
+2014-05-31 Daiki Ueno <ueno@gnu.org>
+
+ msgfilter: Pass previous msgid to the child process
+ Suggested by Pavel Kharitonov in:
+ <http://lists.gnu.org/archive/html/bug-gettext/2014-05/msg00022.html>.
+ * msgfilter.c (process_message): Set MSGFILTER_PREV_* envvar.
+
+2014-05-19 Daiki Ueno <ueno@gnu.org>
+
+ * Makefile.am (po-gram-gen2.h): Adjust the directory to which
+ po-gram-gen.h, for VPATH build.
+
+2014-05-15 Stanislav Brabec <sbrabec@suse.cz> (tiny change)
+
+ msgfilter: Implement plural support
+ * msgfilter.c (process_message): Set or unset
+ MSGFILTER_MSGID_PLURAL and MSGFILTER_PLURAL_FORM.
+
+2014-05-15 Stanislav Brabec <sbrabec@suse.cz> (tiny change)
+
+ msgexec: Implement plural support
+ * msgexec.c (process_string): Set or unset MSGEXEC_MSGID_PLURAL.
+ (process_message): Set or unset MSGEXEC_PLURAL_FORM.
+
+2014-05-14 Daiki Ueno <ueno@gnu.org>
+
+ msgfmt: Report error on accelerator mismatch
+ * msgl-check.c (check_pair): Increment error count on missing
+ accelerator character or too many accelerators.
+
+2014-05-13 Daiki Ueno <ueno@gnu.org>
+
+ msgfmt: Accumulate errors when parsing the PO header
+ Problem reported by Peter Eisentraut at
+ <https://savannah.gnu.org/bugs/?40262>.
+ * msgl-check.c (check_header_entry): Return the number of errors.
+ (check_message): Check the return value of check_header_entry.
+
+2014-05-13 Felipe Sateler <fsateler@debian.org> (tiny change)
+
+ project-id: Add missing quotes around `pwd` for basename
+ Problem reported at <http://bugs.debian.org/654779>.
+ * project-id: Quote argument of the basename command.
+
+2014-05-12 Daiki Ueno <ueno@gnu.org>
+
+ msgfilter: Fix quote handling of doubled grave charaters
+ * filter-quote.c (convert_ascii_quote_to_unicode): Fix handling of
+ doubled grave characters.
+
+2014-05-12 Daiki Ueno <ueno@gnu.org>
+
+ * msgfmt.c (add_languages): New function split from get_languages.
+ (get_languages): Use add_languages instead of manually parsing
+ LINGUAS envvar with strtok_r.
+
+ * x-c.h (literalstring_c): Remove unnecessary DLL_VARIABLE.
+ * xgettext.c (arglist_parser_alloc): Use LET_NONE instead of 0.
+ (arglist_parser_remember): Likewise.
+ * xgettext.h (enum literalstring_escape_type): New enum value
+ LET_NONE.
+
+2014-05-10 Guido Flohr <guido@imperia.net>
+
+ msgattrib: Add --empty option to clear msgstr
+ * msgattrib.c (REMOVE_TRANSLATION): New enum value.
+ (long_options): Add --empty.
+ (main): Set REMOVE_TRANSLATION flag when --empty is given.
+ (usage): Show help of --empty.
+ (process_message_list): Handle REMOVE_TRANSLATION flag.
+
+2014-05-09 Daiki Ueno <ueno@gnu.org>
+
+ vala: Interpret string literals lazily
+ * x-vala.c (P7_EOF, P7_STRING_END, P7_QUOTES, P7_QUOTE, P7_NEWLINE)
+ (UNICODE, IS_UNICODE, UNICODE_VALUE): Remove.
+ (phase7_getc): Remove.
+ (phase7_ungetc): Remove.
+ (phase3_get): Use 'phase2_get' directly to extract string
+ literals; use 'arglist_parser_remember_literal' instead of
+ 'arglist_parser_remember'.
+ (literalstring_c): Declare external variable.
+ (extract_balanced): Remove the
+ 'xgettext_current_source_encoding' setting to prevent encoding
+ conversion around 'arglist_parser_done'.
+ (token_ty): New field 'escape'.
+ * x-vala.h (SCANNERS_VALA): Register 'literalstring_c' as a
+ literalstring_parser.
+
+2014-05-09 Daiki Ueno <ueno@gnu.org>
+
+ c: Interpret string literals lazily
+ * x-c.c (P7_EOF, P7_STRING_END, P7_QUOTES, P7_QUOTE, P7_NEWLINE)
+ (UNICODE, IS_UNICODE, UNICODE_VALUE): Remove.
+ (phase7_get): Remove.
+ (phase7_ungetc): Remove.
+ (phase5_get): Use 'phase3_get' directly to extract string
+ literals; use 'arglist_parser_remember_literal' instead of
+ 'arglist_parser_remember'.
+ (literalstring_parse): New function.
+ (literalstring_c): New variable.
+ (extract_parenthesized): Remove the
+ 'xgettext_current_source_encoding' setting to prevent encoding
+ conversion around 'arglist_parser_done'.
+ * x-c.h (SCANNERS_C): Register 'literalstring_c' as a
+ literalstring_parser.
+ (literalstring_c): New variable declaration.
+
+2014-05-09 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Provide a way to interpret string literals lazily
+ * xgettext.c (extract_ty): New field 'literalstring_parser'.
+ (current_literalstring_parser): New variable.
+ (extract_from_file): Set 'current_literalstring_parser'.
+ (savable_comment_convert_encoding): New function.
+ (arglist_parser_remember_literal): New function.
+ (arglist_parser_remember): Call 'arglist_parser_remember_literal'.
+ (arglist_parser_done): Call literalstring_parser on msgctxt,
+ msgid, and msgid_plural before calling 'remember_a_message';
+ convert encoding of msgid_comment.
+ * x-*.h: Register 'literalstring_parser' through SCANNER_*.
+ * xgettext.h (enum literalstring_escape_type): New enum.
+ (struct literalstring_parser): New struct.
+ (struct partial_call): New fields 'msgctxt_escape',
+ 'msgid_escape', and 'msgid_plural_escape'.
+ (arglist_parser_remember_literal): New function declaration.
+ (savable_comment_convert_encoding): New function declaration.
+
+2014-05-03 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Recognize prefixed comment tag
+ Reported by Jiang Xin in
+ <http://article.gmane.org/gmane.comp.version-control.git/246462>.
+ * xgettext.c (remember_a_message): Discard a string prefixed to
+ the comment tag from all remaining comment lines.
+
+2014-05-03 Daiki Ueno <ueno@gnu.org>
+
+ c: Support C99-style Unicode character escapes
+ * x-c.c: Include assert.h and po-charset.h.
+ (P7_QUOTES, P7_QUOTE, P7_NEWLINE): Redefine as a negative integer.
+ (P7_EOF, P7_STRING_END): New definitions.
+ (UNICODE): New macro.
+ (IS_UNICODE): New macro.
+ (UNICODE_VALUE): New macro.
+ (phase7_getc): Recognize "\unnnn" and "\Unnnnnnnn".
+ (phase5_get): Use mixed_string_buffer for parse string literal.
+
+2014-05-02 Daiki Ueno <ueno@gnu.org>
+
+ vala: Support C99-style Unicode character escapes
+ * x-vala.c: Include assert.h and po-charset.h.
+ (P7_QUOTES, P7_QUOTE, P7_NEWLINE): Redefine as a negative integer.
+ (P7_EOF, P7_STRING_END): New definitions.
+ (UNICODE): New macro.
+ (IS_UNICODE): New macro.
+ (UNICODE_VALUE): New macro.
+ (phase7_getc): Recognize "\unnnn" and "\Unnnnnnnn".
+ (phase3_get): Use mixed_string_buffer for parse string literal.
+
+2014-05-02 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Factor out commonly used mixed_string_buffer
+ * x-python.c (init_mixed_string_buffer)
+ (mixed_string_buffer_append_byte)
+ (mixed_string_buffer_append_unicode_grow)
+ (mixed_string_buffer_append_unicode)
+ (mixed_string_buffer_flush_utf16_surr)
+ (mixed_string_buffer_flush_curr_buffer)
+ (mixed_string_buffer_append, mixed_string_buffer_result)
+ (free_mixed_string_buffer): Move to...
+ * xgettext.c: ...here.
+ (mixed_string_buffer_alloc): Rename from init_mixed_string_buffer.
+ (mixed_string_buffer_append_to_curr_buffer): Rename from
+ mixed_string_buffer_append_byte.
+ (mixed_string_buffer_append_to_utf8_buffer): Rename from
+ mixed_string_buffer_append_unicode.
+ (mixed_string_buffer_grow_utf8_buffer): Rename from
+ mixed_string_buffer_append_unicode_grow.
+ (mixed_string_buffer_append_char): Split from
+ mixed_string_buffer_append.
+ (mixed_string_buffer_append_unicode): Split from
+ mixed_string_buffer_append.
+ (mixed_string_buffer_done): New function merging
+ mixed_string_buffer_result and free_mixed_string_buffer.
+ * xgettext.h (mixed_string_buffer): New struct moved from
+ x-python.c; add logical_file_name and line_number fields.
+ (mixed_string_buffer_alloc): New function declaration.
+ (mixed_string_buffer_append_char): New function declaration.
+ (mixed_string_buffer_append_unicode): New function declaration.
+ (mixed_string_buffer_done): New function declaration.
+ * x-javascript.c (init_mixed_string_buffer)
+ (mixed_string_buffer_append_byte)
+ (mixed_string_buffer_append_unicode_grow)
+ (mixed_string_buffer_append_unicode)
+ (mixed_string_buffer_flush_utf16_surr)
+ (mixed_string_buffer_flush_curr_buffer)
+ (mixed_string_buffer_append, mixed_string_buffer_result)
+ (free_mixed_string_buffer): Remove.
+
+2014-04-30 Daiki Ueno <ueno@gnu.org>
+
+ scheme: Recognize GIMP script-fu extension _"abc"
+ * x-scheme.c (read_object): Recognize _"abc".
+
+2014-04-30 Daiki Ueno <ueno@gnu.org>
+
+ format-python-brace: Limit acceptable format specifiers
+ Problem reported by Kovid Goyal at:
+ <https://savannah.gnu.org/bugs/?41668>.
+ * format-python-brace.c (parse_directive): Only recognize a single
+ nested format directive or the standard format specifiers as
+ format specifiers.
+
+2014-04-22 Roumen Petrov <bugtrack@roumenpetrov.info> (tiny change)
+
+ build: Use Automake 'subdir-objects' option
+ * Makefile.am (AUTOMAKE_OPTIONS): Add 'subdir-objects'.
+
+2014-04-21 Daiki Ueno <ueno@gnu.org>
+
+ msgfilter: Fix single quote handling in the quot filter
+ * filter-quote.c (convert_ascii_quote_to_unicode): Fix single
+ quote handling to accept multiple quotations in a string.
+
+2014-04-17 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Strip multiple ".in" suffixes from the file name.
+ * xgettext.c (main): Strip multiple ".in" suffixes.
+
+2014-04-16 Daiki Ueno <ueno@gnu.org>
+
+ * msgfmt.c (get_languages): Allow any whitespace character as a
+ list separator in LINGUAS.
+
+2014-04-15 Daiki Ueno <ueno@gnu.org>
+
+ msgfilter: Add 'quot' and 'boldquot' built-in filters
+ * filter-quote.c: New file.
+ * filters.h (ascii_quote_to_unicode, ascii_quote_to_unicode_bold):
+ New function declaration.
+ * msgfilter.c (main): Handle 'quot' and 'boldquot' filters.
+ * Makefile.am (msgfilter_SOURCES): Add filter-quote.c.
+
+2014-04-04 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (main): Warn user if invalid encoding name is
+ specified with the --from-code option.
+ Reported by jaroslav.fojtik@evolvsys.cz in
+ <https://lists.gnu.org/archive/html/bug-gettext/2014-03/msg00034.html>.
+
+2014-04-04 Daiki Ueno <ueno@gnu.org>
+
+ msgfmt: Add support for Desktop Entry files
+ * write-desktop.h: New file.
+ * write-desktop.c: New file.
+ * msgfmt.c (desktop_mode, desktop_locale_name)
+ (desktop_template_name, desktop_base_directory, desktop_keywords)
+ (desktop_default_keywords): New variables.
+ (long_options): Add --desktop and --template options for Desktop
+ Entry mode.
+ (get_languages): New function.
+ (msgfmt_desktop_bulk): New function which implements bulk
+ operation mode for Desktop Entry mode.
+ (main): Handle Desktop Entry mode options; call
+ 'msgfmt_desktop_bulk' if -d option is seen.
+ * Makefile.am (noinst_HEADERS): Add write-desktop.h.
+ (msgfmt_SOURCES): Add write-desktop.c
+
+2014-04-04 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Add support for Desktop Entry files
+ * read-desktop.h: New file.
+ * read-desktop.c: New file.
+ * x-desktop.h: New file.
+ * x-desktop.h: New file.
+ * xgettext.c (main): Regiser keywords for Desktop Entry mode.
+ (usage): Mention Desktop Entry source language.
+ (language_to_extractor): Add Desktop Entry rule.
+ (extension_to_language): Add Desktop Entry rule.
+ * Makefile.am (noinst_HEADERS): Add read-desktop.h and x-desktop.h.
+ (xgettext_SOURCES): Add x-desktop.c.
+
+2014-03-26 Aurélien Gâteau <mail@agateau.com> (tiny change)
+
+ msgfmt: Add --source option to generate .java file instead of .class
+ * msgfmt.c (java_output_source): New variable.
+ (long_options, main, usage): Add --source option.
+ * write-java.h (msgdomain_write_java): Add OUTPUT_SOURCE argument.
+ * write-java.c (msgdomain_write_java): Generate .java file instead
+ of .class if OUTPUT_SOURCE argument is given.
+ Reported at <https://savannah.gnu.org/bugs/?41766>.
+
+2014-03-25 Daiki Ueno <ueno@gnu.org>
+
+ Extend --add-location option to suppress line number output
+ The --add-location option of msgattrib, msgcat, msgcomm, msgconv,
+ msgen, msgfilter, msggrep, msgmerge, msguniq, and xgettext
+ commands now got new semantics. It takes an optional argument
+ 'never', 'full', or 'file', to control the format of "#: ..."
+ comments.
+ The default catalog reader changed to always remember file
+ positions so the line number part can be suppressed in output
+ phase rather than input phase.
+ Feature requested in:
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-08/msg00039.html>.
+ * read-catalog.h (line_comment): Abolish.
+ (DEFAULT_CATALOG_READER_TY): Remove handle_filepos_comments field.
+ * read-catalog.c (line_comment): Abolish.
+ (default_destructor, default_copy_comment_state)
+ (default_reset_comment_state, default_comment_filepos): Always
+ remember filepos.
+ (default_parse_brief, read_catalog_stream): Adjust to the change.
+ * write-po.h (enum filepos_comment_type): New enum.
+ (message_print_style_filepos): New function declaration.
+ (handle_filepos_comment_option): New function declaration.
+ * write-po.c (message_print_style_filepos): New function
+ (handle_filepos_comment_option): New function.
+ (message_print_comment_filepos): Uniquify mp->filepos elements
+ ignoring line number if filepos_comment_type is
+ filepos_comment_file.
+ * msgfmt.c: Adjust to the change.
+ * msgattrib.c (long_options, main): Allow --add-location option to
+ take an optional format specifier.
+ * msgcat.c (long_options, main): Likewise.
+ * msgcomm.c (long_options, main): Likewise.
+ * msguniq.c (long_options, main): Likewise.
+ * xgettext.c (long_options, main): Likewise.
+ * msgconv.c (long_options, main): Likewise; add a new option -n as
+ an alias of --add-location.
+ * msgen.c (long_options, main): Likewise.
+ * msgfilter.c (long_options, main): Likewise.
+ * msggrep.c (long_options, main): Likewise.
+ * msgmerge.c (long_options, main): Likewise.
+
+2014-03-12 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Fix infloop on loading Glade files with non-DL expat
+ * libexpat-compat.c: Keep the references to
+ XML_GetCurrent{Line,Column}Number symbols before including
+ libexpat-compat.h, since they are redefined.
+
+2014-03-10 Daiki Ueno <ueno@gnu.org>
+
+ php: Recognize single and double quotes around heredoc label
+ Problem reported by Byrial Jensen in:
+ <https://lists.gnu.org/archive/html/bug-gettext/2012-04/msg00001.html>.
+ Based on the patch by Andreas Stricker posted as:
+ <https://lists.gnu.org/archive/html/bug-gettext/2012-04/msg00002.html>.
+ * x-php.c (phase4_get): Strip quotes around heredoc label.
+
+2013-11-20 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: Add E4X support to JavaScript scanner
+ Reported by Piotr Drąg at: <https://savannah.gnu.org/bugs/?40125>.
+ * xgettext.h (enum lexical_context_ty): New enumeration items
+ lc_xml_open_tag, lc_xml_close_tag, lc_xml_content.
+ * x-javascript.c (phase5_scan_xml_markup): New function.
+ (phase5_get): Handle '<', '>', '/', '=', '{', and '}' specially
+ to support E4X.
+ (enum token_type_ty): New enumeration item token_type_equal.
+ (xml_element_depth): New variable.
+ (inside_embedded_in_xml): New variable.
+ (extract_javascript): Initialize those variables.
+
+2013-11-14 Daiki Ueno <ueno@gnu.org>
+
+ * x-javascript.c (phase3_getc): Make sure to call comment_line_end
+ after parsing C++ style comment line.
+ Reported by Illimar Tambek at: <http://savannah.gnu.org/bugs/?40572>.
+
+2013-11-14 Daiki Ueno <ueno@gnu.org>
+
+ * x-javascript.c (comment_line_end): Add missing chars_to_remove
+ argument; all callers changed.
+ Reported by Illimar Tambek at: <http://savannah.gnu.org/bugs/?40573>.
+
+2013-11-11 Daiki Ueno <ueno@gnu.org>
+
+ * x-c.c (phase5_get): Fix misuse of a logical operator.
+ * x-perl.c (extract_quotelike_pass3): Likewise.
+ * x-vala.c (phase3_get): Likewise.
+ Reported by David Binderman at: <https://savannah.gnu.org/bugs/?40528>.
+
+2013-10-23 Daiki Ueno <ueno@gnu.org>
+
+ * read-catalog-abstract.c (catalog_reader_parse): Clear
+ error_message_count before parsing, rather than after. The
+ variable may be > 0 before calling the PO parser, when xgettext
+ handles mutiple files.
+ Problem reported by Emil Wojak in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-10/msg00005.html>.
+
+2013-10-15 Peter Eisentraut <peter_e@gmx.net> (tiny change)
+
+ * msgl-check.c (check_header_entry): Adjust the default value of
+ PO-Revision-Date to xgettext output. Reported at
+ <https://savannah.gnu.org/bugs/?40261>.
+
+2013-08-29 Daiki Ueno <ueno@gnu.org>
+
+ * po-gram-gen.y (message): Free memory allocated for
+ msgid_pluralform.
+ (string_list): Free memory allocated for STRING.
+ (prev_string_list): Free memory allocated for PREV_STRING.
+ Reported by Alexander Potashev in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-08/msg00043.html>.
+
+2013-08-13 Miguel Angel Arruga Vivas <rosen644835@gmail.com>
+
+ * x-glade.c (start_element_glade1): Use extract_all variable.
+ (start_element_glade2): Ignore --extract-all option.
+ (start_element_gtkbuilder): Likewise.
+ (start_element_handler): Initialize p->extract_string to false.
+
+2013-08-12 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (remember_a_message): Handle multi-line extracted
+ comments.
+ Reported by Gabor Kelemen in
+ <http://lists.gnu.org/archive/html/bug-gettext/2013-08/msg00026.html>.
+
+2013-08-09 Daiki Ueno <ueno@gnu.org>
+
+ * x-gsettings.c (extract_gsettings): Add guard when expat is not
+ available at compile time.
+
+2013-08-08 Miguel Angel Arruga Vivas <rosen644835@gmail.com> (tiny change)
+
+ Fix copyright year in xgettext version string.
+ * xgettext.c (main): Update copyright year.
+
+2013-08-06 Daiki Ueno <ueno@gnu.org>
+
+ xgettext: add support for GSettings schema file
+ * x-gsettings.h: New file.
+ * x-gsettings.c: New file.
+ * xgettext.c: Include x-gsettings.h.
+ (flag_table_vala): New variable.
+ (usage): Mention GSettings source language.
+ (language_to_extractor): Add GSettings rule.
+ (extension_to_language): Add GSettings rule.
+ * Makefile.am (noinst_HEADERS): Add x-gsettings.h.
+ (xgettext_SOURCES): Add x-gsettings.c.
+ * FILES: Update.
+
+2013-08-05 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (main): Allow exntension with multiple
+ dots. e.g. .gschema.xml.
+
+2013-03-02 Miguel Angel Arruga Vivas <rosen644385@gmail.com>
+
+ Extract libexpat compatibility layer.
+ * libexpat-compat.h, libexpat-compat.c: Extracted from x-glade.c
+ * x-glade.c: Use libexpat-compat.h
+
+2013-07-11 Daiki Ueno <ueno@gnu.org>
+
+ Fix crash when parsing '..' with non-string argument.
+ Reported by Koen Dergent at <http://savannah.gnu.org/bugs/?39458>.
+ * x-lua.c (extract_balanced): Skip stray '..'.
+
+2013-06-27 Daiki Ueno <ueno@gnu.org>
+
+ * x-python.c (phase5_pushback): Increment size to 2.
+
+2013-06-25 Daiki Ueno <ueno@gnu.org>
+
+ Fix handling of \u escape sequences in Tcl.
+ * x-tcl.c (do_getc_escaped): Fix handling of \u.
+ Reported by Guido Berhoerster in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-06/msg00022.html>.
+
+2013-06-17 Daiki Ueno <ueno@gnu.org>
+
+ * x-python.c (init_flag_table_python): Enable python-brace-format
+ by default.
+
+2013-06-17 Daiki Ueno <ueno@gnu.org>
+
+ * x-glade.c (start_element_null): New function.
+ (end_element_null): New function.
+ (element_parser_null): New variable.
+ (start_element_handler): Use element_parser_null if the top-level
+ element does not indicate a valid Glade file.
+ (do_extract_glade): Initialize element_parser to
+ element_parser_null rather than NULL.
+
+2013-06-13 Miguel Angel Arruga Vivas <rosen644835@gmail.com>
+ Daiki Ueno <ueno@gnu.org>
+
+ Support for GtkBuilder file format in the Glade scanner.
+ * x-glade.h (EXTENSIONS_GLADE): Recognize .ui.
+ * x-glade.c (element_parser): New struct.
+ (start_element_glade1): New function split from start_element_handler.
+ (end_element_glade1): New function split from end_element_handler.
+ (start_element_glade2): New function split from start_element_handler.
+ (end_element_glade2): New function split from end_element_handler.
+ (start_element_gtkbuilder): New function.
+ (end_element_gtkbuilder): New function.
+ (element_parser_glade1): New variable.
+ (element_parser_glade2): New variable.
+ (element_parser_gtkbuilder): New variable.
+ (start_element_handler): Delegate the actual parsing logic to
+ specific element_parser.
+ (end_element_handler): Likewise.
+ Thanks to Miguel Ángel Arruga Vivas for the initial implementation
+ and the discussion in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-03/msg00074.html>
+ footnote 2.
+
+2013-06-10 Daiki Ueno <ueno@gnu.org>
+
+ * Makefile.am: Use $(MKDIR_P) instead of $(mkdir_p).
+ Suggested by Stefano Lattarini in
+ <https://lists.gnu.org/archive/html/bug-gettext/2013-04/msg00044.html>.
+
+2013-06-04 Daiki Ueno <ueno@gnu.org>
+
+ Support for Vala.
+ * x-vala.h: New file.
+ * x-vala.c: New file.
+ * xgettext.c: Include x-vala.h.
+ (flag_table_vala): New variable.
+ (main): Invoke init_flag_table_vala, x_vala_extract_all,
+ x_vala_keyword.
+ (usage): Mention Vala source language.
+ (language_to_extractor): Add Vala rule.
+ (extension_to_language): Add Vala rule.
+ * Makefile.am (noinst_HEADERS): Add x-vala.h.
+ (xgettext_SOURCES): Add x-vala.c.
+ * FILES: Update.
+
+2013-05-21 Daiki Ueno <ueno@gnu.org>
+
+ * x-javascript.c (phase7_getuc): Treat non-legitimate character
+ escape sequences more strictly.
+
+2013-05-20 Pavel Kharitonov <ineiev@gnu.org> (tiny change)
+
+ Add --previous option to msgattrib.
+ * msgattrib.c (long_options, main, process_message_list):
+ (usage): Add --previous option.
+
+2013-05-12 Daiki Ueno <ueno@gnu.org>
+
+ Fix end-of-string handling in JavaScript scanner.
+ * x-javascript.c (phase7_getuc): Remove unused BACKSLASH_COUNTER
+ argument; all callers changed.
+ Reported by Andreas Stricker in
+ <http://lists.gnu.org/archive/html/bug-gettext/2013-04/msg00051.html>.
+
+2013-05-03 Daiki Ueno <ueno@gnu.org>
+
+ * user-email.sh.in (lowercase_sed): Don't use non-portable
+ character escape in sed 's' command.
+ Reported by Ben Fox-Moore in
+ <http://lists.gnu.org/archive/html/bug-gettext/2013-05/msg00003.html>
+ and thanks to Ineiev for the suggestion.
+
+2013-04-26 Daiki Ueno <ueno@gnu.org>
+
+ Support for Python brace format.
+ * message.h (format_type): New enum value 'format_python_brace.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_python_brace entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_python_brace): New declaration.
+ * format-python-brace.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_python_brace.
+ * x-python.h (SCANNERS_PYTHON): Refar to formatstring_python_brace.
+ * xgettext.c (xgettext_record_flag): Handle format_python_brace.
+ * FILES: Update.
+
+2013-04-22 Daiki Ueno <ueno@gnu.org>
+
+ Make msgfmt --check-header more reliable.
+ * msgl-check.c (check_header_entry): Don't use c_strstr to parse
+ message header.
+
+2013-04-22 Daiki Ueno <ueno@gnu.org>
+
+ Support CR/LF line terminators in Python sources even on Unix.
+ * x-python.c (phase0_getc, phase0_ungetc): New functions.
+ (phase1_getc): Use them instead of calling getc/ungetc directly.
+
+2013-04-22 Daiki Ueno <ueno@gnu.org>
+
+ Support explicit string concatenation in Python.
+ * x-python.c (enum token_type_ty): New enumeration item
+ token_type_plus.
+ (free_token): New function.
+ (phase5_get): Recognize token_type_plus.
+ (x_python_lex): Handle string concatenation with '+'.
+ (extract_balanced): Handle token_type_plus.
+
+2013-04-18 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (usage): Wrap long lines in --help output.
+
+2013-04-17 Andreas Stricker <astricker@futurelab.ch>
+
+ Support for JavaScript.
+ * message.h (format_type): New enum value 'format_javascript'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_javascript entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_javascript): New declaration.
+ * format-javascript.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_javascript.
+ * x-javascript.h: New file.
+ * x-javascript.c: New file.
+ * xgettext.c: Include x-javascript.h.
+ (flag_table_javascript): New variable.
+ (main): Invoke init_flag_table_javascript, x_javascript_extract_all,
+ x_javascript_keyword.
+ (usage): Mention JavaScript source language.
+ (xgettext_record_flag): Handle format_javascript.
+ (language_to_extractor): Add JavaScript rule.
+ (extension_to_language): Add JavaScript rule.
+ * Makefile.am (noinst_HEADERS): Add x-javascript.h.
+ (FORMAT_SOURCE): Add format-javascript.c.
+ (xgettext_SOURCES): Add x-javascript.c.
+ * FILES: Update.
+
+2013-04-16 Ľubomír Remák <lubomirr@lubomirr.eu>
+
+ Support for escape sequences added in Lua 5.2.
+ * x-lua.c (phase3_get): Add \x and \z escape sequences.
+
+2013-04-11 Ľubomír Remák <lubomirr@lubomirr.eu>
+
+ Support for Lua.
+ * message.h (format_type): New enum value 'format_lua'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_lua entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_lua): New declaration.
+ * format-lua.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_lua.
+ * x-lua.h: New file.
+ * x-lua.c: New file.
+ * xgettext.c: Include x-lua.h.
+ (flag_table_lua): New variable.
+ (main): Invoke init_flag_table_lua, x_lua_extract_all,
+ x_lua_keyword.
+ (usage): Mention Lua source language.
+ (xgettext_record_flag): Handle format_lua.
+ (language_to_extractor): Add Lua rule.
+ (extension_to_language): Add Lua rule.
+ * Makefile.am (noinst_HEADERS): Add x-lua.h.
+ (FORMAT_SOURCE): Add format-lua.c.
+ (xgettext_SOURCES): Add x-lua.c.
+ * FILES: Update.
+
+2013-03-15 Miguel Ángel Arruga Vivas <rosen644835@gmail.com>
+ Daiki Ueno <ueno@gnu.org>
+
+ Extract msgctxt from Glade input files.
+ Reported at <https://savannah.gnu.org/bugs/?34506>
+ * x-glade.c (struct element_state): Add field 'extract_context'.
+ (start_element_handler): Check "context" attribute if the string
+ contains msgctxt.
+ (end_element_handler): Extract msgctxt if extract_context is set.
+
+2013-02-25 Daiki Ueno <ueno@gnu.org>
+
+ * Makefile.am (libgettextsrc_la_CPPFLAGS): Define to specify Woe32
+ DLL export flags.
+
+2013-01-09 Andreas Stricker <astricker@futurelab.ch> (tiny change)
+
+ * po-xerror.c: Include error.h for error_message_count.
+ * read-catalog-abstract.c: Likewise.
+
+2013-01-06 Daiki Ueno <ueno@gnu.org>
+
+ * msgl-fsearch.c (message_fuzzy_index_ty): Don't use float-derived
+ integer SHORT_MSG_MAX to define array.
+ (message_fuzzy_index_alloc): Allocate memory for 'short_messages'
+ field dynamically.
+ (message_fuzzy_index_free): Free it.
+
+2013-01-03 Daiki Ueno <ueno@gnu.org>
+
+ * xgettext.c (construct_header): Fix memory leak.
+
+2012-12-25 Daiki Ueno <ueno@gnu.org>
+
+ * gettext-0.18.2 released.
+
+2012-06-03 Jim Meyering <jim@meyering.net>
+
+ * msginit.c: Spelling fixes.
+ * write-catalog.c: Likewise.
+
+2012-05-03 Bruno Haible <bruno@clisp.org>
+
+ Document msgfmt option --endianness.
+ * msgfmt.c (usage): Document the option --endianness.
+ Reported by Paul Martin <pm@debian.org> via
+ Santiago Vila <sanvila@unex.es>.
+
+2012-01-26 Bruno Haible <bruno@clisp.org>
+
+ Modernize quoting.
+ * hostname.c (usage): Quote 'like this', not `like this', as per the
+ recent change to the GNU coding standards.
+ * msgattrib.c (usage): Likewise.
+ * msgcat.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * msgcomm.c (usage): Likewise.
+ * msgconv.c (usage): Likewise.
+ * msgen.c (usage): Likewise.
+ * msgexec.c (usage): Likewise.
+ * msgfilter.c (usage): Likewise.
+ * msgfmt.c (usage, msgfmt_set_domain, msgfmt_frob_new_message):
+ Likewise.
+ * msggrep.c (usage): Likewise.
+ * msginit.c (usage): Likewise.
+ * msgl-cat.c (catenate_msgdomain_list): Likewise.
+ * msgl-check.c (check_pair, check_header_entry): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * msguniq.c (usage): Likewise.
+ * recode-sr-latin.c (usage): Likewise.
+ * urlget.c (usage): Likewise.
+ * write-po.c (wrap): Likewise.
+ * xgettext.c (main, usage, remember_a_message, construct_header,
+ language_to_extractor): Likewise.
+ * po-gram-gen.y: Likewise.
+ * po-error.h: Likewise.
+ * po-lex.h: Likewise.
+ * read-catalog-abstract.h: Likewise.
+ * x-c.c: Likewise.
+
+2011-10-04 Bruno Haible <bruno@clisp.org>
+
+ xgettext for Scheme: Understand guile 2.0 comment syntax, part 2.
+ * x-scheme.c (read_object): Extract and don't ignore block comments.
+ Understand the #|...|# syntax of SRFI-30. (Code copied from x-lisp.c.)
+
+2011-10-04 Bruno Haible <bruno@clisp.org>
+
+ xgettext for Scheme: Understand guile 2.0 comment syntax, part 1.
+ * x-scheme.c (read_object): Understand !# as a block comment terminator
+ even when not surrounded by newlines.
+ Reported by David Pirotte <david@altosw.be>
+ via Santiago Vila <sanvila@unex.es>.
+
+2011-07-29 Bruno Haible <bruno@clisp.org>
+
+ Fix xgettext crash when extracting a message with plural that is
+ excluded.
+ * xgettext.h (remember_a_message): Document the return value.
+ * xgettext.c (arglist_parser_done): Handle the case where
+ remember_a_message returned NULL.
+ * x-smalltalk.c (extract_smalltalk): Likewise.
+ * x-ycp.c (extract_parenthesized): Likewise.
+ Reported by Jean-Luc Coulon <jean-luc.coulon@wanadoo.fr> via
+ Santiago Vila <sanvila@unex.es>.
+
+2011-06-13 Bruno Haible <bruno@clisp.org>
+
+ Avoid compilation error on Solaris 7 with cc.
+ * msgl-fsearch.c (SHORT_MSG_MAX): Define more directly with SunPRO C.
+
+2011-06-04 Bruno Haible <bruno@clisp.org>
+
+ Avoid link error when linking statically on AIX 7.
+ * Makefile.am (xgettext_LDADD): Add LTLIBICONV.
+
+2011-04-13 Bruno Haible <bruno@clisp.org>
+
+ * msgcat.c (usage): Fix description of --use-first.
+ * msgcomm.c (usage): Fix typo.
+ Reported by Matthijs Kooijman.
+
+2011-06-04 Bruno Haible <bruno@clisp.org>
+
+ Update after __attibute__ is no longer defined by gnulib.
+ * msgl-check.c (formatstring_error_logger): Use __attribute__ only with
+ compiler versions that support it.
+
+2011-06-02 Bruno Haible <bruno@clisp.org>
+
+ Use u8_mbtoucr instead of u8_mbtouc in some places.
+ * po-lex.c (mbfile_getc): Call u8_mbtoucr instead of u8_mbtouc.
+ * x-csharp.c (phase2_getc): Likewise.
+ * x-python.c (phase2_getc): Likewise.
+ Needed after libunistring changed on 2010-11-13.
+
+2011-06-02 Bruno Haible <bruno@clisp.org>
+
+ Update after gnulib changed.
+ * msgexec.c: Include spawn-pipe.h instead of pipe.h.
+ * msginit.c: Likewise.
+ * read-csharp.c: Likewise.
+ * read-java.c: Likewise.
+ * read-resources.c: Likewise.
+ * read-tcl.c: Likewise.
+ * write-resources.c: Likewise.
+
+2010-11-20 Bruno Haible <bruno@clisp.org>
+
+ Port to uClibc.
+ * write-po.c (wrap): Treat uClibc like a non-glibc platform.
+ * format-c-parse.h (HANDLE_I_FLAG): Likewise.
+ * po-charset.c (po_lex_charset_set): Likewise.
+ * recode-sr-latin.c (process): Likewise.
+ * x-python.c (set_current_file_source_encoding): Likewise.
+ * xgettext.c (main): Likewise.
+
+2010-11-13 Ihar Hrachyshka <ihar.hrachyshka@gmail.com> (tiny change)
+
+ * plural-table.c (plural_table): Added Belarusian, copied from Russian
+ and Ukrainian.
+
+2010-11-07 Bruno Haible <bruno@clisp.org>
+
+ format-c.c: Share code with libintlext.
+ * format-c-parse.h: New file, extracted from format-c.c.
+ * format-c.c: Include it.
+ (format_parse_entrails): Remove function.
+ * Makefile.am (FORMAT_SOURCE): Add format-c-parse.h.
+ * FILES: Update.
+
+2010-11-07 Bruno Haible <bruno@clisp.org>
+
+ format-c.c: Prepare for sharing code with libintlext.
+ * format-c.c: Move INVALID_* macros.
+ (IF_OOM): New macro.
+ (SYSDEP_SEGMENTS_PROCESSED): New macro.
+ (FAT_BASIC_MASK): New enumeration value.
+ (HANDLE_I_FLAG): New macro.
+ (format_parse_entrails): New function, extracted from format_parse.
+ Use the IF_OOM, SYSDEP_SEGMENTS_PROCESSED, HANDLE_I_FLAG macros. Handle
+ I64 as a size specifier on native Win32.
+ (format_parse): Rewritten to call format_parse_entrails.
+
+2010-11-07 Bruno Haible <bruno@clisp.org>
+
+ format-c.c: Prepare for sharing code with libintlext.
+ * format-c.c (INVALID_ANGLE_BRACKET, INVALID_IGNORED_ARGUMENT): New
+ macros.
+ (format_parse): Use them.
+
+2011-06-07 Bruno Haible <bruno@clisp.org>
+
+ Rely more on libtool.
+ * Makefile.am (libgettextsrc_la_LDFLAGS): Use -no-undefined always.
+ Don't use @LTNOUNDEF@.
+
+2010-06-06 Bruno Haible <bruno@clisp.org>
+
+ Bug fixes in libgettextpo.
+ * msgl-check.h (check_message_list): Add ignore_untranslated_messages,
+ ignore_fuzzy_messages arguments.
+ * msgl-check.c (check_plural, check_message_list): Likewise.
+ * msgfmt.c (main): Update.
+
+2010-06-04 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.18.1 released.
+
+2010-06-03 Bruno Haible <bruno@clisp.org>
+
+ xgettext: Recognize language of files ending in .perl.
+ * x-perl.h (EXTENSIONS_PERL): Recognize .perl.
+ Suggested by Ævar Arnfjörð Bjarmason <avarab@gmail.com>.
+
+2010-06-03 Bruno Haible <bruno@clisp.org>
+
+ urlget: Don't attempt to run the Java program if we don't install it.
+ * Makefile.am (USEJAVA): New variable.
+ (DEFS): Define USEJAVA.
+ * urlget.c (fetch): Don't try to use Java is USEJAVA is 0.
+
+2010-05-19 Bruno Haible <bruno@clisp.org>
+
+ Link with libunistring, if it exists.
+ * Makefile.am (LDADD, libgettextsrc_la_LDFLAGS): Add LTLIBUNISTRING.
+
+2010-05-18 Bruno Haible <bruno@clisp.org>
+
+ Correct program dependencies.
+ * Makefile.am (OTHERPROGDEPENDENCIES): New variable.
+ (recode_sr_latin_LDADD): Remove redundant variable.
+ (recode_sr_latin_DEPENDENCIES): Use OTHERPROGDEPENDENCIES.
+ (hostname_DEPENDENCIES, urlget_DEPENDENCIES): New variables.
+
+2010-05-09 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.18 released.
+
+2010-05-09 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2010-04-25 Bruno Haible <bruno@clisp.org>
+
+ Update support of object-pascal-format strings.
+ * format-pascal.c: Update description of format strings.
+ (enum format_arg_type): Remove FAT_INTEGER64.
+ (format_parse): Accept an empty digit sequence before ':'. Treat 'd',
+ 'u', 'x' the same way.
+ (main): Update.
+
+2010-04-05 Bruno Haible <bruno@clisp.org>
+
+ Interoperability with mono versions >= 2009-02-27.
+ * write-csharp.c (write_csharp_code): Emit a TableInitialized field.
+ Change ReadResources so that it may be called multiple times, even
+ concurrently.
+ Reported by Guido Flohr <guido@imperia.net>
+
+2010-03-31 Guido Flohr <guido@imperia.net>
+
+ Improve how xgettext handles Perl syntax ambiguities.
+ * x-perl.c(enum token_type_ty): New enumeration items
+ token_type_number, token_type_object.
+ (struct token_ty): New field 'last_type'.
+ (token2string): Handle token_type_number, token_type_object.
+ (free_token): Likewise.
+ (prefer_division_over_regexp): Remove variable.
+ (extract_variable): Recognize token of type token_type_object.
+ (prefer_regexp_over_division): New function.
+ (last_token_type): Renamed from last_token.
+ (x_perl_prelex): Assign the token's last_type. Recognize token of type
+ token_type_number. Don't special-case "grep" and "split". Invoke
+ prefer_regexp_over_division for disambiguation.
+ (token_stack_dump): Handle token_type_number, token_type_object.
+ (x_perl_lex): Assign the token's last_type. Update last_token_type
+ intelligently.
+ (collect_message): Invoke prefer_regexp_over_division for
+ disambiguation.
+ (extract_balanced): Don't set last_token_type here. Handle
+ token_type_number, token_type_object.
+ (extract_perl): Initialize last_token_type here.
+ Reported by Guillem Jover <guillem@debian.org> via Santiago Vila.
+
+ * x-perl.c (x_perl_prelex): Clarify interpolate_keywords arguments.
+
+ * x-perl.c (eaten_here): Renamed from here_eaten.
+ (phase1_getc, get_here_document, skip_pod, extract_perl): Update.
+
+2010-03-13 Bruno Haible <bruno@clisp.org>
+
+ New options --color, --style for many programs.
+ * msgattrib.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgcomm.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgconv.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgen.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgfilter.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msggrep.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msginit.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgmerge.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msgunfmt.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * msguniq.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ * xgettext.c: Include color.h.
+ (long_options): Add options --color, --style.
+ (main): Implement them.
+ (usage): Document them.
+ Reported by Kalle Olavi Niemitalo <kon@iki.fi>
+ via Santiago Vila <sanvila@unex.es>.
+
+2010-02-20 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Add Bulgarian.
+ Reported by Roumen Petrov <transl@roumenpetrov.info>.
+
+2010-02-20 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Sort in the same order as
+ gettext.texi.
+
+2010-02-17 Bruno Haible <bruno@clisp.org>
+
+ Fix error in conversion of large PO file (> 4000 msgs) to Java class.
+ * write-java.c: Include minmax.h.
+ (write_java1_init_statements, write_java2_init_statements): New
+ functions, extracted from write_java_code.
+ (write_java_code): If there are many messages, split the initialization
+ code into several static methods.
+ Reported by sasha <predator@savannah.gnu.org>.
+
+2009-12-21 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Add Mapudungun, Lower Sorbian,
+ Upper Sorbian, Mohawk, Yakut, Southern Sami, Lule Sami, Inari Sami,
+ Skolt Sami.
+ * msginit.c (catalogname_for_locale): Add Mapudungun, Lower Sorbian,
+ Upper Sorbian, Yakut, Inari Sami, Skolt Sami.
+
+2009-12-12 Bruno Haible <bruno@clisp.org>
+
+ * *.h, *.c, *.y: Untabify.
+
+2009-11-28 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (extract_variable): Fix tp->type when returning at EOF.
+ Reported by Guido Flohr <guido@imperia.bg>.
+
+2009-11-15 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c: Update comments regarding PEP 0263.
+
+2009-09-05 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Remove unused initialization.
+ * x-glade.c (comment_handler): Likewise.
+ * xgettext.c (flag_context_list_table_insert): Add comment.
+ Found by clang's static analyzer.
+
+2009-08-30 Bruno Haible <bruno@clisp.org>
+
+ Accommodate the Solaris iconv_open function, which cannot convert
+ directly between many pairs of encodings.
+ * msgl-iconv.h (convert_string_directly): Renamed from convert_string.
+ * msgl-iconv.c: Include xstriconveh.h.
+ (convert_string_directly): Renamed from convert_string.
+ (convert_string): New function.
+ (convert_string_list, convert_prev_msgid, convert_msgid,
+ iconvable_string_list, iconvable_prev_msgid, iconvable_msgid): Change
+ argument type to 'const iconveh_t *'.
+ (convert_msgstr, iconvable_string, iconvable_msgstr): Likewise. Invoke
+ xmem_cd_iconveh instead of xmem_cd_iconv.
+ (iconv_message_list_internal, is_message_list_iconvable): Invoke
+ iconveh_open, iconveh_close instead of iconv_open, iconv_close.
+ * xgettext.c (from_current_source_encoding): Update.
+
+2009-08-16 Bruno Haible <bruno@clisp.org>
+
+ Reduce dependency on project-id heuristic.
+ * msginit.c (project_id): Add header argument. Return first part of
+ Project-Id-Version field if present.
+ (fill_header): Update.
+
+2009-08-13 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c: Include msgl-charset.h.
+ (main): Invoke compare_po_locale_charsets.
+ (process_string): Add comment.
+ * msgfilter.c (process_message): Likewise.
+
+2009-08-10 Bruno Haible <bruno@clisp.org>
+
+ Avoid a gcc warning.
+ * msgfilter.c (sub_argv): Change type to 'const char **'.
+ (main): Update.
+
+2009-08-10 Bruno Haible <bruno@clisp.org>
+
+ Use type 'ucs4_t' more often. Avoids gcc warnings on Cygwin.
+ * po-lex.c (struct mbchar): Change type of 'uc'.
+ (mb_width): Update.
+ * read-properties.c (read_escaped_string): Change type of local
+ variable 'uc'.
+ * read-stringtable.c (phase2_getc): Likewise.
+ * write-properties.c (conv_to_java, write_escaped_string): Change type
+ of local variables 'uc', 'uc1', 'uc2'.
+ * write-java.c (string_hashcode, write_java_string): Likewise.
+ * write-csharp.c (construct_class_name, write_csharp_string): Change
+ type of local variable 'uc'.
+ * write-tcl.c (write_tcl_string): Likewise.
+ * write-qt.c (conv_to_iso_8859_1, conv_to_utf16): Likewise.
+ * x-python.c (phase2_getc, mixed_string_buffer_append): Likewise.
+ (mixed_string_buffer_append_unicode): Change argument type to 'ucs4_t'.
+ * x-java.c (string_buffer_append): Change type of local variable 'uc'.
+ (string_buffer_append_unicode): Change argument type to 'ucs4_t'.
+ * x-csharp.c (phase2_getc): Change type of local variable 'uc'.
+
+2009-08-04 Bruno Haible <bruno@clisp.org>
+
+ Port msgfilter to Woe32 platforms.
+ * msgfilter.c: Don't include errno.h, fcntl.h, io.h, sys/select.h,
+ pipe.h, wait-process.h. Include pipe-filter.h instead.
+ (HAVE_SELECT): Remove undefine.
+ (nonintr_close, nonintr_read, nonintr_write, nonintr_select): Remove
+ functions.
+ (O_NONBLOCK): Remove fallback.
+ (IS_EAGAIN): Remove macro.
+ (struct locals): New type.
+ (prepare_write, done_write, prepare_read, done_read): New functions.
+ (generic_filter): Implement using pipe_filter_ii_execute.
+
+2009-07-27 Часлав Илић (Chusslove Illich) <caslav.ilic@gmx.net>
+
+ Extend recode-sr-latin to also transform letters with accents.
+ * filter-sr-latin.c (table): Add entries for letters with accents.
+ (IS_UPPERCASE_CYRILLIC): Recognize also U+04E2, U+04EE.
+ (serbian_to_latin): Update.
+
+2009-06-06 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (x_perl_prelex): Recognize the perl 5.10 operator '//'.
+ Reported by Kevin Ryde <user42@zip.com.au>.
+
+2009-05-29 Bruno Haible <bruno@clisp.org>
+
+ Improve msgfmt error message about format directive mismatches
+ in msgstr[i].
+ * format.h (struct formatstring_parser): Add pretty_msgid argument to
+ 'check' member.
+ * format-awk.c (format_check): Add pretty_msgid argument.
+ * format-boost.c (format_check): Likewise.
+ * format-c.c (format_check): Likewise.
+ * format-csharp.c (format_check): Likewise.
+ * format-elisp.c (format_check): Likewise.
+ * format-gcc-internal.c (format_check): Likewise.
+ * format-gfc-internal.c (format_check): Likewise.
+ * format-java.c (format_check): Likewise.
+ * format-kde.c (format_check): Likewise.
+ * format-librep.c (format_check): Likewise.
+ * format-lisp.c (format_check): Likewise.
+ * format-pascal.c (format_check): Likewise.
+ * format-perl-brace.c (format_check): Likewise.
+ * format-perl.c (format_check): Likewise.
+ * format-php.c (format_check): Likewise.
+ * format-python.c (format_check): Likewise.
+ * format-qt.c (format_check): Likewise.
+ * format-qt-plural.c (format_check): Likewise.
+ * format-scheme.c (format_check): Likewise.
+ * format-sh.c (format_check): Likewise.
+ * format-tcl.c (format_check): Likewise.
+ * format-ycp.c (format_check): Likewise.
+ * format.c (check_msgid_msgstr_format_i): Pass either "msgid" or
+ "msgid_plural" as pretty_msgid argument.
+
+2009-05-23 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (libgettextsrc_la_LINK, msgattrib_LINK, msgcat_LINK,
+ msgcomm_LINK, msgconv_LINK, msgen_LINK, msgfilter_LINK, msggrep_LINK,
+ msgmerge_LINK, msguniq_LINK, xgettext_LINK): Add $(AM_V_lt) flag.
+
+2009-05-16 Bruno Haible <bruno@clisp.org>
+
+ Extract comments meant for translators from Glade input files.
+ * xgettext.h (remember_a_message): Add 'extracted_comment' argument.
+ * xgettext.c (remember_a_message): Likewise.
+ (arglist_parser_done): Update.
+ * x-glade.c (struct element_state): Add field 'extracted_comment'.
+ (start_element_handler): Fill it.
+ (end_element_handler): Pass it to 'remember_a_message', free it.
+ * x-awk.c (extract_parenthesized): Update.
+ * x-c.c (extract_parenthesized): Update.
+ * x-csharp.c (extract_parenthesized): Update.
+ * x-elisp.c (read_object): Update.
+ * x-java.c (extract_parenthesized): Update.
+ * x-librep.c (read_object): Update.
+ * x-lisp.c (read_object): Update.
+ * x-perl.c (extract_variable, interpolate_keywords, extract_balanced):
+ Update.
+ * x-php.c (extract_balanced): Update.
+ * x-python.c (extract_balanced): Update.
+ * x-rst.c (extract_rst): Update.
+ * x-scheme.c (read_object): Update.
+ * x-sh.c (read_word, read_command): Update.
+ * x-smalltalk.c (extract_smalltalk): Update.
+ * x-tcl.c (read_command): Update.
+ * x-ycp.c (extract_parenthesized): Update.
+ Reported by <sandro.bonazzola@gmail.com>
+ at <https://savannah.gnu.org/bugs/?26570>.
+
+2009-03-29 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_gfc_internal'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_gfc_internal entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_gfc_internal): New declaration.
+ * format-gfc-internal.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_gfc_internal.
+ * x-c.h (SCANNERS_C): In language GCC-source, also keep track of
+ gfc-internal-format format strings.
+ * x-c.c (init_flag_table_gcc_internal): Also register flags for
+ gfc-internal-format.
+ * xgettext.c (xgettext_record_flag): Store gfc-internal-format flags in
+ flag_table_gcc_internal.
+ * Makefile.am (FORMAT_SOURCE): Add format-gfc-internal.c.
+ * FILES: Update.
+ Reported by Göran Uddeborg <goeran@uddeborg.se>.
+
+2009-01-27 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_qt_plural'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_qt_plural entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_qt_plural): New declaration.
+ * format-qt-plural.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_qt_plural.
+ * xgettext.h (struct flag_context_ty): Add fields is_format3,
+ pass_format3.
+ * xgettext.c (struct extractor_ty): Add field formatstring_parser3.
+ (inherited_context, flag_context_list_table_insert): Handle the new
+ flag_context_ty fields.
+ (xgettext_record_flag): Handle format_qt_plural.
+ (current_formatstring_parser3): New variable.
+ (extract_from_file): Initialize it.
+ (set_format_flags_from_context): Handle the new flag_context_ty fields.
+ (remember_a_message, remember_a_message_plural): Handle
+ current_formatstring_parser3. Avoid adding a c-format flag to a message
+ already flagged as qt-plural-format.
+ (arglist_parser_remember): Allow argnum1 and argnum2 in the call shape
+ to be the same.
+ (arglist_parser_done): Add special recognition of qt-plural-format
+ strings.
+ (language_to_extractor): Set the formatstring_parser3 in the result.
+ * Makefile.am (FORMAT_SOURCE): Add format-qt-plural.c.
+ * FILES: Update.
+
+2009-01-27 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Put Turkish under nplurals=2.
+ Reported by Sertaç Ö. Yıldız <sertacyildiz@gmail.com>.
+
+2009-01-26 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+ * urlget.c (main): Likewise.
+
+2009-01-25 Bruno Haible <bruno@clisp.org>
+
+ Fix bug introduced on 2005-10-01.
+ * msgl-charset.c (compare_po_locale_charsets): Fix recognition of
+ header entry.
+
+2009-01-25 Bruno Haible <bruno@clisp.org>
+
+ Fix bug introduced on 2008-10-04.
+ * read-catalog-abstract.c (po_parse_comment_special): Initialize
+ *rangep.
+ Reported by Ralf Wildenhues <Ralf.Wildenhues@gmx.de>.
+
+2009-01-18 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c: Include str-list.h.
+ (main): When --statistics and --verbose are both specified, output the
+ input file name in front of the statistics line.
+ Suggested by Vincent Lefevre <vincent@vinc17.org>.
+
+2009-01-18 Bruno Haible <bruno@clisp.org>
+
+ Allow multiple levels of verbosity in 'msgfmt'.
+ * msgfmt.h (verbose): Change from 'bool' to 'int'.
+ * msgfmt.c (verbose): Likewise.
+ (main): Increment 'verbose' for each --verbose option.
+ * write-java.c (msgdomain_write_java): Update.
+ * write-csharp.c (msgdomain_write_csharp): Update.
+ * write-resources.c (msgdomain_write_csharp_resources): Update.
+
+2009-01-18 Bruno Haible <bruno@clisp.org>
+
+ * str-list.h (string_list_join): Change the separator argument,
+ allowing an entire separator string.
+ * str-list.c (string_list_join): Likewise.
+
+2009-01-18 Bruno Haible <bruno@clisp.org>
+
+ Fix a '(null)' in an error message. Bug present since gettext-0.16.
+ * read-catalog.h (DEFAULT_CATALOG_READER_TY): Add a file_name field.
+ * read-catalog.c (read_catalog_stream): Initialize it.
+ * x-po.c (extract): Likewise.
+ * msgfmt.c (read_catalog_file_msgfmt): Likewise.
+ (msgfmt_parse_debrief): Use the file_name field from the catalog reader
+ instead of gram_pos.file_name.
+
+2009-01-17 Bruno Haible <bruno@clisp.org>
+
+ Update after gnulib changed.
+ * Makefile.am (RELOCATABLE_STRIP): New variable.
+
+2008-12-07 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (message_print_comment_filepos): Use a 'const' pointer
+ where possible.
+ * write-stringtable.c (write_message): Likewise.
+
+2008-12-07 Bruno Haible <bruno@clisp.org>
+
+ Avoid gcc -Wredundant-decls warnings.
+ * x-awk.c: Don't include the specification header file twice.
+ * x-c.c: Likewise.
+ * x-csharp.c: Likewise.
+ * x-elisp.c: Likewise.
+ * x-glade.c: Likewise.
+ * x-java.c: Likewise.
+ * x-librep.c: Likewise.
+ * x-lisp.c: Likewise.
+ * x-perl.c: Likewise.
+ * x-php.c: Likewise.
+ * x-po.c: Likewise.
+ * x-python.c: Likewise.
+ * x-rst.c: Likewise.
+ * x-scheme.c: Likewise.
+ * x-sh.c: Likewise.
+ * x-smalltalk.c: Likewise.
+ * x-tcl.c: Likewise.
+ * x-ycp.c: Likewise.
+
+2008-11-14 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: Include xvasprintf.h, xsetenv.h.
+ (process_message): Set the environment variables MSGFILTER_MSGCTXT,
+ MSGFILTER_MSGID, MSGFILTER_LOCATION.
+
+2008-10-28 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (match_domain): Remove space between '#' and 'pragma' for
+ OpenMP.
+ Reported by Lamarque Eric <eric.lamarque@free.fr>.
+
+2008-10-04 Bruno Haible <bruno@clisp.org>
+
+ * plural-distrib.h (struct plural_distribution): New field 'histogram'.
+ * format.h (check_msgid_msgstr_format_i, check_msgid_msgstr_format):
+ Add range argument.
+ * format.c (check_msgid_msgstr_format_i): Add range argument. Set
+ strict_checking to false if, due to the range, the plural forms applies
+ to only one value of n.
+ (check_msgid_msgstr_format): Add range argument.
+ * msgl-check.c (plural_expression_histogram): New function.
+ (check_plural_eval, check_plural): Update.
+ (check_pair): Pass the range to check_msgid_msgstr_format.
+ (check_message_list): Update.
+ * msgmerge.c (message_merge): Pass the range to
+ check_msgid_msgstr_format_i.
+ Reported by Anatoly Techtonik <techtonik@gmail.com>
+ via <https://savannah.gnu.org/bugs/?24433>.
+
+2008-10-04 Bruno Haible <bruno@clisp.org>
+
+ * message.h (struct argument_range): New type.
+ (has_range_p): New macro.
+ (struct message_ty): Add field 'range'.
+ * message.c (message_alloc): Initialize the 'range' field.
+ (message_copy): Copy the 'range' field.
+ * read-catalog-abstract.h (po_parse_comment_special): Add 'rangep'
+ argument.
+ * read-catalog-abstract.c: Include <limits.h>.
+ (po_parse_comment_special): Add 'rangep' argument. Parse the range
+ description syntax.
+ * read-catalog.h (DEFAULT_CATALOG_READER_TY): Add 'range' field.
+ * read-catalog.c (default_constructor): Initialize the 'range' field.
+ (default_copy_comment_state): Copy the 'range' field into the new
+ message.
+ (default_reset_comment_state): Clear the 'range' field.
+ (default_comment_special): Update.
+ * write-po.h (make_range_description_string): New declaration.
+ * write-po.c (make_range_description_string): New function.
+ (message_print_comment_flags): Also print the range.
+ * write-stringtable.c (write_message): Likewise.
+ * msgl-cat.c: Include <limits.h>.
+ (catenate_msgdomain_list): Fill in the range of the resulting messages.
+ * msgl-equal.c (message_equal): Compare also the ranges.
+ * msgmerge.c (message_merge): Fill in the range of the resulting
+ message. Set it fuzzy if a range was introduced or extended.
+ * xgettext.c (remember_a_message): Set the range of the new message.
+
+2008-10-03 Bruno Haible <bruno@clisp.org>
+
+ * plural-distrib.h: New file.
+ * format.h: Include plural-distrib.h.
+ (check_msgid_msgstr_format_i, check_msgid_msgstr_format): Replace
+ plural_distribution, plural_distribution_length arguments with a single
+ distribution argument.
+ * format.c (check_msgid_msgstr_format_i, check_msgid_msgstr_format):
+ Likewise.
+ * msgl-check.h: Include plural-distrib.h.
+ (check_plural_eval, check_message): Replace plural_distribution,
+ plural_distribution_length arguments with a single distribution
+ argument.
+ * msgl-check.c (check_plural_eval): Likewise. Free array in case of
+ error.
+ (check_plural): Replace plural_distribution, plural_distribution_length
+ arguments with a single distribution argument. Don't store a
+ distribution result when there are errors.
+ (check_pair, check_message): Replace plural_distribution,
+ plural_distribution_length arguments with a single distribution
+ argument.
+ (check_message_list): Update.
+ * msgmerge.c (message_merge: Replace plural_distribution,
+ plural_distribution_length arguments with a single distribution
+ argument.
+ (match_domain): Update.
+ * Makefile.am (noinst_HEADERS): Add plural-distrib.h.
+ * FILES: Mention plural-distrib.h.
+
+2008-09-28 Bruno Haible <bruno@clisp.org>
+
+ * write-catalog.c (msgdomain_list_print): Open the output stream in
+ binary mode, for consistency with the term_styled_ostream or fd_stream
+ based case.
+
+2008-09-28 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msg*_DEPENDENCIES, xgettext_DEPENDENCIES,
+ recode_sr_latin_DEPENDENCIES): Add $(WOE32_LDADD).
+
+2008-09-28 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Don't die from SIGPIPE if the subprocess
+ does not want our input.
+ Reported by Rainer Tammer <tammer@tammer.net>.
+
+2008-09-28 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msgcmp_LDADD): Add MSGMERGE_LIBM.
+ Reported by Rainer Tammer <tammer@tammer.net>.
+
+2008-09-27 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Add Asturian, Crimean Tatar, Friulian,
+ Papiamento.
+ * msginit.c (catalogname_for_locale): Add Asturian, Friulian,
+ Papiamento.
+
+2008-09-26 Bruno Haible <bruno@clisp.org>
+
+ * write-catalog.c (cmp_by_msgid, cmp_by_filepos): Compare the msgctxt
+ fields if the msgid fields are the same.
+ Reported by Rainer Tammer <tammer@tammer.net>.
+
+2008-09-15 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msg*_DEPENDENCIES, xgettext_DEPENDENCIES,
+ recode_sr_latin_DEPENDENCIES): New variables.
+
+2008-09-15 Bruno Haible <bruno@clisp.org>
+
+ * msgl-fsearch.h: Include stdbool.h.
+ (message_fuzzy_index_search): Add 'heuristic' argument.
+ * msgl-fsearch.c (message_fuzzy_index_search): Likewise. If !heuristic,
+ consider all messages.
+ * msgmerge.c (struct definitions_ty): Add fields 'curr_findex',
+ 'curr_findex_init_lock'.
+ (definitions_init): Initialize them.
+ (definitions_set_current_list): Clear the previous curr_findex.
+ (definitions_init_curr_findex): New function.
+ (definitions_search_fuzzy): Do fuzzy matching by calling
+ message_fuzzy_index_search on a hashed index, rather than through
+ message_list_search_fuzzy.
+ (definitions_destroy): Update.
+ (merge): Determine the definitions' canonical encoding.
+ * msgcmp.c: Include xmalloca.h, po-charset.h, msgl-fsearch.h.
+ (match_domain): Add defmlp_findex, def_canon_charset arguments. Do
+ fuzzy matching by calling message_fuzzy_index_search on a hashed
+ index, rather than through message_list_search_fuzzy.
+ (compare): Determine the definitions' canonical encoding. Prepare room
+ for a lazily allocated hashed index.
+ * Makefile.am (msgcmp_SOURCES): Add msgl-fsearch.c.
+
+2008-09-15 Bruno Haible <bruno@clisp.org>
+
+ * msgcmp.c (use_fuzzy_matching): New variable.
+ (long_options): Add option -N/--no-fuzzy-matching.
+ (main, match_domain): Implement it.
+ (usage): Document it.
+
+2008-09-14 Bruno Haible <bruno@clisp.org>
+
+ * msgl-fsearch.h (message_fuzzy_index_search): Add 'lower_bound'
+ argument.
+ * msgl-fsearch.c (message_fuzzy_index_search): Likewise.
+ * msgmerge.c (definitions_search_fuzzy): Use the result of the fuzzy
+ search in the current list as a lower bound for the fuzzy search in the
+ compendiums.
+
+2008-09-14 Bruno Haible <bruno@clisp.org>
+
+ * message.h (message_list_search_fuzzy, FUZZY_THRESHOLD): Clarify
+ documentation.
+
+ * msgmerge.c (struct definitions_ty): Rename field 'findex' to
+ 'comp_findex', and 'findex_init_lock' to 'comp_findex_init_lock'.
+ (definitions_init): Update.
+ (definitions_init_comp_findex): Renamed from definitions_init_findex.
+ Update.
+ (definitions_search_fuzzy, definitions_destroy): Update.
+
+ * msgmerge.c (definitions_current_list, definitions_set_current_list):
+ Move functions.
+
+2008-09-14 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+ Bruno Haible <bruno@clisp.org>
+
+ * message.h (fuzzy_search_goal_function): Add 'lower_bound' argument.
+ * message.c (fuzzy_search_goal_function): Likewise. Use fstrcmp_bounded
+ instead of fstrcmp.
+ (message_list_search_fuzzy_inner): Pass fuzzy_search_goal_function the
+ best weight known so far, to shortcut computations.
+ * msgl-fsearch.c (message_fuzzy_index_search): Likewise.
+ * msgmerge.c (definitions_search_fuzzy): Update
+ fuzzy_search_goal_function calls.
+
+2008-09-14 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+ Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c: Include concat-filename.h.
+ (main, xgettext_open): Use xconcatenated_filename instead of
+ concatenated_filename.
+
+2008-09-01 Bruno Haible <bruno@clisp.org>
+
+ * color.c: Include concat-filename.h.
+ (style_file_lookup, style_file_prepare): Use xconcatenated_filename
+ instead of concatenated_filename.
+ * msginit.c: Include concat-filename.h instead of filename.h.
+ (project_id, project_id_version): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * open-catalog.c: Include concat-filename.h.
+ (try_open_catalog_file): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * read-csharp.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_read_csharp): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * read-resources.c: Include concat-filename.h instead of filename.h.
+ (read_resources_file): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * read-tcl.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_read_tcl): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * write-resources.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_write_csharp_resources): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * write-csharp.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_write_csharp): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * write-java.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_write_java): Use xconcatenated_filename instead of
+ concatenated_filename.
+ * write-tcl.c: Include concat-filename.h instead of filename.h.
+ (msgdomain_write_tcl: Use xconcatenated_filename instead of
+ concatenated_filename.
+
+2008-08-31 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c: Include <signal.h>.
+ (main): Block SIGPIPE for the duration of the processing.
+
+2008-08-23 Bruno Haible <bruno@clisp.org>
+
+ Fix behaviour of "msgmerge --update" when sorting is requested and
+ obsolete messages are present.
+ * write-catalog.h (struct catalog_output_format): New field
+ 'sorts_obsoletes_to_end'.
+ * write-po.c (output_format_po): Initialize it to true.
+ * write-properties.c (output_format_properties): Initialize it to false.
+ * write-stringtable.c (output_format_stringtable): Likewise.
+ * msgmerge.c (msgdomain_list_stablesort_by_obsolete): New function.
+ (main): Before testing whether the result is the same as the old
+ contents, sort the result using msgdomain_list_stablesort_by_obsolete.
+ Reported by Vincent Danjean <vdanjean.abo@free.fr>
+ via <http://savannah.gnu.org/bugs/?24123>.
+
+2008-08-16 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c (enum token_type_ty): New values token_type_lbracket,
+ token_type_rbracket.
+ (phase5_get): Recognize also token_type_lbracket, token_type_rbracket.
+ (extract_balanced): Renamed from extract_parenthesized. Add 'delim'
+ argument. Handle token_type_lbracket and token_type_rbracket.
+ (extract_python): Update.
+ Reported by Claude Paroz <claude@2xlibre.net>
+ via <http://savannah.gnu.org/bugs/?23824>.
+
+2008-08-16 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (extract_balanced): Fix small bug in 2007-03-17 commit.
+
+2008-08-15 Bruno Haible <bruno@clisp.org>
+
+ * format-python.c (format_parse): For %.0s and %.0r, set the type to
+ FORMAT_ANY.
+ (format_check): When strict equality is not desired, compare FORMAT_ANY
+ as matching any type.
+ Reported by Alexander Dupuy <alex.dupuy@mac.com> in
+ <http://savannah.gnu.org/bugs/?24025>.
+
+2008-08-14 Bruno Haible <bruno@clisp.org>
+
+ * format-python.c (format_parse): For '%', set the type to FAT_NONE.
+
+2008-08-14 Bruno Haible <bruno@clisp.org>
+
+ * format-python.c (format_check): Require the same number of unnamed
+ arguments also when !equality.
+ Reported by Alexander Dupuy <alex.dupuy@mac.com> in
+ <http://savannah.gnu.org/bugs/?24025>.
+
+2008-08-14 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c: Include "glthread/lock.h" instead of "lock.h".
+
+2008-08-03 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c (mixed_string_buffer_append): Replace a lone high
+ surrogate with U+FFFD.
+ Reported by Yann <asterix@lagaule.org>
+ via Santiago Vila <sanvila@unex.es>.
+
+2008-07-19 Bruno Haible <bruno@clisp.org>
+
+ * gnu/gettext/GetURL.java: Don't output anything to standard error.
+ Instead, set exit code to indicate failure reason.
+ * urlget.c (verbose): New variable.
+ (long_options): Add --quiet, --silent option.
+ (main): Implement --quiet, --silent option.
+ (usage): Document --quiet, --silent option.
+ (java_exitcode): New variable.
+ (execute_it): Set it. Return false also when the exit code is 2.
+ (fetch): Implement verbosity to standard error here.
+ Reported by 宋浩 <baritono.tux@gmail.com>.
+
+2008-06-10 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Update for changed signature of
+ wait_subprocess().
+ * msgfilter.c (generic_filter): Likewise.
+ * msginit.c (project_id, project_id_version, get_user_email,
+ language_team_address): Likewise.
+ * read-csharp.c (execute_and_read_po_output): Likewise.
+ * read-java.c (execute_and_read_po_output): Likewise.
+ * read-resources.c (execute_and_read_po_output): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * write-resources.c (execute_writing_input): Likewise.
+ * urlget.c (execute_it, fetch): Update for changed signature of
+ execute().
+
+2008-05-26 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (get_field, put_field): Recognize a field also if there is
+ no space after the ':'.
+ Reported by Nacho <nacho.resa@gmail.com>.
+
+2008-05-16 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c (iconvable_prev_msgid): Fix typo.
+ Reported by Karl Eichwalder <ke@novell.com>
+ via Philipp Thomas <pth@novell.com>
+ at <https://bugzilla.novell.com/show_bug.cgi?id=391372>.
+
+2008-05-10 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c: Include unilbrk.h instead of linebreak.h.
+ (wrap): Update.
+ * po-lex.c: Don't include linebreak.h.
+
+2008-04-25 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Add Beja. Remove Adangme, Banda,
+ Batak. Update Scottish Gaelic, Central Khmer, Romansh.
+ * msginit.c (catalogname_for_locale: Add Beja. Remove Banda, Batak.
+ Update Scottish Gaelic, Central Khmer, Romansh.
+
+2008-04-20 Bruno Haible <bruno@clisp.org>
+
+ Improve error message.
+ * xgettext.h (lexical_context_ty): New type.
+ (non_ascii_error_message): New declaration.
+ (from_current_source_encoding): Add lcontext argument.
+ * xgettext.c (non_ascii_error_message): New function.
+ (from_current_source_encoding): Add lcontext argument. Use
+ non_ascii_error_message.
+ (CONVERT_STRING): Add lcontext argument.
+ (remember_a_message, remember_a_message_plural): Update.
+ * x-csharp.c (lexical_context): New variable.
+ (phase2_getc): Use non_ascii_error_message.
+ (comment_start, comment_line_end, phase6_get): Set lexical_context.
+ (extract_csharp): Initialize lexical_context.
+ * x-java.c (struct string_buffer): Add lcontext field.
+ (init_string_buffer): Add lcontext argument.
+ (string_buffer_flush_curr_buffer): Update from_current_source_encoding
+ call.
+ (comment_start): Set lcontext.
+ (phase5_get): Pass lcontext argument.
+ * x-perl.c (get_here_document, phase2_getc,
+ extract_quotelike_pass1_utf8): Pass lcontext argument.
+ * x-python.c (lexical_context): New variable.
+ (phase2_getc): Use non_ascii_error_message.
+ (comment_start, comment_line_end, phase6_get): Set lexical_context.
+ (struct mixed_string_buffer): Add lcontext field.
+ (init_mixed_string_buffer): Add lcontext argument.
+ (mixed_string_buffer_flush_curr_buffer): Update
+ from_current_source_encoding call.
+ (phase5_get): Set lexical_context. Pass lcontext argument.
+ (extract_python): Initialize lexical_context.
+ Reported by Mark Weyer.
+
+2008-04-20 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (usage): Clarify single-letter options that take an
+ optional argument: -c, -k, -m, -M.
+
+2008-04-16 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Add entries for Maori, Uighur.
+
+2008-02-20 Jakub Jelinek <jakub@redhat.com>
+ Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c: Update for GCC 4.3.
+ (FAT_TREE_STATEMENT): New enum item.
+ (format_parse): Recognize %K.
+ (format_print): Update.
+
+2008-02-04 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (match_domain): Remove the prev_msgid fields also from
+ the untranslated messages.
+ Reported by Yukiko Bando <ybando@k6.dion.ne.jp> via
+ Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
+2008-02-04 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (message_merge): Store the prev_msgid field also if the
+ resulting message is not fuzzy: it might be marked as fuzzy later.
+ (match_domain): Remove the prev_msgid fields from the non-fuzzy
+ messages.
+
+2008-01-29 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Change the severity of the "should not contain
+ escape sequence" message from PO_SEVERITY_ERROR to PO_SEVERITY_WARNING.
+ Reported by Kerb <ykerb2@free.fr>.
+
+2008-01-13 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (noinst_headers): Add msgl-header.h.
+
+2007-12-24 Bruno Haible <bruno@clisp.org>
+
+ Introduce 'Language' header field.
+ * xgettext.c (construct_header): Add an empty 'Language' field.
+ * msginit.c (language_value): New function.
+ (fields): Add 'Language'.
+ * msgmerge.c: Include lang-table.h.
+ (catalogname): New variable.
+ (long_options): Add --lang option.
+ (main): Handle --lang option.
+ (usage): Document --lang option.
+ (message_merge): Set the 'Language' field if --lang was specified or
+ if it is missing and can be inferred from the 'Language-Team' field.
+ * msgl-header.h: New file.
+ * msgl-header.c: New file.
+ * msgcat.c: Include msgl-header.h.
+ (long_options): Add --lang option.
+ (main): Handle --lang option.
+ (usage): Document --lang option.
+ * msgen.c: Include msgl-header.h.
+ (long_options): Add --lang option.
+ (main): Handle --lang option.
+ (usage): Document --lang option.
+ * msgl-check.c (plural_help): Look at the 'Language' field before
+ looking at the 'Language-Team' field.
+ (check_header_entry): Fix comparison with default value. When the
+ 'Language' field is missing, signal a warning.
+ * Makefile.am (libgettextsrc_la_SOURCES): Add msgl-header.c.
+ (msgmerge_SOURCES): Add lang-table.c.
+ * FILES: Update.
+
+2007-12-22 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Avoid breaking line in the middle of a format
+ directive.
+ Reported by Dwayne Bailey <dwayne@translate.org.za>.
+
+2007-11-07 Jim Meyering <meyering@redhat.com>
+ Bruno Haible <bruno@clisp.org>
+
+ * write-catalog.c (msgdomain_list_print): Fix open() call.
+
+2007-11-07 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.17 released.
+
+2007-10-28 Bruno Haible <bruno@clisp.org>
+
+ * color.c (style_file_lookup): New function.
+ (style_file_prepare): Use it.
+
+2007-10-21 Bruno Haible <bruno@clisp.org>
+
+ Normalize the leading space of every comment line during input, not
+ during output.
+ * read-catalog-abstract.c (po_callback_comment_dispatcher): Before
+ calling po_callback_comment or po_callback_comment_dot, drop the
+ leading space.
+ * write-po.c (message_print_comment, message_print_comment_dot): Don't
+ remove the leading space of every comment line.
+ * write-stringtable.c (write_message): Likewise.
+ Suggested by Dwayne Bailey <dwayne@translate.org.za>.
+
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (message_merge): Set the fuzzy flag if the msgid_plural
+ changed.
+ Suggested by Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ Make msgmerge's introduction of fuzzy markers more consistent with
+ msgfmt's --check-format.
+ * format.h (check_msgid_msgstr_format_i): New declaration.
+ * format.c (check_msgid_msgstr_format_i): New function, extracted from
+ check_msgid_msgstr_format.
+ (check_msgid_msgstr_format): Use it.
+ * msgl-check.h: Include plural-eval.h.
+ (check_plural_eval): New declaration.
+ * msgl-check.c (check_plural_eval): Add const to first parameter. Make
+ non-static.
+ (check_plural): Update.
+ * msgmerge.c: Include plural-exp.h, msgl-check.h, po-xerror.h.
+ (msgfmt_check_pair_fails): Remove function.
+ (silent_error_logger, silent_xerror): New functions.
+ (message_merge): Add plural_distribution, plural_distribution_length
+ arguments. Call check_msgid_msgstr_format_i instead of
+ msgfmt_check_pair_fails.
+ (match_domain): Extract not only the plural count, but also the
+ plural expression from the header entry. Determine the plural
+ distribution from it. Pass it to message_merge.
+ Reported by Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.c (check_plural): If there is no header entry, or if the
+ header entry does not contain plural= and nplurals=, return a plural
+ distribution corresponding to the Germanic plural.
+
+2007-10-20 Bruno Haible <bruno@clisp.org>
+
+ * format.h (check_msgid_msgstr_format): Add plural_distribution_length
+ argument.
+ * format.c (check_msgid_msgstr_format): Likewise.
+ * msgl-check.h (check_message): Likewise.
+ * msgl-check.c (check_plural_eval, check_plural, check_pair,
+ check_message): Likewise.
+ (check_message_list): Update.
+
+2007-10-18 Bruno Haible <bruno@clisp.org>
+
+ * plural-count.c (get_plural_count): Adapt to changed prototype of
+ extract_plural_expression.
+ * write-csharp.c (write_csharp_expression): Add const to parameter.
+ (write_csharp_code): Adapt to changed prototype of
+ extract_plural_expression.
+ * write-java.c (write_java_expression): Add const to parameter.
+ (write_java_code): Adapt to changed prototype of
+ extract_plural_expression.
+
+2007-10-07 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (main): In the --version output, say GPLv3+.
+ * msgattrib.c (main): Likewise.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * recode-sr-latin.c (main): Likewise.
+ * urlget.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2007-09-30 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (package_name, package_version): New variables.
+ (long_options): Add --package-name, --package-version.
+ (main): Handle options --package-name, --package-version.
+ (usage): Document them.
+ (construct_header): Use package_name if present.
+ * msginit.c (project_id_version): Take a header argument. Return the
+ old value if already present.
+ (fields): Update.
+
+2007-09-30 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_kde'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_kde entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_kde): New declaration.
+ * format-kde.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_kde.
+ * x-c.c (init_flag_table_c): Also register flags for qt-format and
+ kde-format.
+ * xgettext.c (flag_table_cxx_kde): New variable.
+ (recognize_format_kde): New variable.
+ (main): Handle --kde option.
+ (usage): Document --kde option.
+ (xgettext_record_flag): Also fill flag_table_cxx_kde.
+ (remember_a_message, remember_a_message_plural): In the heuristics,
+ don't mark a string as c-format that is already known to be a
+ kde-format.
+ (language_to_extractor): Use a flag_table_cxx_kde that is different
+ from flag_table_c.
+ * Makefile.am (FORMAT_SOURCE): Add format-kde.c.
+ * FILES: Update.
+
+2007-09-09 Bruno Haible <bruno@clisp.org>
+
+ Add support for Qt 4 format strings.
+ * format-qt.c (struct spec): Increase args_used field size to 100.
+ Add 'simple' field.
+ (format_parse): Update for Qt 4 syntax. Remove error message when the
+ same format argument is used more than once.
+ (format_check): Add check: If the msgid is simple, the msgstr must be
+ simple as well.
+ Reported by Chusslove Illich <caslav.ilic@gmx.net>.
+
+2007-09-02 Bruno Haible <bruno@clisp.org>
+
+ Correct handling of different libexpat ABIs.
+ * x-glade.c: Include <stdint.h>.
+ (p_XML_GetCurrentLineNumber, p_XML_GetCurrentColumnNumber): New
+ variables.
+ (is_XML_LARGE_SIZE_ABI, GetCurrentLineNumber, GetCurrentColumnNumber):
+ New functions.
+ (XML_GetCurrentLineNumber, XML_GetCurrentColumnNumber): Redefine.
+ (XML_Expat_Version, XML_FeatureEnum, XML_Feature): New types.
+ (p_XML_ExpatVersionInfo, p_XML_GetFeatureList): New variables.
+ (p_XML_GetCurrentLineNumber, p_XML_GetCurrentColumnNumber): Change
+ type to 'void *'.
+ (XML_ExpatVersionInfo, XML_GetFeatureList): New macros.
+ (XML_Size_ABI): New type.
+ (get_XML_Size_ABI): New function.
+ (XML_GetCurrentLineNumber, XML_GetCurrentColumnNumber): New functions.
+ (load_libexpat): Try both version 2 and version 1. Initialize
+ p_XML_ExpatVersionInfo, p_XML_GetFeatureList.
+
+2007-09-02 Bruno Haible <bruno@clisp.org>
+
+ Implement msgctxt for C# ResourceManagers.
+ * x-csharp.c (init_keywords): Also register GetParticularString and
+ GetParticularPluralString.
+ (init_flag_table_csharp): Update accordingly.
+ * write-csharp.c: Include xmalloca.h.
+ (write_csharp_msgid): New function.
+ (write_csharp_code): Use it instead of write_csharp_string.
+ (msgdomain_write_csharp): Remove error message if mlp has entries with
+ context.
+ * msgunfmt.cs (DumpResource.DumpMessage): Emit an msgctxt line
+ if the message contain the context separator.
+
+2007-09-01 Bruno Haible <bruno@clisp.org>
+
+ Implement msgctxt for Java ResourceBundles.
+ * x-java.c (init_keywords): Also register pgettext and npgettext.
+ (init_flag_table_java): Update accordingly.
+ * write-java.c (msgid_hashcode): New function.
+ (compute_hashsize, compute_table_items): Use it instead of
+ string_hashcode.
+ (write_java_msgid): New function.
+ (write_java_code): Use it instead of write_java_string.
+ (msgdomain_write_java): Remove error message if mlp has entries with
+ context.
+ * gnu/gettext/DumpResource.java (dumpMessage): Emit an msgctxt line
+ if the message contain the context separator.
+ Suggested by Felix Berger.
+
+2007-09-01 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c: Include uniwidth.h.
+
+2007-06-19 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (phase8_get): Call free_token, so that the reference to
+ tmp.comment gets dropped.
+
+2007-08-27 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c (phase7_getuc): Interpret octal and hexadecimal escapes
+ as Unicode code points inside Unicode strings.
+ Reported and patch by Jakub Wilk <ubanus@users.sf.net>.
+
+2007-08-23 Bruno Haible <bruno@clisp.org>
+
+ * file-list.c: Don't include getline.h.
+ * format-awk.c: Likewise.
+ * format-boost.c: Likewise.
+ * format-c.c: Likewise.
+ * format-csharp.c: Likewise.
+ * format-elisp.c: Likewise.
+ * format-gcc-internal.c: Likewise.
+ * format-java.c: Likewise.
+ * format-librep.c: Likewise.
+ * format-lisp.c: Likewise.
+ * format-pascal.c: Likewise.
+ * format-perl-brace.c: Likewise.
+ * format-perl.c: Likewise.
+ * format-php.c: Likewise.
+ * format-python.c: Likewise.
+ * format-qt.c: Likewise.
+ * format-scheme.c: Likewise.
+ * format-sh.c: Likewise.
+ * format-tcl.c: Likewise.
+ * format-ycp.c: Likewise.
+ * msginit.c: Likewise.
+ * x-perl.c: Likewise.
+
+2007-08-18 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c (msgdomain_write_csharp): Don't recommend to use
+ --verbose if it is already in use.
+ * write-java.c (msgdomain_write_java): Likewise.
+ Reported by Karsten Kousgaard <karsten@f-kousgaard.dk>.
+
+2007-07-07 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (AM_CPPFLAGS): Remove reference to libuniname directory.
+ (LIBUNINAME): Remove variable.
+ (xgettext_LDADD): Update.
+
+2007-07-04 Bruno Haible <bruno@clisp.org>
+
+ Recognize the PHP string concatenation operator.
+ * x-php.c (enum token_type_ty): New elements token_type_dot,
+ token_type_operator1, token_type_operator2.
+ (struct token_ty): Add comment field.
+ (free_token): Drop reference to comment field.
+ (phase4_pushback, phase4_pushback_length): New variables.
+ (phase4_get): Renamed from x_php_lex. Return last pushed-back token if
+ available. Recognize tokens '.', '+', '-', '*', '/', '%', '++', '--',
+ '!', '~', '@'. Fill in tp->comment.
+ (phase4_unget): New function.
+ (phase5_last): New variable.
+ (x_php_lex): New function.
+ (extract_balanced): Handle the new token types. Pass token's comment
+ to remember_a_message.
+ (extract_php): Initialize phase5_last.
+ Reported by Jan Engelhardt <jengelh@computergmbh.de>.
+
+2007-06-30 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (main): Use the standard --version output, see
+ <http://lists.gnu.org/archive/html/bug-gnulib/2007-03/msg00302.html>.
+ * msgattrib.c (main): Likewise.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * recode-sr-latin.c (main): Likewise.
+ * urlget.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2007-06-30 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Fix entry for Javanese.
+
+2007-06-28 Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c (format_parse): Tweak an error message.
+ Reported by Karl Eichwalder.
+
+2007-06-28 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.h (language_variant_table, language_variant_table_size):
+ New declarations.
+ * lang-table.c (language_variant_table, language_variant_table_size):
+ New variables.
+ * msginit.c (language_team_englishname): New function.
+ (language_team): Use it instead of englishname_of_language.
+ Reported by Li Daobing <lidaobing@gmail.com>.
+
+2007-06-25 Bruno Haible <bruno@clisp.org>
+
+ Change boundary of extracted comments.
+ * x-perl.c (struct token_ty): Add comment field.
+ (free_token): Release tp->comment.
+ (extract_quotelike): Fill in tp->comment.
+ (x_perl_prelex): Set or release tp->comment when constructing tokens.
+ (x_perl_lex): Likewise.
+ (extract_balanced): Pass token's comment to remember_a_message.
+
+2007-06-18 Bruno Haible <bruno@clisp.org>
+
+ Change boundary of extracted comments.
+ * x-ycp.c (struct token_ty): Add comment field.
+ (free_token): New function.
+ (phase5_get): Fill in tp->comment.
+ (phase8_get): Use free_token.
+ (extract_parenthesized): Pass token's comment to remember_a_message.
+ Use free_token.
+ Reported by Karl Eichwalder <ke@suse.de>.
+
+2007-06-09 Bruno Haible <bruno@clisp.org>
+
+ * format-java.c: Include xmalloca.h instead of xallocsa.h.
+ (message_format_parse): Use xmalloca/freea instead of xallocsa/freesa.
+ * message.c: Include xmalloca.h instead of xallocsa.h.
+ (message_list_hash_insert_entry, message_list_search): Use
+ xmalloca/freea instead of xallocsa/freesa.
+ * msggrep.c: Include xmalloca.h instead of xallocsa.h.
+ (is_message_selected_no_invert): Use xmalloca/freea instead of
+ xallocsa/freesa.
+ * msginit.c: Include xmalloca.h instead of xallocsa.h.
+ (subst_string): Use xmalloca/freea instead of xallocsa/freesa.
+ * msgl-cat.c: Include xmalloca.h instead of xallocsa.h.
+ (catenate_msgdomain_list): Use xmalloca/freea instead of
+ xallocsa/freesa.
+ * msgl-charset.c: Include xmalloca.h instead of xallocsa.h.
+ (compare_po_locale_charsets): Use xmalloca/freea instead of
+ xallocsa/freesa.
+ * msgl-iconv.c: Include xmalloca.h instead of xallocsa.h.
+ (iconv_message_list_internal, is_message_list_iconvable): Use
+ xmalloca/freea instead of xallocsa/freesa.
+ * msgmerge.c: Include xmalloca.h instead of xallocsa.h.
+ (merge): Use xmalloca/freea instead of xallocsa/freesa.
+ * po-charset.c: Include xmalloca.h instead of xallocsa.h.
+ (po_lex_charset_set): Use xmalloca/freea instead of xallocsa/freesa.
+ * read-tcl.c: Include xmalloca.h instead of xallocsa.h.
+ (msgdomain_read_tcl): Use xmalloca/freea instead of xallocsa/freesa.
+ * write-java.c: Include xmalloca.h instead of xallocsa.h.
+ (compute_hashsize, msgdomain_write_java): Use xmalloca/freea instead of
+ xallocsa/freesa.
+ * write-mo.c: Include xmalloca.h instead of xallocsa.h.
+ (write_table): Use xmalloca/freea instead of xallocsa/freesa.
+ * write-po.c: Include xmalloca.h instead of xallocsa.h.
+ (msgdomain_list_print_po): Use xmalloca/freea instead of
+ xallocsa/freesa.
+ * write-tcl.c: Include xmalloca.h instead of xallocsa.h.
+ (msgdomain_write_tcl): Use xmalloca/freea instead of xallocsa/freesa.
+ * xgettext.c: Include xmalloca.h instead of xallocsa.h.
+ (flag_context_list_table_insert): Use xmalloca/freea instead of
+ xallocsa/freesa.
+
+2007-06-08 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+ * recode-sr-latin.c (main): Likewise.
+ * hostname.c (main): Likewise.
+ * urlget.c (main): Likewise.
+
+2007-06-08 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c: Include localename.h.
+ (_nl_locale_name): Remove declaration.
+ (main): Use gl_locale_name instead of _nl_locale_name.
+ * Makefile.am (msginit_LDADD): Remove localename object file.
+
+2007-05-28 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (WOE32_LDADD): New variable.
+ (LDADD, msgcmp_LDADD, msgfmt_LDADD, msgmerge_LDADD, msgunfmt_LDADD,
+ xgettext_LDADD, msgattrib_LDADD, msgcat_LDADD, msgcomm_LDADD,
+ msgconv_LDADD, msgen_LDADD, msgexec_LDADD, msgfilter_LDADD,
+ msggrep_LDADD, msginit_LDADD, msguniq_LDADD, recode_sr_latin_LDADD):
+ Include it.
+ (gettext.res): New rule.
+ (MOSTLYCLEANFILES): Add gettext.res.
+
+2007-05-27 Bruno Haible <bruno@clisp.org>
+
+ Avoid link error; u16_mbtouc_aux is no longer a public API.
+ * x-java.c (string_buffer_append): Use u16_mbtouc instead of
+ u16_mbtouc_aux.
+ * x-python.c (mixed_string_buffer_append): Likewise.
+
+2007-05-13 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (mkdir): Remove definition. Now done by gnulib.
+ * write-csharp.c (mkdir): Remove definition. Now done by gnulib.
+
+2007-05-13 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.c: Include stdio.h instead of vasprintf.h.
+ * po-lex.c: Don't include vasprintf.h.
+
+2007-03-27 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (usage): Ask translators to specify a translation bug
+ report address.
+ * msgattrib.c (usage): Likewise.
+ * msgcat.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * msgcomm.c (usage): Likewise.
+ * msgconv.c (usage): Likewise.
+ * msgen.c (usage): Likewise.
+ * msgexec.c (usage): Likewise.
+ * msgfilter.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * msggrep.c (usage): Likewise.
+ * msginit.c (usage): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * msguniq.c (usage): Likewise.
+ * recode-sr-latin.c (usage): Likewise.
+ * urlget.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+
+2007-03-24 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c: Include unistr.h instead of utf8-ucs4.h.
+ * read-properties.c: Include unistr.h instead of utf16-ucs4.h,
+ ucs4-utf8.h.
+ * x-java.c: Likewise.
+ * read-stringtable.c: Include unistr.h instead of utf8-ucs4.h,
+ ucs4-utf8.h.
+ * x-csharp.c: Likewise.
+ * write-csharp.c: Include unistr.h instead of utf8-ucs4.h.
+ * write-java.c: Likewise.
+ * write-properties.c: Likewise.
+ * write-qt.c: Likewise.
+ * write-tcl.c: Likewise.
+ * x-perl.c: Likewise.
+ * x-python.c: Include unistr.h instead of utf8-ucs4.h, utf16-ucs4.h,
+ ucs4-utf8.h.
+ * x-tcl.c: Include unistr.h instead of ucs4-utf8.h.
+
+2007-03-17 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (enum token_type_ty): New values token_type_lbracket,
+ token_type_rbracket.
+ (x_php_lex): Recognize also token_type_lbracket, token_type_rbracket.
+ (extract_balanced): Renamed from extract_parenthesized. Add 'delim'
+ argument. Handle token_type_lbracket and token_type_rbracket.
+ (extract_php): Update.
+ Reported by Robert Vock <RobertVock@gmx.de>.
+
+2007-03-10 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (get_user_fullname): Reduce scope of local variables.
+
+2007-03-04 Bruno Haible <bruno@clisp.org>
+
+ Moved --enable-relocatable infrastructure to gnulib.
+ * Makefile.am: Remove SET_RELOCATABLE invocation.
+
+2007-02-25 Bruno Haible <bruno@clisp.org>
+
+ * color.c: Include filename.h instead of pathname.h.
+ (style_file_prepare): Update.
+ * msginit.c: Include filename.h instead of pathname.h.
+ (project_id, project_id_version): Update.
+ * open-catalog.c: Include filename.h instead of pathname.h.
+ (try_open_catalog_file): Update.
+ * read-csharp.c: Include filename.h instead of pathname.h.
+ (msgdomain_read_csharp): Update.
+ * read-resources.c: Include filename.h instead of pathname.h.
+ (read_resources_file): Update.
+ * read-tcl.c: Include filename.h instead of pathname.h.
+ (msgdomain_read_tcl): Update.
+ * write-csharp.c: Include filename.h instead of pathname.h.
+ (msgdomain_write_csharp): Update.
+ * write-java.c Include filename.h instead of pathname.h.
+ (msgdomain_write_java): Update.
+ * write-resources.c: Include filename.h instead of pathname.h.
+ (msgdomain_write_csharp_resources): Update.
+ * write-tcl.c: Include filename.h instead of pathname.h.
+ (msgdomain_write_tcl): Update.
+ * xgettext.c: Include filename.h instead of pathname.h.
+ (main, xgettext_open): Update.
+
+2007-02-19 Bruno Haible <bruno@clisp.org>
+
+ * file-list.c: Don't include exit.h.
+ * hostname.c: Likewise.
+ * msgattrib.c: Likewise.
+ * msgcat.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgen.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgfilter.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgl-cat.c: Include <stdlib.h> instead of exit.h.
+ * msgl-charset.c: Likewise.
+ * msgl-check.c: Don't include exit.h.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * po-lex.c: Likewise.
+ * po-xerror.c: Likewise.
+ * read-csharp.c: Likewise.
+ * read-java.c: Likewise.
+ * read-mo.c: Likewise.
+ * read-resources.c: Likewise.
+ * read-tcl.c: Likewise.
+ * recode-sr-latin.c: Likewise.
+ * urlget.c: Likewise.
+ * write-mo.c: Likewise.
+ * write-qt.c: Likewise.
+ * write-resources.c: Likewise.
+ * write-tcl.c: Include <stdlib.h> instead of exit.h.
+ * x-awk.c: Don't include exit.h.
+ * x-c.c: Likewise.
+ * x-csharp.c: Likewise.
+ * x-elisp.c: Likewise.
+ * x-glade.c: Likewise.
+ * x-java.c: Likewise.
+ * x-librep.c: Likewise.
+ * x-lisp.c: Likewise.
+ * x-perl.c: Likewise.
+ * x-php.c: Likewise.
+ * x-python.c: Likewise.
+ * x-rst.c: Include <stdlib.h> instead of exit.h.
+ * x-scheme.c: Don't include exit.h.
+ * x-sh.c: Likewise.
+ * x-smalltalk.c: Likewise.
+ * x-tcl.c: Likewise.
+ * x-ycp.c: Likewise.
+ * xgettext.c: Likewise.
+
+2007-01-28 Bruno Haible <bruno@clisp.org>
+
+ * x-java.c (string_buffer_append_lone_surrogate): New function.
+ (string_buffer_flush_utf16_surr, string_buffer_append): Use it.
+
+2007-01-28 Bruno Haible <bruno@clisp.org>
+
+ * x-java.c (string_buffer_flush_utf16_surr): Give a warning when
+ converting a surrogate code point to U+FFFD.
+ (string_buffer_append): Convert a lone high surrogate code point to
+ U+FFFD, and give a warning.
+
+2007-01-26 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: Include <sys/time.h> unconditionally.
+
+2007-01-26 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c: Don't include strpbrk.h, use <string.h> instead.
+
+2007-01-26 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c: Don't include stpncpy.h, use <string.h> instead.
+
+2007-01-26 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c: Don't include stpcpy.h, use <string.h> instead.
+ * msgmerge.c: Likewise.
+
+2007-01-21 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c (convert_string, convert_msgstr, iconvable_string,
+ iconvable_msgstr): Update for changed calling convention of
+ xmem_cd_iconv.
+ * recode-sr-latin.c (process): Likewise.
+
+2007-01-07 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (message_merge): Copy the obsolete bit from the ref
+ message.
+ Reported by Leonardo Fontenelle <leo.fontenelle@gmail.com>.
+
+2006-12-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (AM_CXXFLAGS): New variable.
+
+2006-12-23 Bruno Haible <bruno@clisp.org>
+
+ * color.c (print_color_test): Print labels before the hue/saturation
+ rectangle. Show also a few lines that mix colors and attributes.
+
+2006-12-03 Bruno Haible <bruno@clisp.org>
+
+ * msgcat.c: Include color.h.
+ (long_options): Add --color, --style options.
+ (main): Handle them. Invoke print_color_test when --color=test was
+ given.
+ (usage): Document --color, --style options.
+
+2006-12-01 Bruno Haible <bruno@clisp.org>
+
+ Add styling support to the PO output routines.
+ * color.h: New file.
+ * color.c: New file.
+ * write-catalog.h (struct catalog_output_format): Add field
+ 'supports_color'.
+ * write-catalog.c: Include fcntl.h, unistd.h, styled-ostream.h,
+ term-styled-ostream.h, html-styled-ostream.h, fd-ostream.h, color.h,
+ po-charset.h, msgl-iconv.h.
+ (STDOUT_FILENO): New macro.
+ (ENABLE_COLOR): New macro.
+ (msgdomain_list_print): Use a styled_ostream_t that uses the
+ style_file_name.
+ * write-po.c: Include format.h, styled-ostream.h.
+ (is_stylable, begin_css_class, end_css_class): New functions or macros.
+ (class_header, class_translated, class_untranslated, class_fuzzy,
+ class_obsolete, class_comment, class_translator_comment,
+ class_extracted_comment, class_reference_comment, class_reference,
+ class_flag_comment, class_flag, class_fuzzy_flag,
+ class_previous_comment, class_previous, class_msgid, class_msgstr,
+ class_keyword, class_string, class_text, class_escape_sequence,
+ class_format_directive, class_invalid_format_directive): New variables.
+ (ATTR_ESCAPE_SEQUENCE, ATTR_FORMAT_DIRECTIVE,
+ ATTR_INVALID_FORMAT_DIRECTIVE): New enum items.
+ (message_print_comment, message_print_comment_dot,
+ message_print_comment_filepos, message_print_comment_flags): Call
+ begin/end_css_class.
+ (memset_small): New function.
+ (wrap): Add css_class argument. Determine the extent of format
+ string directives. Call begin/end_css_class.
+ (print_blank_line): Call begin/end_css_class.
+ (message_print, message_print_obsolete): Likewise. Pass a css_class
+ argument to 'wrap'.
+ (msgdomain_list_print_po): Call begin/end_css_class.
+ (output_format_po): Update.
+ * write-properties.c (output_format_properties): Update.
+ * write-stringtable.c (output_format_stringtable): Update.
+ * Makefile.am (noinst_HEADERS): Add color.h.
+ (libgettextsrc_la_SOURCES): Add color.c.
+
+2006-11-26 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (make_format_description_string,
+ make_c_width_description_string): Strip off the leading space.
+ (message_print_comment_flags): Print the space here.
+ * write-stringtable.c (write_message): Print the space here.
+
+2006-11-26 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c: Include write-catalog.h instead of write-properties.h,
+ write-stringtable.h.
+
+2006-11-26 Bruno Haible <bruno@clisp.org>
+
+ * format.h (FMTDIR_START, FMTDIR_END, FMTDIR_ERROR): New enum values.
+ (FDI_SET): New macro.
+ (struct formatstring_parser): Add fdi argument to 'parse' method.
+ * format.c (check_msgid_msgstr_format): Update.
+ * format-awk.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-boost.c (format_parse): Likewise.
+ * format-c.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ (format_c_parse, format_objc_parse): Add fdi argument.
+ (get_sysdep_c_format_directives): Update.
+ * format-csharp.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-elisp.c (format_parse): Likewise.
+ * format-gcc-internal.c (format_parse): Likewise.
+ * format-java.c (message_format_parse): Add fdi argument. Invoke
+ FDI_SET.
+ (choice_format_parse): Update.
+ (format_parse): Add fdi argument.
+ * format-librep.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-lisp.c (parse_upto): Add fdi argument. Invoke FDI_SET.
+ (format_parse): Add fdi argument.
+ * format-pascal.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-perl.c (format_parse): Likewise.
+ * format-perl-brace.c (format_parse): Likewise.
+ * format-php.c (format_parse): Likewise.
+ * format-python.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ (get_python_format_unnamed_arg_count): Update.
+ * format-qt.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-scheme.c (parse_upto): Add fdi argument. Invoke FDI_SET.
+ (format_parse): Add fdi argument.
+ * format-sh.c (format_parse): Add fdi argument. Invoke FDI_SET.
+ * format-tcl.c (format_parse): Likewise.
+ * format-ycp.c (format_parse): Likewise.
+ * msgmerge.c (msgfmt_check_pair_fails): Update.
+ * read-mo.c (read_mo_file): Update.
+ * xgettext.c (set_format_flags_from_context, remember_a_message,
+ remember_a_message_plural): Update.
+
+2006-11-25 Bruno Haible <bruno@clisp.org>
+
+ * message.h (message_list_copy, msgdomain_list_copy): New declarations.
+ * message.c (message_list_copy, msgdomain_list_copy): New functions.
+
+2006-11-25 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.h (iconv_msgdomain_list): Add update_header argument.
+ * msgl-iconv.c (iconv_message_list_internal): Renamed from
+ iconv_msgdomain_list. Add update_header argument.
+ (iconv_message_list): New function.
+ (iconv_msgdomain_list): Add update_header argument.
+ * msgcmp.c (compare): Update iconv_msgdomain_list call.
+ * msgconv.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgmerge.c (merge): Likewise.
+ * xgettext.c (main): Likewise.
+
+2006-11-23 Bruno Haible <bruno@clisp.org>
+
+ * write-catalog.h: Include ostream.h. Don't include <stdio.h>.
+ (struct catalog_output_format): Use ostream_t instead of 'FILE *'.
+ * write-catalog.c: Include ostream.h, file-ostream.h.
+ (msgdomain_list_print): Open a temporary file_ostream_t.
+ * write-po.h: Include ostream.h. Don't include <stdio.h>.
+ (message_print_comment, message_print_comment_dot,
+ message_print_comment_filepos, message_print_comment_flags): Replace
+ 'FILE *' argument with an 'ostream_t' argument.
+ * write-po.c: Include ostream.h.
+ (message_print_comment, message_print_comment_dot,
+ message_print_comment_filepos, message_print_comment_flags, wrap,
+ print_blank_line, message_print, message_print_obsolete,
+ msgdomain_list_print_po): Replace 'FILE *' argument with an 'ostream_t'
+ argument.
+ * write-properties.c: Include ostream.h.
+ (write_escaped_string, write_message, write_properties,
+ msgdomain_list_print_properties): Replace 'FILE *' argument with an
+ 'ostream_t' argument.
+ * write-stringtable.c: Include ostream.h, xvasprintf.h. Don't include
+ <stdio.h>.
+ (write_escaped_string, write_message, write_stringtable,
+ msgdomain_list_print_stringtable): Replace 'FILE *' argument with an
+ 'ostream_t' argument.
+
+2006-11-12 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Indent by use of spaces, not tabs.
+
+2006-11-03 Bruno Haible <bruno@clisp.org>
+
+ Simplify xmalloc expressions. Add overflow check in xmalloc arguments.
+ * filter-sr-latin.c (serbian_to_latin): Use XNMALLOC instead of xmalloc.
+ * format-awk.c (format_parse): Use XMALLOC instead of xmalloc.
+ * format-boost.c (format_parse): Likewise.
+ * format-c.c (format_parse): Use XMALLOC, XNMALLOC instead of xmalloc.
+ (get_sysdep_c_format_directives): Use XNMALLOC instead of xmalloc.
+ * format-csharp.c (format_parse): Use XMALLOC instead of xmalloc.
+ * format-elisp.c (format_parse): Likewise.
+ * format-gcc-internal.c (format_parse): Likewise.
+ * format-java.c (format_parse): Likewise.
+ * format-librep.c (format_parse): Likewise.
+ * format-lisp.c (copy_list): Use XMALLOC, XNMALLOC instead of xmalloc.
+ (make_unconstrained_list): Likewise.
+ (make_empty_list): Use XMALLOC instead of xmalloc.
+ (rotate_loop): Use XNMALLOC instead of xmalloc.
+ (make_intersected_list): Use XMALLOC instead of xmalloc.
+ (make_union_list): Use XMALLOC, XNMALLOC instead of xmalloc.
+ (make_repeated_list_of_lists): Likewise.
+ (make_repeated_list): Likewise.
+ (format_parse): Use XMALLOC instead of xmalloc.
+ * format-pascal.c (format_parse): Likewise.
+ * format-perl.c (format_parse): Likewise.
+ * format-perl-brace.c (format_parse): Use XMALLOC, XNMALLOC instead of
+ xmalloc.
+ * format-php.c (format_parse): Use XMALLOC instead of xmalloc.
+ * format-python.c (format_parse): Use XMALLOC, XNMALLOC instead of
+ xmalloc.
+ * format-qt.c (format_parse): Use XMALLOC instead of xmalloc.
+ * format-scheme.c (copy_list): Use XMALLOC, XNMALLOC instead of xmalloc.
+ (make_unconstrained_list): Likewise.
+ (make_empty_list): Use XMALLOC instead of xmalloc.
+ (rotate_loop): Use XNMALLOC instead of xmalloc.
+ (make_intersected_list): Use XMALLOC instead of xmalloc.
+ (make_union_list): Use XMALLOC, XNMALLOC instead of xmalloc.
+ (make_repeated_list_of_lists): Likewise.
+ (make_repeated_list): Likewise.
+ (format_parse): Use XMALLOC instead of xmalloc.
+ * format-sh.c (format_parse): Use XMALLOC, XNMALLOC instead of xmalloc.
+ * format-tcl.c (format_parse): Use XMALLOC instead of xmalloc.
+ * format-ycp.c (format_parse): Likewise.
+ * message.c (message_alloc, message_list_list_alloc, msgdomain_alloc):
+ Use XMALLOC instead of xmalloc.
+ (msgdomain_list_alloc): Use XMALLOC, XNMALLOC instead of xmalloc.
+ * msgexec.c (main): Use XNMALLOC instead of xmalloc.
+ * msgfilter.c (main, generic_filter, process_message): Likewise.
+ * msgfmt.c (add_mo_suffix): Likewise.
+ (new_domain): Use XMALLOC instead of xmalloc.
+ * msginit.c (catalogname_for_locale, language_of_locale,
+ get_user_fullname, get_field, put_field, subst_string,
+ update_msgstr_plurals): Use XNMALLOC instead of xmalloc.
+ * msgl-cat.c (catenate_msgdomain_list): Likewise.
+ * msgl-check.c (check_plural_eval): Use XCALLOC instead of xcalloc.
+ * msgl-english.c (msgdomain_list_english): Use XNMALLOC instead of
+ xmalloc.
+ * msgl-fsearch.c (new_index): Likewise.
+ (message_fuzzy_index_alloc): Use XMALLOC instead of xmalloc.
+ (mult_index_list_accumulate): Use XNMALLOC instead of xmalloc.
+ * msgl-iconv.c (iconv_message_list): Likewise.
+ * msgmerge.c (message_merge, match_domain): Likewise.
+ * po-gram-gen.y (plural_form_list): Likewise.
+ * read-catalog-abstract.c (po_parse_comment_filepos,
+ po_parse_comment_solaris_filepos): Likewise.
+ * read-mo.c (get_sysdep_string): Likewise.
+ * read-properties.c (conv_from_iso_8859_1, read_escaped_string):
+ Likewise.
+ * read-stringtable.c (conv_from_ucs4): Likewise.
+ * str-list.c (string_list_alloc): Use XMALLOC instead of xmalloc.
+ (string_list_concat, string_list_join): Use XNMALLOC instead of xmalloc.
+ * write-csharp.c (construct_class_name, msgdomain_write_csharp):
+ Likewise.
+ * write-java.c (compute_hashsize, compute_table_items): Likewise.
+ * write-mo.c: Include xsize.h.
+ (write_table): Use XNMALLOC instead of xmalloc. Use xsum, xtimes in
+ xmalloc argument.
+ * write-po.c (wrap): Use XNMALLOC instead of xmalloc.
+ * write-properties.c (conv_to_java): Likewise.
+ * write-qt.c (conv_to_iso_8859_1, conv_to_utf16): Likewise.
+ * x-elisp.c (init_token, string_of_object): Likewise.
+ (read_object): Use XMALLOC instead of xmalloc.
+ * x-librep.c (init_token, string_of_object): Use XNMALLOC instead of
+ xmalloc.
+ (read_object): Use XMALLOC instead of xmalloc.
+ * x-lisp.c (x_lisp_keyword, init_token, string_of_object): Use XNMALLOC
+ instead of xmalloc.
+ (read_object): Use XMALLOC instead of xmalloc.
+ * x-perl.c (get_here_document, extract_quotelike_pass1,
+ extract_quotelike_pass3): Use XNMALLOC instead of xmalloc.
+ (x_perl_lex): Use XMALLOC instead of xmalloc.
+ * x-po.c (strextract_add_message, extract): Use XNMALLOC instead of
+ xmalloc.
+ * x-python.c (try_to_extract_coding): Use XNMALLOC instead of xmalloc.
+ * x-scheme.c (init_token, string_of_object): Use XNMALLOC
+ instead of xmalloc.
+ (read_object): Use XMALLOC instead of xmalloc.
+ * x-sh.c (init_token, string_of_token, string_of_word): Use XNMALLOC
+ instead of xmalloc.
+ (read_word): Use XMALLOC instead of xmalloc.
+ * x-smalltalk.c (phase2_get): Use XNMALLOC instead of xmalloc.
+ * x-tcl.c (init_token, string_of_word): Use XNMALLOC instead of xmalloc.
+ (read_word): Use XMALLOC instead of xmalloc.
+ * xgettext.c: Include xsize.h.
+ (split_keywordspec): Use XNMALLOC instead of xmalloc.
+ (insert_keyword_callshape): Use XMALLOC instead of xmalloc. Use xsum,
+ xtimes to avoid overflow.
+ (flag_context_list_table_insert): Use XMALLOC instead of xmalloc.
+ (savable_comment_add): Likewise.
+ (remember_a_message_plural): Use XNMALLOC instead of xmalloc.
+ (arglist_parser_alloc, arglist_parser_clone): Use xsum, xtimes.
+ (arglist_parser_done): Use XNMALLOC instead of xmalloc.
+ (finalize_header): Likewise.
+
+2006-10-30 Bruno Haible <bruno@clisp.org>
+
+ * plural-eval.h [C++]: Define functions without name mangling.
+
+2006-10-29 Bruno Haible <bruno@clisp.org>
+
+ Clean up libgettextpo exports in C++ mode.
+ * po-gram.h: Wrap declarations in extern "C".
+
+2006-10-29 Bruno Haible <bruno@clisp.org>
+
+ Make it compile in C++ mode.
+ * dir-list.c (dir_list_restore): Add cast.
+ * format-c.c (format_arg_type_t): New type. Use it instead of
+ 'enum format_arg_type'.
+ * format-gcc-internal.c (format_arg_type_t): New type. Use it instead of
+ 'enum format_arg_type'.
+ * format-perl.c (format_arg_type_t): New type. Use it instead of
+ 'enum format_arg_type'.
+ * format-lisp.c (struct segment): Move out of the scope of struct
+ format_arg_list.
+ * format-scheme.c (struct segment): Likewise.
+ * message.h (struct altstr): Move out of the scope of struct message_ty.
+ * msgl-equal.c (msgstr_equal_ignoring_potcdate): Cast memchr results.
+ * po-lex.c (mb_copy): Rename arguments to new_mbc, old_mbc.
+ * read-mo.c (enum mo_endianness): Move out of the scope of struct
+ binary_mo_file.
+ * read-po.c (this): New macro.
+ * read-properties.c (this): New macro.
+ * write-mo.c (write_table): Cast alloca result.
+ * x-glade.c (load_libexpat): Cast dlsym results.
+ * x-perl.c (get_here_document): Use xmalloc instead of
+ xrealloc (NULL...). Cast its result.
+ * x-po.c (extract_add_message): Limit the scope of variable
+ 'charsetstr'.
+ * x-smalltalk.c (phase2_get): Cast xmalloc results.
+
+2006-10-29 Bruno Haible <bruno@clisp.org>
+
+ * x-awk.h: Make includable without prerequisites.
+ * x-c.h: Likewise.
+ * x-csharp.h: Likewise.
+ * x-elisp.h: Likewise.
+ * x-glade.h: Likewise.
+ * x-java.h: Likewise.
+ * x-librep.h: Likewise.
+ * x-lisp.h: Likewise.
+ * x-perl.h: Likewise.
+ * x-php.h: Likewise.
+ * x-po.h: Likewise.
+ * x-properties.h: Likewise.
+ * x-python.h: Likewise.
+ * x-rst.h: Likewise.
+ * x-scheme.h: Likewise.
+ * x-sh.h: Likewise.
+ * x-smalltalk.h: Likewise.
+ * x-stringtable.h: Likewise.
+ * x-tcl.h: Likewise.
+ * x-ycp.h: Likewise.
+ * x-awk.c: Include x-awk.h.
+ * x-c.c: Include x-c.h.
+ * x-csharp.c: Include x-csharp.h.
+ * x-elisp.c: Include x-elisp.h.
+ * x-glade.c: Include x-glade.h.
+ * x-java.c: Include x-java.h.
+ * x-librep.c: Include x-librep.h.
+ * x-lisp.c: Include x-lisp.h.
+ * x-perl.c: Include x-perl.h.
+ * x-php.c: Include x-php.h.
+ * x-po.c: Include x-po.h, x-properties.h, x-stringtable.h.
+ * x-python.c: Include x-python.h.
+ * x-rst.c: Include x-rst.h.
+ * x-scheme.c: Include x-scheme.h.
+ * x-sh.c: Include x-sh.h.
+ * x-smalltalk.c: Include x-smalltalk.h.
+ * x-tcl.c: Include x-tcl.h.
+ * x-ycp.c: Include x-ycp.h.
+ * xgettext.c: Remove extern "C" around x-*.h includes.
+
+2006-10-29 Bruno Haible <bruno@clisp.org>
+
+ * format-awk.c (format_parse): Fix bug with unnumbered argument in
+ precision field.
+
+2006-11-27 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.16.1 released.
+
+2006-10-26 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.16 released.
+
+2006-10-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (AM_CPPFLAGS): Add also $(top_srcdir). Needed so that
+ woe32dll/export.h is found while compiling gettextsrc-exports.c in a
+ VPATH build.
+ Reported by Charles Wilson <cygwin@cwilson.fastmail.fm>.
+
+2006-10-24 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (struct definitions_ty): Remove semicolon after
+ gl_lock_define invocation.
+
+2006-10-21 Bruno Haible <bruno@clisp.org>
+
+ * po-error.h (po_error, po_error_at_line): Use format attribute only
+ with gcc >= 3.1.
+
+2006-10-21 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (struct definitions_ty): Use gl_lock_define to declare
+ the findex_init_lock field.
+ (definitions_init): Use gl_lock_init to initialize it, instead of
+ memcpy hack.
+
+2006-10-16 Bruno Haible <bruno@clisp.org>
+
+ Work around automake-1.10 problem described in
+ http://lists.gnu.org/archive/html/bug-automake/2006-08/msg00025.html.
+ * Makefile.am (CXXLINK): Remove variable.
+ (msgmerge_CXXFLAGS): New variable.
+ (libgettextsrc_la_LINK, msgattrib_LINK, msgcat_LINK, msgcomm_LINK,
+ msgconv_LINK, msgen_LINK, msgfilter_LINK, msggrep_LINK, msgmerge_LINK,
+ msguniq_LINK, xgettext_LINK): New variables.
+
+2006-10-18 Bruno Haible <bruno@clisp.org>
+
+ * read-po.h (input_format_po): Mark as DLL_VARIABLE.
+ * read-properties.h (input_format_properties): Likewise.
+ * read-stringtable.h (input_format_stringtable): Likewise.
+ * write-po.h (output_format_po): Likewise.
+ * write-properties.h (output_format_properties): Likewise.
+ * write-stringtable.h (output_format_stringtable): Likewise.
+
+2006-10-16 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h.in: Move to ../libgettextpo/.
+ * gettext-po.c: Move to ../libgettextpo/.
+ * Makefile.am (lib_LTLIBRARIES): Remove libgettextpo.la.
+ (nodist_include_HEADERS): Remove variable.
+ (libgettextpo_la_SOURCES, LTV_CURRENT, LTV_REVISION, LTV_AGE,
+ libgettextpo_la_LDFLAGS, libgettextpo_la_DEPENDENCIES,
+ libgettextpo_la_LDFLAGS): Remove variables.
+ (EXTRA_DIST, BUILT_SOURCES, CLEANFILES): Remove gettext-po.h support.
+ (gettext-po.h): Remove rule.
+
+2006-10-18 Bruno Haible <bruno@clisp.org>
+
+ * po-error.h (__attribute__, __format__, __printf__): New macros.
+ (po_error, po_error_at_line): Declare format string argument.
+ * po-lex.h (__attribute__, __format__, __printf__): New macros.
+ (po_gram_error, po_gram_error_at_line): Declare format string argument.
+
+ * msgl-cat.c (catenate_msgdomain_list): Avoid "gcc -Wshadow" warning.
+ * msgl-check.c (formatstring_error_logger): Avoid "gcc
+ -Wmissing-format-attribute" warning.
+ (check_pair): Avoid "gcc -Wshadow" warning.
+
+ * read-stringtable.c (stringtable_parse): Pass a mutable string as
+ msgstr to po_callback_message.
+ * write-po.c (msgdomain_list_print_po): Avoid "gcc -Wwrite-strings"
+ warning.
+ * xgettext.c (remember_a_message_plural): Free a temporary string
+ after use.
+
+2006-10-17 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Add the most important languages that
+ have an ISO 639-2 code.
+ * msginit.c (catalogname_for_locale): Likewise.
+
+2006-10-15 Bruno Haible <bruno@clisp.org>
+
+ * read-catalog-abstract.h (input_syntax_ty): Remove type.
+ (catalog_input_format): New structure type.
+ (catalog_input_format_ty): New type.
+ (catalog_reader_parse): Change type of input_syntax argument.
+ * read-catalog.h (input_syntax): Remove variable.
+ (read_catalog_stream, read_catalog_file): Add input_syntax argument.
+ * msgl-cat.h: Include read-catalog-abstract.h.
+ (catenate_msgdomain_list): Add input_syntax argument.
+ * read-po.h: New file.
+ * read-po.c: New file, extracted from read-catalog-abstract.c.
+ * msgattrib.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file calls.
+ * msgcat.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update catenate_msgdomain_list call.
+ * msgcmp.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update compare call.
+ (compare): Add input_syntax argument.
+ * msgcomm.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update catenate_msgdomain_list call.
+ * msgconv.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msgen.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msgexec.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msgfilter.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msgfmt.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file_msgfmt call.
+ (read_catalog_file_msgfmt): Add input_syntax argument.
+ * msggrep.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msginit.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update read_catalog_file call.
+ * msgl-cat.c (catenate_msgdomain_list): Add input_syntax argument.
+ * msgmerge.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update merge call.
+ (compendium): Read it in PO syntax.
+ (merge): Add input_syntax argument.
+ * msguniq.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (main): Change handling of --properties-input, --stringtable-input
+ options. Update catenate_msgdomain_list call.
+ * read-catalog-abstract.c: Don't include po-gram.h, read-properties.h,
+ read-stringtable.h.
+ (catalog_reader_parse): Change type of input_syntax argument. Invoke
+ the parser indirectly.
+ * read-catalog.c (input_syntax): Remove variable.
+ (read_catalog_stream, read_catalog_file): Add input_syntax argument.
+ * read-csharp.c: Include read-po.h.
+ (execute_and_read_po_output): Update read_catalog_stream call.
+ * read-java.c: Include read-po.h.
+ (execute_and_read_po_output): Update read_catalog_stream call.
+ * read-properties.c (properties_parse): Make static.
+ (input_format_properties): New variable.
+ * read-properties.h (properties_parse): Remove declaration.
+ (input_format_properties): New declaration.
+ * read-resources.c: Include read-po.h.
+ (execute_and_read_po_output): Update read_catalog_stream call.
+ * read-stringtable.c (stringtable_parse): Make static.
+ (input_format_stringtable): New variable.
+ * read-stringtable.h (stringtable_parse): Remove declaration.
+ (input_format_stringtable): New declaration.
+ * read-tcl.c: Include read-po.h.
+ (msgdomain_read_tcl): Update read_catalog_stream call.
+ * x-po.c: Include read-po.h, read-properties.h, read-stringtable.h.
+ (extract): Change type of input_syntax argument.
+ (extract_po, extract_properties, extract_stringtable): Update.
+ * xgettext.c: Include read-po.h.
+ (read_exclusion_file): Read it in PO syntax.
+ * gettext-po.c: Include read-po.h.
+ (po_file_read_v3, po_file_read_v2, po_file_read): Update
+ read_catalog_stream call.
+ * Makefile.am (noinst_HEADERS): Add read-po.h.
+ (COMMON_SOURCE): Add read-po.c.
+
+2006-10-15 Bruno Haible <bruno@clisp.org>
+
+ * read-catalog-abstract.h (abstract_catalog_reader_ty): Renamed from
+ abstract_po_reader_ty.
+ (abstract_catalog_reader_class_ty): Renamed from
+ abstract_po_reader_class_ty. Update.
+ (ABSTRACT_CATALOG_READER_TY): Renamed from ABSTRACT_PO_READER_TY.
+ Update.
+ (abstract_catalog_reader_ty): Renamed from abstract_po_reader_ty.
+ Update.
+ (catalog_reader_alloc): Renamed from po_reader_alloc.
+ (catalog_reader_parse): Renamed from po_scan.
+ (catalog_reader_free): Renamed from po_reader_free.
+ * read-catalog.h: Update.
+ (default_catalog_reader_class_ty): Renamed from
+ default_po_reader_class_ty.
+ (DEFAULT_CATALOG_READER_TY): Renamed from DEFAULT_PO_READER_TY. Update.
+ (default_catalog_reader_ty): Renamed from default_po_reader_ty. Update.
+ (default_constructor, default_destructor, default_parse_brief,
+ default_parse_debrief, default_directive_domain,
+ default_directive_message, default_comment, default_comment_dot,
+ default_comment_filepos, default_comment_special, default_set_domain,
+ default_add_message): Update.
+ (default_catalog_reader_alloc): Renamed from default_po_reader_alloc.
+ Update.
+ (read_catalog_stream): Renamed from read_po.
+ (read_catalog_file): Renamed from read_po_file.
+ * open-catalog.h (open_catalog_file): Renamed from open_po_file.
+ * msgattrib.c: Update.
+ (main): Update.
+ * msgcat.c: Update.
+ * msgcmp.c: Update.
+ (compare): Update.
+ * msgcomm.c: Update.
+ * msgconv.c: Update.
+ (main): Update.
+ * msgen.c: Update.
+ (main): Update.
+ * msgexec.c: Update.
+ (main): Update.
+ * msgfilter.c: Update.
+ (main): Update.
+ * msgfmt.c: Update.
+ (read_catalog_file_msgfmt): Renamed from read_po_file_msgfmt. Update.
+ (main): Update.
+ (msgfmt_catalog_reader_ty): Renamed from msgfmt_po_reader_ty. Update.
+ (msgfmt_constructor, msgfmt_parse_debrief, msgfmt_set_domain,
+ msgfmt_add_message, msgfmt_frob_new_message, msgfmt_comment_special,
+ msgfmt_methods): Update.
+ (read_catalog_file_msgfmt): Renamed from read_po_file_msgfmt. Update.
+ * msggrep.c: Update.
+ (main): Update.
+ * msginit.c: Update.
+ (main): Update.
+ * msgl-cat.c: Update.
+ (catenate_msgdomain_list): Update.
+ * msgmerge.c: Update.
+ (compendium, merge): Update.
+ * msguniq.c: Update.
+ * open-catalog.c: Update.
+ (try_open_catalog_file): Renamed from try_open_po_file.
+ (open_catalog_file): Renamed from open_po_file. Update.
+ * po-gram-gen.y: Update.
+ * read-catalog-abstract.c: Update.
+ (callback_arg): Update.
+ (catalog_reader_alloc): Renamed from po_reader_alloc.
+ (catalog_reader_free): Renamed from po_reader_free.
+ (call_parse_brief, call_parse_debrief, call_directive_domain,
+ call_directive_message, call_comment, call_comment_dot,
+ call_comment_filepos, call_comment_special): Update.
+ (parse_start): Renamed from po_scan_start.
+ (parse_end): Renamed from po_scan_end.
+ (catalog_reader_parse): Renamed from po_scan.
+ * read-catalog.c: Update.
+ (call_set_domain, call_add_message, call_frob_new_message): Update.
+ (default_constructor, default_destructor, default_parse_brief,
+ default_parse_debrief, default_copy_comment_state,
+ default_reset_comment_state, default_directive_domain,
+ default_directive_message, default_comment, default_comment_dot,
+ default_comment_filepos, default_comment_special, default_set_domain,
+ default_add_message, default_methods): Update.
+ (default_catalog_reader_alloc): Renamed from default_po_reader_alloc.
+ (read_catalog_stream): Renamed from read_po. Update.
+ (read_catalog_file): Renamed from read_po_file. Update.
+ * read-csharp.c: Update.
+ (execute_and_read_po_output): Update.
+ * read-java.c: Update.
+ (execute_and_read_po_output): Update.
+ * read-properties.c: Update.
+ (properties_parse): Update.
+ * read-properties.h: Update.
+ (properties_parse): Update.
+ * read-resources.c: Update.
+ (execute_and_read_po_output): Update.
+ * read-stringtable.c: Update.
+ (stringtable_parse): Update.
+ * read-stringtable.h: Update.
+ (stringtable_parse): Update.
+ * read-tcl.c: Update.
+ (msgdomain_read_tcl): Update.
+ * x-po.c: Update.
+ (extract_add_message, extract_methods, extract): Update.
+ * xgettext.c: Update.
+ (exclude_directive_domain, exclude_directive_message, exclude_methods,
+ read_exclusion_file): Update.
+ * xgettext.h: Update.
+ * gettext-po.c: Update.
+ (po_file_read_v3, po_file_read_v2, po_file_read): Update.
+
+ * read-catalog-abstract.h: Renamed from read-po-abstract.h.
+ * read-catalog-abstract.c: Renamed from read-po-abstract.c.
+ * read-catalog.h: Renamed from read-po.h.
+ * read-catalog.c: Renamed from read-po.c.
+ * open-catalog.h: Renamed from open-po.h.
+ * open-catalog.c: Renamed from open-po.c.
+ * Makefile.am (noinst_HEADERS, COMMON_SOURCE, libgettextsrc_la_SOURCES):
+ Update.
+
+2006-10-15 Bruno Haible <bruno@clisp.org>
+
+ * write-catalog.h: New file.
+ * write-catalog.c: New file, mostly extracted from write-po.c.
+ * write-po.h (message_page_width_set): Move declaration to
+ write-catalog.h.
+ (message_print_syntax_properties, message_print_syntax_stringtable):
+ Remove declarations.
+ (msgdomain_list_print): Move out to write-catalog.h, adding a
+ catalog_output_format_ty argument.
+ (msgdomain_list_sort_by_msgid, msgdomain_list_sort_by_filepos): Move
+ out to write-catalog.h.
+ (output_format_po): New declaration.
+ * write-po.c: Don't include fwriteerror.h, error-progname.h.
+ (page_width, message_page_width_set): Move out to write-catalog.c.
+ (use_syntax_properties): Remove variable.
+ (message_print_syntax_properties): Remove function.
+ (use_syntax_stringtable): Remove variable.
+ (message_print_syntax_stringtable): Remove function.
+ (wrap, message_print, message_print_obsolete, msgdomain_list_print_po):
+ Add page_width argument.
+ (msgdomain_list_print): Move out to write-catalog.c, adding a
+ catalog_output_format_ty argument.
+ (cmp_by_msgid, msgdomain_list_sort_by_msgid,
+ cmp_filepos, msgdomain_list_sort_filepos,
+ cmp_by_filepos, msgdomain_list_sort_by_filepos): Move out to
+ write-catalog.c.
+ (output_format_po): New variable.
+ * write-properties.h (msgdomain_list_print_properties): Remove
+ declaration.
+ (output_format_properties): New declaration.
+ * write-properties.c (msgdomain_list_print_properties): Make static.
+ (output_format_properties): New variable.
+ * write-stringtable.h (msgdomain_list_print_stringtable): Remove
+ declaration.
+ (output_format_stringtable): New declaration.
+ * write-stringtable.c (msgdomain_list_print_stringtable): Make static.
+ (output_format_stringtable): New variable.
+ * msgattrib.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgcat.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgcomm.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgconv.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgen.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgfilter.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msggrep.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msginit.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgmerge.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msgunfmt.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * msguniq.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (main): Change handling of --properties-output, --stringtable-output
+ options. Pass the output_syntax to msgdomain_list_print.
+ * xgettext.c: Include write-catalog.h, write-properties.h,
+ write-stringtable.h.
+ (output_syntax): Change type to catalog_output_format_ty.
+ (main): Update. Pass the output_syntax to msgdomain_list_print.
+ (finalize_header): Update.
+ * gettext-po.c: Include write-catalog.h.
+ (po_file_write): Specify PO syntax.
+ * Makefile.am (noinst_HEADERS): Add write-catalog.h.
+ (libgettextsrc_la_SOURCES): Add write.catalog.c.
+
+2006-10-05 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.c (language_table): Add Adangme, Kashubian. Fix the
+ names of Western Frisian, Marshallese, Sinhala.
+ * msginit.c (catalogname_for_locale): Add entries for Kashubian,
+ Western Frisian, South Ndebele. Change entry for Serbian from sr_CS to
+ sr_RS.
+
+2006-10-06 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (msgdomain_write_java): Use fopen_temp, fwriteerror_temp
+ instead of fopen, fwriteerror.
+ * write-csharp.c (msgdomain_write_csharp): Likewise.
+
+2006-10-06 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (LTV_CURRENT, LTV_REVISION, LTV_AGE): Bump to 3:0:3.
+
+2006-10-03 Bruno Haible <bruno@clisp.org>
+
+ * message.h (struct message_ty): New fields prev_msgctxt, prev_msgid,
+ prev_msgid_plural.
+ * message.c (message_alloc): Initialize the prev_msg* fields.
+ (message_free): Free the prev_msg* fields.
+ (message_copy): Copy the prev_msg* fields.
+ * msgl-ascii.c (is_ascii_message): Consider also the prev_msg* fields.
+ * msgl-cat.c (catenate_msgdomain_list): Copy the prev_msg* fields if
+ a message is copied, not merged.
+ * msgl-equal.c (message_equal): Consider also the prev_msg* fields.
+ * msgl-iconv.c (convert_prev_msgid): New function.
+ (iconv_message_list): Call it.
+ iconvable_prev_msgid): New function.
+ (is_message_list_iconvable): Call it.
+
+ * po-lex.c (po_lex_previous): New variable.
+ (lex_start, lex_end): Reset it.
+ (keyword_p): Test it. New return values PREV_MSGID, PREV_MSGID_PLURAL,
+ PREV_MSGCTXT.
+ (po_gram_lex): Recognize #| and #~| syntax. New return value
+ PREV_STRING.
+ * po-gram-gen.y (do_callback_message): Add prev_msgctxt, prev_msgid,
+ prev_msgid_plural arguments.
+ (free_message_intro): New macro.
+ (PREV_MSGCTXT, PREV_MSGID, PREV_MSGID_PLURAL, PREV_STRING): New tokens.
+ (prev, message_intro): New structures.
+ (po_file): Renamed from msgfmt.
+ (message): Pass prev_* fields around. Call free_message_intro.
+ (message_intro, prev): New rules.
+ (msg_intro): Renamed from message_intro.
+ (prev_msg_intro, prev_msgid_pluralform, prev_string_list): New rules.
+ * read-po-abstract.h (struct abstract_po_reader_class_ty): Add
+ prev_msgctxt, prev_msgid, prev_msgid_plural arguments to the
+ 'directive_message' method.
+ (po_callback_message): Add prev_msgctxt, prev_msgid,
+ prev_msgid_plural arguments.
+ * read-po-abstract.c (call_directive_message, po_callback_message): Add
+ prev_msgctxt, prev_msgid, prev_msgid_plural arguments.
+ * read-po.h (struct default_po_reader_class_ty): Add prev_msgctxt,
+ prev_msgid, prev_msgid_plural arguments to the 'add_message' method.
+ (default_directive_message, default_add_message): Add prev_msgctxt,
+ prev_msgid, prev_msgid_plural arguments.
+ * read-po.c (call_add_message, default_directive_message,
+ default_add_message): Add prev_msgctxt, prev_msgid, prev_msgid_plural
+ arguments.
+ * read-properties.c (properties_parse): Update.
+ * read-stringtable.c (stringtable_parse): Update.
+ * xgettext.c (exclude_directive_message): Add prev_msgctxt, prev_msgid,
+ prev_msgid_plural arguments.
+ * x-po.c (extract_add_message): Add prev_msgctxt, prev_msgid,
+ prev_msgid_plural arguments.
+
+ * write-po.c (wrap): Add extra_indent argument.
+ (message_print, message_print_obsolete): Print the prev_msgctxt,
+ prev_msgid, prev_msgid_plural fields. Compute an extra_indent.
+
+ * msgmerge.c (keep_previous): New variable.
+ (long_options): Add --previous option.
+ (main): Handle it.
+ (usage): Document --previous option.
+ (message_merge): Add force_fuzzy argument. Set the new message's
+ prev_msg* fields.
+ (match_domain): Update.
+ * msgattrib.c (REMOVE_PREV): New enum item.
+ (long_options): Add --clear-previous option.
+ (main): Handle it.
+ (usage): Document --clear-previous option.
+ (process_message_list): Handle REMOVE_PREV.
+ * msgfmt.c (msgfmt_add_message): Add prev_msgctxt, prev_msgid,
+ prev_msgid_plural arguments.
+ * gettext-po.c (po_message_prev_msgctxt, po_message_set_prev_msgctxt,
+ po_message_prev_msgid, po_message_set_prev_msgid,
+ po_message_prev_msgid_plural, po_message_set_prev_msgid_plural): New
+ functions.
+
+2006-10-03 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (definitions_init): Fix initialization of fresh_lock.
+
+2006-10-03 Bruno Haible <bruno@clisp.org>
+
+ * msgcmp.c (include_fuzzies, include_untranslated): New variables.
+ (long_options): Add options --use-fuzzy, --use-untranslated.
+ (main): Handle them.
+ (usage): Document them.
+ (match_domain): Consider include_fuzzies and include_untranslated.
+
+2006-10-02 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h.in (LIBGETTEXTPO_VERSION): Bump version number.
+ * Makefile.am (LTV_CURRENT, LTV_REVISION, LTV_AGE): Bump to 2:1:2.
+
+2006-10-01 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (extract_balanced): Remove state argument.
+
+ * x-perl.c (extract_balanced): Remove unused variable prev_last_token.
+
+ Fix Perl parsing bug introduced between 0.14.6 and 0.15.
+ * x-perl.c (extract_balanced): Add eat_delim and comma_delim arguments.
+ Use recursion to handle the case of a keyword not followed by an
+ opening parenthesis. When a keyword is seen, set next_argparser
+ instead of modifying the argparser variable of the current recursion.
+ When seeing an opening parenthesis, distinguish the case of function
+ arguments and the case of a subexpression. Don't call collect_message
+ for unused argument positions.
+ Reported by Nicolas François <nicolas.francois@centraliens.net>.
+
+ * x-perl.c (extract_balanced): Remove unused variable.
+
+2006-09-06 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c: Include xstriconv.h instead of iconvstring.h.
+ (convert_string, convert_msgstr, iconvable_string, iconvable_msgstr):
+ Use xmem_cd_iconv instead of iconv_string.
+ * recode-sr-latin.c: Include xstriconv.h instead of iconvstring.h.
+ (process): Use xmem_cd_iconv instead of iconv_string.
+
+2006-09-04 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msginit_SOURCES): Add ../../gettext-runtime/intl/lock.c.
+ Reported by Aaron Williams <aaron_williams@net.com>.
+
+2006-08-28 Bruno Haible <bruno@clisp.org>
+
+ * x-scheme.c (read_object): Don't call arglist_parser_remember if
+ argparser is NULL.
+ * x-elisp.c (read_object): Likewise.
+ * x-librep.c (read_object): Likewise.
+ * x-lisp.c (read_object): Likewise.
+ Reported by Kevin Ryde <user42@zip.com.au>.
+
+2006-08-16 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Put Hungarian under nplurals=2.
+ Reported by Arpad Biro <biro_arpad@yahoo.com> and
+ Gabor Kelemen <kelemeng@gnome.hu>.
+
+2006-08-16 Bruno Haible <bruno@clisp.org>
+
+ * read-mo.c: Include stdbool.h, stdlib.h, format.h.
+ (read_mo_file): Mark messages with system-dependent segments as
+ "#, c-format" or "#, objc-format", as appropriate.
+ Reported by Egmont Koblinger <egmont@uhulinux.hu>.
+
+2006-08-07 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (remember_a_message): Use the position passed as argument
+ instead of a dummypos.
+ Reported by Paul Eggert <eggert@cs.ucla.edu>.
+
+2006-08-01 Bruno Haible <bruno@clisp.org>
+
+ Fix behaviour of msgmerge when the PO file and the compendium are
+ in different encodings.
+ * msgl-iconv.h (is_message_list_iconvable): New declaration.
+ * msgl-iconv.c (iconvable_string, iconvable_string_list,
+ iconvable_msgid, iconvable_msgstr, is_message_list_iconvable): New
+ functions.
+ * msgmerge.c: Include xallocsa.h.
+ (merge): In the case that the .pot file is ASCII, not UTF-8, convert
+ the definitions and the compendia contents to a common encoding.
+ Reported by Stanislav Brabec <sbrabec@suse.cz>.
+
+2006-07-30 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (AM_CPPFLAGS, LDADD, libgettextsrc_la_LDFLAGS,
+ libgettextpo_la_LDFLAGS, recode_sr_latin_LDADD, RELOCATABLE_SRC_DIR,
+ RELOCATABLE_BUILD_DIR): Replace ../lib with ../gnulib-lib.
+ (JAVACOMP, CSHARPCOMP): Remove lib/ from pathname.
+
+2006-07-30 Bruno Haible <bruno@clisp.org>
+
+ * msgl-cat.c: Include c-strstr.h instead of strstr.h.
+ (catenate_msgdomain_list): Use c_strstr instead of strstr.
+ * msgl-charset.c: Include c-strstr.h instead of strstr.h.
+ (compare_po_locale_charsets): Use c_strstr instead of strstr.
+ * msgl-check.c: Include c-strstr.h instead of strstr.h.
+ (plural_help, check_plural, check_header_entry): Use c_strstr instead
+ of strstr.
+ * msgl-iconv.c: Include c-strstr.h instead of strstr.h.
+ (iconv_message_list): Use c_strstr instead of strstr.
+ * po-charset.c: Include c-strstr.h instead of strstr.h.
+ (po_lex_charset_set): Use c_strstr instead of strstr.
+ * write-po.c: Include c-strstr.h instead of strstr.h.
+ (msgdomain_list_print_po): Use c_strstr instead of strstr.
+ * write-stringtable.c: Include c-strstr.h instead of strstr.h.
+ (write_message): Use c_strstr instead of strstr.
+ * msgcmp.c: Include c-strstr.h instead of strstr.h.
+ (compare): Use c_strstr instead of strstr.
+ * msginit.c: Include c-strstr.h instead of strstr.h.
+ (content_type): Use c_strstr instead of strstr.
+ * msgmerge.c: Include c-strstr.h instead of strstr.h.
+ (message_merge, merge): Use c_strstr instead of strstr.
+ * x-python.c: Include c-strstr.h instead of strstr.h.
+ (try_to_extract_coding): Use c_strstr instead of strstr.
+ * xgettext.c: Include c-strstr.h instead of strstr.h.
+ (remember_a_message, finalize_header): Use c_strstr instead of strstr.
+
+2006-07-23 Bruno Haible <bruno@clisp.org>
+
+ Exploit CPUs with multiple execution units.
+ * msgmerge.c: Include lock.h.
+ (struct definitions_ty): Add 'findex_init_lock' field.
+ (definitions_init): Initialize it.
+ (definitions_init_findex): Ensure findex is initialized by the
+ first thread who attempts so.
+ (match_domain): Split the main loop into two. Parallelize the first
+ loop using OpenMP pragmas.
+ * Makefile.am (msgmerge_CFLAGS): New variable.
+ (msgmerge_LDADD): Add OPENMP_CFLAGS.
+
+2006-07-25 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc: Remove file.
+ * Makefile.am (EXTRA_DIST): Remove Makefile.msvc.
+
+2006-07-25 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.vms: Remove file.
+ * Makefile.am (EXTRA_DIST): Remove Makefile.vms.
+
+2006-07-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (include_HEADERS): Remove variable.
+ (nodist_include_HEADERS): New variable.
+ Suggested by Ralf Corsepius <rc040203@freenet.de>.
+
+2006-07-21 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.15 released.
+
+2006-07-20 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h.in (LIBGETTEXTPO_VERSION): Bump version number.
+
+2006-07-20 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (LTV_CURRENT, LTV_REVISION, LTV_AGE): Bump to 2:0:2.
+
+2006-07-14 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (MOSTLYCLEANFILES): New variable.
+
+2006-07-02 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c (msgdomain_write_csharp): Update.
+ * write-java.c (msgdomain_write_java): Update.
+
+2006-06-29 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c (msgdomain_write_csharp): Update.
+ * write-java.c (msgdomain_write_java): Likewise.
+
+2006-06-28 Bruno Haible <bruno@clisp.org>
+
+ Assume <dirent.h> on all Unix platforms. Assume closedir works.
+ * msginit.c: Don't include <sys/ndir.h>, <sys/dir.h>, <ndir.h>.
+ (find_pot): Use closedir directly.
+
+2006-06-27 Bruno Haible <bruno@clisp.org>
+
+ Assume correct S_ISDIR macro.
+ * write-csharp.c: Remove test of STAT_MACROS_BROKEN.
+ * write-java.c: Likewise.
+
+2006-04-14 Bruno Haible <bruno@clisp.org>
+
+ Assume autoconf >= 2.60.
+ * Makefile.am (localedir): Remove variable.
+
+2006-06-21 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.c (read_word): Recognize the Bash process substitution syntax.
+
+2006-06-21 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.c (read_word): Recognize $(...) and $((...)) also inside
+ double-quoted strings.
+ Reported by Michelle Konzack <linux4michelle@freenet.de>.
+
+2006-06-04 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.c (check_plural_eval): Declare 'distribution' as volatile,
+ so that it is unaffected by longjmp.
+
+2006-06-04 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (arglist_parser_done): Cast length argument for format
+ string.
+
+2006-05-31 Bruno Haible <bruno@clisp.org>
+
+ * x-ycp.c (phase5_pushback, phase5_pushback_length): New variables.
+ (phase5_get): Renamed from x_ycp_lex.
+ (phase5_unget): New function.
+ (phase8_get): New function.
+ (extract_parenthesized): Inside i18n construct, use phase8_get
+ instead of phase5_get.
+ Reported by Karl Eichwalder <ke@suse.de>.
+
+2006-05-22 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: Include <sys/select.h> also on Minix.
+ Reported by Leonard den Ottolander <leonard@den.ottolander.nl>.
+
+2006-05-17 Bruno Haible <bruno@clisp.org>
+
+ Cygwin portability.
+ * hostname.c (WIN32_NATIVE): Renamed from WIN32.
+
+2006-05-16 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (CLEANFILES): Add gettext-po.h.
+
+2006-05-16 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (main): Use a size_t index variable for looping over the
+ files.
+ (arglist_parser_done): Fix a format string.
+
+2006-05-16 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c (main): Change type of i.
+
+2006-05-15 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am: Test flag WOE32DLL instead of CYGWINDLL.
+
+2006-05-14 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (x_php_lex): Fix handling of here documents.
+ Reported by Gaëtan Frenoy <gaetan@frenoy.net>.
+
+2006-05-12 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c [MINGW]: Include <io.h>.
+ (mkdir): Define using _mkdir.
+ * write-java.c [MINGW]: Include <io.h>.
+ (mkdir): Define using _mkdir.
+
+2006-05-11 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h.in: Renamed from gettext-po.h.
+ * Makefile.am: Treat condition WOE32DLL like MINGW. Update file names.
+ (libgettextsrc_la_SOURCES, libgettextsrc_la_LDFLAGS): Modify for
+ Cygwin.
+ libgettextpo_la_SOURCES, libgettextpo_la_LDFLAGS): Likewise.
+ (BUILT_SOURCES): Add gettext-po.h.
+ (EXTRA_DIST): Add gettext-po.h.in.
+ (gettext-po.h): New rule.
+
+2006-05-11 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (format_CFLAGS): Remove variable.
+ (format.lo): Remove variable.
+ (FORMAT_SOURCE) [MINGW]: Use ../mingw/c++format.cc instead of format.c.
+
+2006-05-09 Bruno Haible <bruno@clisp.org>
+
+ * plural-eval.h (USE_SIGINFO): Don't define on Cygwin.
+
+2006-05-02 Charles Wilson <cygwin@cwilson.fastmail.fm>
+
+ * write-mo.h: Remove DLL_VARIABLE declarations, not needed since
+ write-mo.c is not compiled into a shared library.
+ * lang-table.h: Likewise.
+
+2006-05-02 Charles Wilson <cygwin@cwilson.fastmail.fm>
+
+ * filters.h: Add C++ guards.
+
+2006-05-07 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (extract_variable): Fix syntax error introduced on
+ 2005-10-03.
+
+2006-05-07 Bruno Haible <bruno@clisp.org>
+
+ * format-awk.c: Include xvasprintf.h instead of xerror.h.
+ * format-elisp.c: Likewise.
+ * format-librep.c: Likewise.
+ * format-pascal.c: Likewise.
+ * format-php.c: Likewise.
+ * format-tcl.c: Likewise.
+
+2006-04-30 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (msgdomain_write_java): Specify source-version 1.3 and
+ require target-version 1.1.
+
+2006-04-30 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (main): Simplify by use of xasprintf().
+ * write-java.c: Include xvasprintf.h.
+ (msgdomain_write_java): Simplify by use of xasprintf().
+ * x-c.c: Include xvasprintf.h.
+ (phase8a_get): Simplify by use of xasprintf().
+ * xgettext.c: Don't include stpcpy.h.
+ (remember_a_message, remember_a_message_plural): Simplify by use of
+ xasprintf().
+
+2006-04-30 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c: Include xvasprintf.h.
+ * msginit.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-charset.c: Likewise.
+ * po-xerror.c: Likewise.
+ * x-csharp.c: Likewise.
+ * x-glade.c: Likewise.
+ * x-python.c: Likewise.
+ * xgettext.c: Likewise.
+ * format-boost.c: Include xvasprintf.h instead of xerror.h.
+ * format-c.c: Likewise.
+ * format-csharp.c: Likewise.
+ * format-gcc-internal.c: Likewise.
+ * format-java.c: Likewise.
+ * format-lisp.c: Likewise.
+ * format-perl.c: Likewise.
+ * format-python.c: Likewise.
+ * format-qt.c: Likewise.
+ * format-scheme.c: Likewise.
+ * format-ycp.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgl-check.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * open-po.c: Likewise.
+ * po-charset.c: Likewise.
+ * po-lex.c: Likewise.
+ * po-time.c: Likewise.
+ * read-po-abstract.c: Likewise.
+ * read-properties.c: Likewise.
+ * read-stringtable.c: Likewise.
+ * write-po.c: Likewise.
+
+2006-04-30 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (project_id, project_id_version, get_user_email): Close
+ the FILE in case of I/O error.
+
+2006-04-30 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (msgdomain_write_java): Don't fclose the FILE after
+ fwriteerror signalled a failure.
+ * write-csharp.c (msgdomain_write_csharp): Likewise.
+
+2006-04-23 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c: Include <unistd.h> unconditionally.
+ * msgfilter.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * hostname.c: Likewise.
+ * urlget.c: Likewise.
+
+2006-04-17 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am: Use $(mkdir_p) instead of $(mkinstalldirs).
+
+2006-04-09 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c: Include clean-temp.h. Don't include unistd.h,
+ mkdtemp.h, tmpdir.h, pathmax.h, fatal-signal.h, xallocsa.h.
+ (cleanup_list): Remove variable.
+ (cleanup): Remove function.
+ (msgdomain_write_csharp): Simplify handling of temporary directory.
+ * write-java.c: Include clean-temp.h. Don't include unistd.h,
+ mkdtemp.h, tmpdir.h, pathmax.h, fatal-signal.h.
+ (cleanup_list): Remove variable.
+ (cleanup): Remove function.
+ (msgdomain_write_java): Simplify handling of temporary directory.
+
+2006-04-03 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.c (p_XML_GetCurrentLineNumber, p_XML_GetCurrentColumnNumber):
+ Declare differently for expat >= 2.0.0.
+ (load_libexpat): Search for a differently library name for expat >=
+ 2.0.0.
+ (do_extract_glade): Update.
+ Reported by Mike Frysinger <vapier@gentoo.org>.
+
+2006-04-02 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c: Include propername.h.
+ (main): Call proper_name.
+ * msgattrib.c: Include propername.h.
+ (main): Call proper_name.
+ * msgcat.c: Include propername.h.
+ (main): Call proper_name.
+ * msgcmp.c: Include propername.h.
+ (main): Call proper_name.
+ * msgcomm.c: Include propername.h.
+ (main): Call proper_name.
+ * msgconv.c: Include propername.h.
+ (main): Call proper_name.
+ * msgen.c: Include propername.h.
+ (main): Call proper_name.
+ * msgexec.c: Include propername.h.
+ (main): Call proper_name.
+ * msgfilter.c: Include propername.h.
+ (main): Call proper_name.
+ * msgfmt.c: Include propername.h.
+ (main): Call proper_name.
+ * msggrep.c: Include propername.h.
+ (main): Call proper_name.
+ * msginit.c: Include propername.h.
+ (main): Call proper_name.
+ * msgmerge.c: Include propername.h.
+ (main): Call proper_name.
+ * msgunfmt.c: Include propername.h.
+ (main): Call proper_name.
+ * msguniq.c: Include propername.h.
+ (main): Call proper_name.
+ * recode-sr-latin.c: Include propername.h.
+ (main): Call proper_name and proper_name_utf8.
+ * urlget.c: Include propername.h.
+ (main): Call proper_name.
+ * xgettext.c: Include propername.h.
+ (main): Call proper_name.
+
+2006-04-01 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (main): Treat --keyword= like --keyword.
+
+2006-03-28 Bruno Haible <bruno@clisp.org>
+
+ * filters.h: New file.
+ * filter-sr-latin.c: New file, based on code from Danilo Šegan.
+ * recode-sr-latin.c: New file.
+ * msgfilter.c: Include filters.h, msgl-iconv.h, po-charset.h.
+ (filter): New variable.
+ (main): Recognize recode-sr-latin as built-in.
+ (generic_filter): New function, extracted from process_string.
+ (process_string): Call the filter.
+ * Makefile.am (bin_PROGRAMS): Add recode-sr-latin.
+ (noinst_HEADERS): Add filters.h.
+ (msgfilter_SOURCES): Add filter-sr-latin.c.
+ (recode_sr_latin_SOURCES, recode_sr_latin_LDADD,
+ recode_sr_latin_CPPFLAGS, recode_sr_latin_LDFLAGS): New variables.
+ * Makefile.msvc (PROGRAMS): Add recode-sr-latin.exe.
+ (msgfilter_OBJECTS): Add filter-sr-latin.obj.
+ (recode_sr_latin_OBJECTS): New variable.
+ (filter-sr-latin.obj, recode-sr-latin.obj): New rules.
+ (recode-sr-latin.exe): New rule.
+ (install): Also install recode-sr-latin.exe.
+ (uninstall): Also uninstall recode-sr-latin.exe.
+ * Makefile.vms (PROGRAMS): Add recode-sr-latin.exe.
+ (msgfilter_OBJECTS): Add filter-sr-latin.obj.
+ (recode_sr_latin_OBJECTS): New variable.
+ (filter-sr-latin.obj, recode-sr-latin.obj): New rules.
+ (recode-sr-latin.exe): New rule.
+ (install): Also install recode-sr-latin.exe.
+ (uninstall): Also uninstall recode-sr-latin.exe.
+
+2006-03-28 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c: Include iconvstring.h. Don't include <errno.h>.
+ (iconv_string): Remove function, moved to ../lib/.
+
+2006-03-19 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h (struct callshape): New field 'xcomments'.
+ (struct partial_call): New field 'xcomments'.
+ * xgettext.c (split_keywordspec): Accept the ID:ARGNUM...,"XCOMMENT"
+ syntax.
+ (insert_keyword_callshape): Store the xcomments from the new shape.
+ (arglist_parser_alloc, arglist_parser_clone): Update.
+ (arglist_parser_done): After calling remember_a_message, attach the
+ specified extracted comments.
+ * x-perl.c (extract_variable): Update.
+ Suggested by Behdad Esfahbod <behdad@cs.toronto.edu>.
+
+2006-03-19 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (arglist_parser_done): Use error_at_line instead of error.
+
+2006-03-16 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (grep_task): Increment size.
+ (long_options): Add --extracted-comment option.
+ (main): Handle --extracted-comment option.
+ (no_pass, usage): Update.
+ (is_message_selected_no_invert): Implement matching on extracted
+ comments.
+ Suggested by Behdad Esfahbod <behdad@cs.toronto.edu>.
+
+2006-03-16 Bruno Haible <bruno@clisp.org>
+
+ Handle GNOME glib context syntax.
+ * xgettext.h (struct callshape): Add fields argnum1_glib_context,
+ argnum2_glib_context.
+ (struct partial_call): Add fields argnum1_glib_context,
+ argnum2_glib_context.
+ * xgettext.c (split_keywordspec): Recognize suffix 'g' and set the
+ argnum1_glib_context, argnum2_glib_context fields.
+ (insert_keyword_callshape, arglist_parser_alloc, arglist_parser_clone):
+ Update.
+ (arglist_parser_done): When suffix 'g' was specified, split off the
+ context from the msgid and/or msgid_plural.
+ * x-perl.c (extract_variable): Update.
+ Suggested by Danilo Šegan <danilo@gnome.org>.
+
+2006-03-16 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (set_format_flags_from_context): Break long line.
+
+2006-03-11 Bruno Haible <bruno@clisp.org>
+
+ * message.c (fuzzy_search_goal_function): Use 'volatile double'.
+
+2006-03-11 Bruno Haible <bruno@clisp.org>
+
+ Speed up msgmerge with large compendia.
+ * message.h (message_list_free): Add keep_messages argument.
+ (message_list_list_free): Add keep_level argument.
+ (fuzzy_search_goal_function): New declaration.
+ (FUZZY_THRESHOLD): New macro.
+ * message.c (message_list_free): Add keep_messages argument.
+ (fuzzy_search_goal_function): New function, extracted from
+ message_list_search_fuzzy_inner.
+ (message_list_search_fuzzy_inner): Use it.
+ (message_list_search_fuzzy): Use symbolic value FUZZY_THRESHOLD.
+ (message_list_list_free): Comment in. Add keep_level argument.
+ (message_list_list_search_fuzzy): Comment out. Use symbolic value
+ FUZZY_THRESHOLD.
+ (msgdomain_free): Update.
+ (msgdomain_list_search_fuzzy): Use symbolic value FUZZY_THRESHOLD.
+ * po-charset.h: Include stddef.h.
+ (character_iterator_t): New type.
+ (po_charset_character_iterator): New declaration.
+ * po-charset.c (char_iterator, euc_character_iterator,
+ euc_jp_character_iterator, euc_tw_character_iterator,
+ big5_character_iterator, big5hkscs_character_iterator,
+ gbk_character_iterator, gb18030_character_iterator,
+ shift_jis_character_iterator, johab_character_iterator,
+ utf8_character_iterator, po_charset_character_iterator): New functions.
+ * msgl-fsearch.h: New file.
+ * msgl-fsearch.c: New file.
+ * msgmerge.c: Include po-charset.h, msgl-fsearch.h.
+ (compendium_filenames): New variable.
+ (compendium): Also put the filename into compendium_filenames.
+ (definitions_ty): New structure type.
+ (definitions_init, definitions_init_findex, definitions_current_list,
+ definitions_set_current_list, definitions_search,
+ definitions_search_fuzzy, definitions_destroy): New functions.
+ (match_domain): Change type of 'definitions' argument.
+ (merge): Also convert the compendium to UTF-8. Use definitions_init,
+ definitions_set_current_list, definitions_destroy.
+ * msgfmt.c (main): Update.
+ * Makefile.am (noinst_HEADERS): Add msgl-fsearch.h.
+ (msgmerge_SOURCES): Add msgl-fsearch.c.
+ (msgmerge_LDADD): Link with MSGMERGE_LIBM.
+ * Makefile.msvc (msgmerge_OBJECTS): Add msgl-fsearch.obj.
+ (msgl-fsearch.obj): New rule.
+ * Makefile.vms (msgmerge_OBJECTS): Add msgl-fsearch.obj.
+ (msgl-fsearch.obj): New rule.
+ Reported by Clytie Siddall <clytie@riverland.net.au>.
+
+2006-03-09 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (CXXLINK) [!mingw]: Overwrite automake's value. Fixes
+ unintended dependency on libstdc++ introduced on 2005-07-05.
+
+2005-10-09 Bruno Haible <bruno@clisp.org>
+
+ * lang-table.h: New file.
+ * lang-table.c: New file, extracted from msginit.c.
+ * msginit.c: Include lang-table.h.
+ (englishname_of_language): Use language_table.
+ * Makefile.am (noinst_HEADERS): Add lang-table.h.
+ (msginit_SOURCES): Add lang-table.c.
+ * Makefile.msvc (MSGINIT_OBJECTS): Add lang-table.obj.
+ (lang-table.obj): New rule.
+ * Makefile.vms (MSGINIT_OBJECTS): Add lang-table.obj.
+ (lang-table.obj): New rule.
+
+2006-02-12 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_boost'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_boost entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_boost): New declaration.
+ * format-boost.c: New file, based on format-c.c.
+ * format.c (formatstring_parsers): Add formatstring_boost.
+ * x-c.c (init_flag_table_c): Also register flags for boost-format.
+ * xgettext.c (flag_table_cxx_qt, flag_table_cxx_boost): New variables.
+ (recognize_format_boost): New variable.
+ (main): Handle --boost option.
+ (usage): Document --boost option.
+ (xgettext_record_flag): Also fill flag_table_cxx_qt,
+ flag_table_cxx_boost.
+ (remember_a_message, remember_a_message_plural): In the heuristics,
+ don't mark a string as c-format that is already known to be a qt-format
+ or boost-format.
+ (language_to_extractor): Use a flag_table_cxx_qt or
+ flag_table_cxx_boost that is different from flag_table_c.
+ * Makefile.am (FORMAT_SOURCE): Add format-boost.c.
+ * Makefile.msvc (OBJECTS): Add format-boost.obj.
+ (format-boost.obj): New rule.
+ * Makefile.vms (OBJECTS): Add format-boost.obj.
+ (format-boost.obj): New rule.
+ * FILES: Update.
+
+2006-02-12 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (xgettext_record_flag): When asked to set "qt-format",
+ really set "qt-format", not "c-format". Fixes bug introduced on
+ 2003-10-19.
+
+2006-02-12 Bruno Haible <bruno@clisp.org>
+
+ Reduce the number of false positives produced by the c-format guessing
+ heuristics.
+ * format.h (struct formatstring_parser): Add 'is_unlikely_intentional'
+ field.
+ * format-awk.c (formatstring_awk): Update.
+ * format-c.c (struct spec): Add 'unlikely_unintentional' field.
+ (format_parse): Set unlikely_unintentional to true when encountering
+ a "%...%" directive other than "%%".
+ (format_is_unlikely_intentional): New function.
+ (formatstring_c, formatstring_objc): Use it.
+ * format-csharp.c (formatstring_csharp): Update.
+ * format-elisp.c (formatstring_elisp): Update.
+ * format-gcc-internal.c (formatstring_gcc_internal): Update.
+ * format-java.c (formatstring_java): Update.
+ * format-librep.c (formatstring_librep): Update.
+ * format-lisp.c (formatstring_lisp): Update.
+ * format-pascal.c (formatstring_pascal): Update.
+ * format-perl.c (formatstring_perl): Update.
+ * format-perl-brace.c (formatstring_perl_brace): Update.
+ * format-php.c (format_php): Update.
+ * format-python.c (formatstring_python): Update.
+ * format-qt.c (formatstring_qt): Update.
+ * format-scheme.c (formatstring_scheme): Update
+ * format-sh.c (formatstring_sh): Update.
+ * format-tcl.c (formatstring_tcl): Update.
+ * format-ycp.c (formatstring_ycp, formatstring_smalltalk): Update.
+ * xgettext.c (remember_a_message, remember_a_message_plural): Don't
+ mark the message as c-format if the validity as c-format looks
+ unintentional.
+
+2006-02-12 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2006-01-25 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (x_php_lex): Treat byte values >= 127 like alphabetic.
+ Reported by Hagen Fritsch <itooktheredpill@gmx.de>.
+
+2005-12-25 Bruno Haible <bruno@clisp.org>
+
+ Tell Python developers to use named arguments in format strings.
+ * format.h (get_python_format_unnamed_arg_count): New declaration.
+ * format-python.c (get_python_format_unnamed_arg_count): New function.
+ * xgettext.c (warn_format_string): New function.
+ (remember_a_message, remember_a_message_plural): Call it.
+ Suggested by Martin von Löwis.
+
+2005-12-07 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c (iconv_message_list): Fix syntax error.
+ Reported by Dave Patton <dpatton@confluence.org>.
+
+2005-12-04 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (CSHARPCOMPFLAGS): Use value set by csharpcomp.m4.
+
+2005-11-29 Colin Watson <cjwatson@ubuntu.com>
+
+ * msggrep.c (invert_match): New variable.
+ (long_options): Add --invert-match option.
+ (main): Handle --invert-match option.
+ (usage): Document --invert-match option.
+ (is_message_selected_no_invert): New function, extracted from
+ is_message_selected.
+ (is_message_selected): Call it. Handle match inversion.
+
+2005-11-01 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c (write_csharp_code): Add culture_name argument.
+ Emit an AssemblyCulture note for it.
+ (msgdomain_write_csharp): Update.
+ Suggested by Pepa <cerna.zelva@seznam.cz>.
+
+2005-10-09 Bruno Haible <bruno@clisp.org>
+
+ * plural-eval.h: New file.
+ * plural-eval.c: Include plural-eval.h.
+ (sigfpe_exit, sigfpe_code, sigfpe_handler, install_sigfpe_handler,
+ uninstall_sigfpe_handler): New definitions, moved here from
+ msgl-check.c.
+ * msgl-check.c: Include plural.eval.h.
+ (sigjmp_buf, sigsetjmp, siglongjmp, USE_SIGINFO): Move to plural-eval.h.
+ (sigfpe_exit, sigfpe_code, sigfpe_handler, install_sigfpe_handler,
+ uninstall_sigfpe_handler): Move to plural-eval.c.
+ * Makefile.am (noinst_HEADERS): Add plural-eval.h.
+
+2005-10-09 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.c: Include c-ctype.h instead of ctype.h.
+ (check_plural): Use c_isspace instead of isspace.
+ * x-librep.c: Include c-ctype.h instead of ctype.h.
+ (read_token): Use c_isxdigit instead of isxdigit.
+ * x-rst.c: Include c-ctype.h instead of ctype.h.
+ (extract_rst): Use c_isdigit instead of isdigit.
+ * x-ycp.c: Don't include ctype.h.
+
+2005-10-09 Bruno Haible <bruno@clisp.org>
+
+ * plural-exp.c: Renamed from plural.c.
+ * Makefile.am (libgettextsrc_la_SOURCES): Update.
+
+2005-10-18 Bruno Haible <bruno@clisp.org>
+
+ Disambiguate overloaded function calls according to argument count.
+ * xgettext.h (struct callshape): Add field 'argtotal'.
+ (struct partial_call): Likewise.
+ (arglist_parser_done): Add argnum argument.
+ * xgettext.c (split_keywordspec): Recognize NNt syntax and fill
+ argtotal.
+ (insert_keyword_callshape, arglist_parser_alloc, arglist_parser_clone):
+ Update.
+ (arglist_parser_decidedp): Compare also the argtotal if given.
+ (arglist_parser_done): Add argnum argument.
+ * x-awk.c (extract_parenthesized): Pass arg count to
+ arglist_parser_done.
+ * x-c.c (extract_parenthesized): Likewise.
+ * x-csharp.c (extract_parenthesized): Likewise.
+ * x-elisp.c (read_object): Likewise.
+ * x-java.c (extract_parenthesized): Likewise.
+ * x-librep.c (read_object): Likewise.
+ * x-lisp.c (read_object): Likewise.
+ * x-perl.c (extract_variable): Update.
+ (extract_balanced): Pass arg count to arglist_parser_done.
+ * x-php.c (extract_parenthesized): Likewise.
+ * x-python.c (extract_parenthesized): Likewise.
+ * x-scheme.c (read_object): Likewise.
+ * x-sh.c (read_command): Likewise.
+ * x-tcl.c (read_command): Likewise.
+ Stimulated by a patch from
+ Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>.
+
+2005-10-09 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale, language_of_locale): Drop support
+ of CEN locale name syntax.
+
+2005-10-05 Bruno Haible <bruno@clisp.org>
+
+ * format.h (check_msgid_msgstr_format): Add plural_distribution
+ argument.
+ * format.c (check_msgid_msgstr_format): Add plural_distribution
+ argument. Use it for index dependent strictness.
+ * msgl-check.h (check_message): Add plural_distribution argument.
+ * msgl-check.c: Include xalloc.h.
+ (check_plural_eval): Create a plural_distribution array as additional
+ output parameter.
+ (check_plural): Return plural_distribution array as additional output
+ parameter.
+ (check_pair, check_message): Add plural_distribution argument.
+ (check_message_list): Pass the plural_distribution from check_plural
+ to check_message.
+ * gettext-po.c (po_message_check_format): Update.
+
+2005-10-04 Bruno Haible <bruno@clisp.org>
+
+ Combine all msgfmt checks in a single place.
+ * format.h (check_msgid_msgstr_format): Change return type to int.
+ * format.c (check_msgid_msgstr_format): Return the number of errors.
+ * msgl-check.h (check_plural): Remove declaration.
+ * msgl-check.c (check_plural): Make static. Return the number of
+ errors.
+ (check_pair): Return the number of errors.
+ (check_message_list): Likewise.
+ * msgfmt.c (main): Perform the message checks here, after all files
+ were read. Call check_message_list instead of check_plural and
+ check_message. Emit statistics of fatal errors.
+ (msgfmt_frob_new_message): Don't call check_message here.
+
+2005-10-04 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.h (check_message_list): New declaration.
+ * msgl-check.c (check_message_list): New function.
+ * gettext-po.c (po_file_check_all, po_message_check_all): Call
+ check_message_list instead of check_message and check_plural.
+
+2005-10-04 Bruno Haible <bruno@clisp.org>
+
+ Fix handling of obsolete messages.
+ * msgl-check.c (check_plural): Ignore obsolete messages.
+ * gettext-po.c (po_file_check_all, po_message_check_all,
+ po_message_check_format): Likewise.
+
+2005-10-04 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.h (check_message): Remove msgstr_pos argument.
+ * msgl-check.c (check_pair, check_message): Remove msgstr_pos argument.
+ * msgfmt.c (msgfmt_frob_new_message): Update.
+ * gettext-po.c (po_file_check_all, po_message_check_all,
+ po_message_check_format): Update.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ Add support for contexts in xgettext.
+ * xgettext.h: Include stdbool.h.
+ (struct callshape): New structure type.
+ (split_keywordspec): Return a struct callshape, not two integers,
+ by reference.
+ (struct callshapes): New structure type.
+ (insert_keyword_callshape): New declaration.
+ (remember_a_message): Add msgctxt argument.
+ (struct partial_call): New structure type.
+ (struct arglist_parser): New structure type.
+ (arglist_parser_alloc, arglist_parser_clone, arglist_parser_remember,
+ arglist_parser_decidedp, arglist_parser_done): New declarations.
+ * xgettext.c (split_keywordspec): Return a struct callshape, not two
+ integers, by reference. Handle the notation Nc for a context argument.
+ (insert_keyword_callshape): New function.
+ (remember_a_message): Add msgctxt argument. Free both strings when
+ the message is excluded.
+ (arglist_parser_alloc, arglist_parser_clone, arglist_parser_remember,
+ arglist_parser_decidedp, arglist_parser_done): New functions.
+ * x-awk.c (x_awk_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_awk): Update.
+ * x-c.c (add_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (init_keywords): Register the new functions [d[c]][n]pgettext.
+ (init_flag_table_c, init_flag_table_objc): Likewise.
+ (struct xgettext_token_ty): Replace argnum1, argnum2 with a callshapes
+ pointer.
+ (x_c_lex): Update.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_whole_file): Update.
+ * x-csharp.c (x_csharp_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_csharp): Update.
+ * x-elisp.c (x_elisp_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_object): Use arglist_parser_* API.
+ * x-glade.c (start_element_handler, end_element_handler): Update.
+ * x-java.c (x_java_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_java): Update.
+ * x-librep.c (x_librep_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_object): Use arglist_parser_* API.
+ * x-lisp.c (x_lisp_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_object): Use arglist_parser_* API.
+ * x-perl.c (x_perl_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_balanced): Replace arg_sg, arg_pl arguments with arg,
+ argparser arguments. Use arglist_parser_* API.
+ (extract_variable, interpolate_keywords, extract_perl): Update.
+ * x-php.c (x_php_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_php): Update.
+ * x-python.c (x_python_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (extract_parenthesized): Replace commas_to_skip, plural_commas
+ arguments with a single argparser argument. Use arglist_parser_* API.
+ (extract_python): Update.
+ * x-rst.c (extract_rst): Update.
+ * x-scheme.c (x_scheme_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_object): Use arglist_parser_* API.
+ * x-sh.c (x_sh_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_word): Update.
+ (read_command): Use arglist_parser_* API.
+ * x-smalltalk.c (extract_smalltalk): Update.
+ * x-tcl.c (x_tcl_keyword): Use callshape API: split_keywordspec,
+ insert_keyword_callshape.
+ (read_command): Use arglist_parser_* API.
+ * x-ycp.c (extract_parenthesized): Update.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h (savable_comment_to_xgettext_comment): Remove
+ declaration.
+ (remember_a_message, remember_a_message_plural): Add a comment
+ argument.
+ * xgettext.c (savable_comment_to_xgettext_comment): Make static.
+ (remember_a_message): Add comment argument. Call
+ savable_comment_to_xgettext_comment and savable_comment_reset at the
+ end.
+ (remember_a_message_plural): Add comment argument. Call
+ savable_comment_to_xgettext_comment and xgettext_comment_reset,
+ savable_comment_reset at the end.
+ * x-awk.c (extract_parenthesized): Pass comment to remember_a_message.
+ * x-c.c (extract_parenthesized): Likewise.
+ * x-csharp.c (extract_parenthesized): Likewise.
+ * x-elisp.c (read_object): Likewise.
+ * x-glade.c (start_element_handler, end_element_handler): Likewise.
+ * x-java.c (extract_parenthesized): Likewise.
+ * x-librep.c (read_object): Likewise.
+ * x-lisp.c (read_object): Likewise.
+ * x-perl.c (extract_variable, interpolate_keywords, extract_balanced):
+ Likewise.
+ * x-php.c (extract_parenthesized): Likewise.
+ * x-python.c (extract_parenthesized): Likewise.
+ * x-scheme.c (read_object): Likewise.
+ * x-sh.c (read_word, read_command): Likewise.
+ * x-smalltalk.c (extract_smalltalk): Likewise.
+ * x-tcl.c (read_command): Likewise.
+ * x-ycp.c (extract_parenthesized): Likewise.
+ * x-rst.c (extract_rst): Update.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h (xgettext_comment_add, xgettext_comment,
+ xgettext_comment_reset): Remove declarations.
+ * xgettext.c (xgettext_comment_add, xgettext_comment,
+ xgettext_comment_reset): Make static.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ Use savable_comment_* API instead of xgettext_comment_* API.
+ * x-awk.c (phase2_getc): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (x_awk_lex): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (extract_parenthesized): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-elisp.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_object): Call savable_comment_reset instead of
+ xgettext_comment_reset. Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-glade.c (start_element_handler, end_element_handler): Call
+ savable_comment_to_xgettext_comment and savable_comment_reset around
+ remember_a_message. Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (comment_handler): Call savable_comment_add instead of
+ xgettext_comment_add.
+ * x-librep.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_object): Call savable_comment_reset instead of
+ xgettext_comment_reset. Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-lisp.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_object): Call savable_comment_reset instead of
+ xgettext_comment_reset. Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-perl.c (phase2_getc): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (extract_variable, interpolate_keywords): Call
+ savable_comment_to_xgettext_comment and savable_comment_reset around
+ remember_a_message.
+ (x_perl_prelex): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (extract_balanced): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-php.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (x_php_lex): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (extract_parenthesized): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-scheme.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_object): Call savable_comment_reset instead of
+ xgettext_comment_reset. Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-sh.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_word): Call savable_comment_reset instead of
+ xgettext_comment_reset. Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ (read_command): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-smalltalk.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (phase2_get): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (extract_smalltalk): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-tcl.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (read_word): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (read_command): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+ * x-ycp.c (phase2_getc): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (x_ycp_lex): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (extract_parenthesized): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset around remember_a_message.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ * message.c (message_list_hash_insert_entry): Update.
+
+2005-10-03 Bruno Haible <bruno@clisp.org>
+
+ * message.c (message_list_alloc, message_list_free,
+ message_list_hash_insert_entry, message_list_remove_if_not,
+ message_list_msgids_changed, message_list_search): Update.
+ * write-qt.c (write_qm): Update.
+ * xgettext.c (flag_context_list_table_lookup,
+ flag_context_list_table_insert): Update.
+ * x-awk.c (x_awk_keyword, extract_parenthesized): Update.
+ * x-c.c (add_keyword, x_c_lex): Update.
+ * x-csharp.c (x_csharp_keyword, extract_parenthesized: Update.
+ * x-elisp.c (x_elisp_keyword, read_object): Update.
+ * x-glade.c (x_glade_keyword, start_element_handler): Update.
+ * x-java.c (x_java_keyword, extract_parenthesized): Update.
+ * x-librep.c (x_librep_keyword, read_object): Update.
+ * x-lisp.c (x_lisp_keyword, read_object): Update.
+ * x-perl.c (x_perl_keyword, extract_variable, interpolate_keywords,
+ extract_balanced): Update.
+ * x-php.c (x_php_keyword, extract_parenthesized): Update.
+ * x-python.c (x_python_keyword, extract_parenthesized): Update.
+ * x-scheme.c (x_scheme_keyword, read_object): Update.
+ * x-sh.c (x_sh_keyword, read_command): Update.
+ * x-tcl.c (x_tcl_keyword, read_command): Update.
+
+2005-10-01 Bruno Haible <bruno@clisp.org>
+
+ Support for context dependent translations in PO files.
+ * message.h (MSGCTXT_SEPARATOR): New macro.
+ (struct message_ty): Add 'msgctxt' field.
+ (message_alloc): Add msgctxt argument.
+ (is_header): New macro.
+ (message_list_search, message_list_search_fuzzy,
+ message_list_list_search, message_list_list_search_fuzzy,
+ msgdomain_list_search, msgdomain_list_search_fuzzy): Add msgctxt
+ argument.
+ * message.c: Include xallocsa.h.
+ (message_alloc): Add msgctxt argument.
+ (message_copy): Update.
+ (message_list_hash_insert_entry): New function.
+ (message_list_append, message_list_prepend, message_list_insert_at,
+ message_list_msgids_changed): Use it.
+ (message_list_search): Add msgctxt argument.
+ (message_list_search_fuzzy_inner): Likewise.
+ (message_list_search_fuzzy): Likewise.
+ (message_list_list_search): Likewise.
+ (message_list_list_search_fuzzy): Likewise.
+ * msgl-ascii.c (is_ascii_message): Also test the msgctxt.
+ * write-po.c (message_print): Warn if some msgctxt has non-ASCII
+ characters. Write out the msgctxt.
+ (message_print_obsolete): Likewise.
+ (msgdomain_list_print_po): Use is_header macro.
+ (msgdomain_list_print): Likewise. Bail out if contexts are present and
+ cannot be stored in the given output format.
+ * write-properties.c (write_message): Use is_header macro.
+ * po-lex.c: Include message.h.
+ (keyword_p): Also recognize 'msgctxt'.
+ (po_gram_lex): Bail out if a string contains the EOT character.
+ * read-po-abstract.h (struct abstract_po_reader_class_ty): Add msgctxt
+ argument to directive_message function pointer.
+ (po_callback_message): Add msgctxt argument.
+ * read-po-abstract.c (call_directive_message, po_callback_message): Add
+ msgctxt argument.
+ * po-gram-gen.y (do_callback_message): Add msgctxt argument. Use
+ is_header macro.
+ (MSGCTXT): New token type.
+ (message_intro, MSGCTXT): Declare return types.
+ (message): Use message_intro instead of just MSGID.
+ (message_intro): New nonterminal reduction rules.
+ * read-properties.c (properties_parse): Update.
+ * read-stringtable.c (stringtable_parse): Update.
+ * read-po.h (struct default_po_reader_class_ty): Add msgctxt argument
+ to add_message field.
+ (default_directive_message, default_add_message): Likewise.
+ * read-po.c (call_add_message, default_directive_message,
+ default_add_message): Add msgctxt argument.
+ * msgl-iconv.c (convert_msgid): Also convert the msgctxt.
+ (iconv_message_list): Use is_header macro. Test also the msgctxt for
+ non-ASCII-ness.
+ * msgl-cat.c (is_message_selected, is_message_needed): Use is_header
+ macro.
+ (catenate_msgdomain_list): Likewise. Update.
+ * msgl-equal.c (message_equal): Likewise. Also compare the msgctxt.
+ * msgcmp.c (is_message_selected): Use is_header macro.
+ (match_domain): Update.
+ (compare): Use is_header macro.
+ * msgmerge.c (message_merge, match_domain): Use is_header macro.
+ Update.
+ (merge): Update.
+ * msgattrib.c (is_message_selected): Use is_header macro.
+ (process_message_list): Likewise. Update.
+ * msgl-charset.c (compare_po_locale_charsets): Use is_header macro.
+ * msgexec.c (process_string): Also set or unset MSGEXEC_MSGCTXT
+ variable.
+ * msgfilter.c (process_message): Use is_header macro.
+ * msggrep.c (grep_task): Increase size from 3 to 4.
+ (long_options): Add --msgctxt option.
+ (main): Accept -J/--msgctxt option. Update grep_pass numbers.
+ (no_pass): Update.
+ (usage): Mention -J option.
+ (is_message_selected): Use is_header macro. Perform a new grep pass on
+ the msgctxt.
+ * msginit.c (fill_header): Use is_header macro. Update.
+ (update_msgstr_plurals): Update.
+ * read-mo.c (read_mo_file): Split msgid into msgctxt and msgid. Update.
+ * read-tcl.c (msgdomain_read_tcl): Use is_header macro.
+ * msgl-check.c (check_plural): Update.
+ (check_message): Use is_header macro.
+ * write-mo.c (write_table): Write msgid with msgctxt, instead of just
+ the msgid.
+ * write-java.c: Include xerror.h.
+ (write_java_code): Update.
+ (msgdomain_write_java): Give error if some entries have a context.
+ * write-csharp.c: Include xerror.h.
+ (write_csharp_code): Update.
+ (msgdomain_write_csharp): Give error if some entries have a context.
+ * write-resources.c (msgdomain_write_csharp_resources): Bail out if
+ contexts are present and cannot be stored in the given output format.
+ * write-tcl.c (write_msg): Use is_header macro.
+ (msgdomain_write_tcl): Bail out if contexts are present and cannot be
+ stored in the given output format.
+ * write-qt.c: Include hash.h.
+ (write_qm): Use is_header macro. Write out msgctxt if present. Write
+ a contexts section if appropriate.
+ * msgfmt.c (msgfmt_add_message): Add msgctxt argument.
+ (msgfmt_frob_new_message): Use is_header macro.
+ * xgettext.c (exclude_directive_message): Add msgctxt argument.
+ (remember_a_message, construct_header, finalize_header): Update.
+ * x-po.c (extract_add_message): Add msgctxt argument. Update.
+ (extract): Update.
+ * gettext-po.h (po_message_msgctxt, po_message_set_msgctxt): New
+ declarations.
+ * gettext-po.c (po_file_domain_header): Use is_header macro.
+ (po_message_create): Update.
+ (po_message_msgctxt, po_message_set_msgctxt): New functions.
+ (po_message_check_all): Use is_header macro.
+
+2005-10-01 Bruno Haible <bruno@clisp.org>
+
+ Avoid a crash when msgcat or msgconv is asked to convert a non-ASCII
+ msgid.
+ * msgl-iconv.h (iconv_message_list): Change return type to boolean.
+ * msgl-iconv.c (iconv_message_list): Likewise. Return true if some
+ msgids change.
+ * msgl-cat.c (catenate_msgdomain_list): Exit with an error message if
+ the msgids change through charset conversion.
+
+2005-10-04 Bruno Haible <bruno@clisp.org>
+
+ * format.c: Include stdbool.h.
+ (check_msgid_msgstr_format): Enable strict checking also when
+ msgid_plural is present but nplurals = 1.
+ Reported by Farzaneh Sarafraz <farzaneh@farsiweb.info>.
+
+2005-10-01 Bruno Haible <bruno@clisp.org>
+
+ Change msgattrib to no longer remove extracted comments and file
+ positions when marking messages obsolete.
+ * msgmerge.c (merge): When marking a message obsolete, remove the
+ extracted comments and file positions.
+ * write-po.c (message_print_obsolete): Also print the extracted
+ comments and file positions.
+
+2005-09-25 Bruno Haible <bruno@clisp.org>
+
+ Support for Python source encodings (PEP 0263).
+ * x-python.c: Include progname.h, basename.h, xerror.h, strstr.h,
+ c-ctype.h, utf8-ucs4.h.
+ (phase1_pushback): Reduce size.
+ (UEOF): New macro.
+ (phase2_pushback, phase2_pushback_length): New variables.
+ (phase2_getc, phase2_ungetc): New functions.
+ (struct unicode_string_buffer): New structure type.
+ (init_unicode_string_buffer, unicode_string_buffer_append_unicode_grow,
+ unicode_string_buffer_append_unicode, unicode_string_buffer_result,
+ free_unicode_string_buffer): New functions.
+ (comment_buffer): New variable.
+ (buffer, bufmax, buflen): Remove variables.
+ (comment_start, comment_add, comment_line_end): Rewritten.
+ (comment_at_start): New function.
+ (xgettext_current_file_source_encoding): New variable.
+ (xgettext_current_file_source_iconv): New variable.
+ (set_current_file_source_encoding, try_to_extract_coding): New
+ functions.
+ (continuation_or_nonblank_line): New variable.
+ (phase3_getc): Renamed from phase2_getc. Use phase2_getc instead of
+ phase1_getc. Return a Unicode character. Call try_to_extract_coding
+ when seeing a comment among the first two lines.
+ (phase3_ungetc): Renamed from phase2_ungetc. Use phase2_ungetc instead
+ of phase1_ungetc.
+ (UNICODE, IS_UNICODE, UNICODE_VALUE): New macros.
+ (struct mixed_string_buffer): New structure type.
+ (init_mixed_string_buffer, mixed_string_buffer_append_byte,
+ mixed_string_buffer_append_unicode_grow,
+ mixed_string_buffer_append_unicode,
+ mixed_string_buffer_flush_utf16_surr,
+ mixed_string_buffer_flush_curr_buffer, mixed_string_buffer_append,
+ mixed_string_buffer_result, free_mixed_string_buffer): New functions.
+ (phase7_getuc): Use phase2_getc instead of phase1_getc. Return a
+ Unicode character except for \ooo and \xnn.
+ (phase5_get): Operate on the level of Unicode characters instead of
+ at the level of bytes. Use a mixed_string_buffer to accumulate a
+ string literal.
+ (extract_parenthesized): Set xgettext_current_source_encoding to UTF-8
+ while passing UTF-8 strings to the xgettext main code.
+ (extract_python): Initialize xgettext_current_file_source_encoding and
+ xgettext_current_source_encoding.
+
+2005-09-25 Bruno Haible <bruno@clisp.org>
+
+ * x-csharp.c (phase2_getc): Fix mis-use of iconv() when the source
+ encoding is neither ASCII nor UTF-8.
+
+2005-09-20 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Invoke bindtextdomain for bison-runtime.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+ * Makefile.am (DEFS): Also define BISON_LOCALEDIR.
+
+2005-09-17 Bruno Haible <bruno@clisp.org>
+
+ * msgl-check.h: New file.
+ * msgl-check.c: New file, mostly extracted from msgfmt.c.
+ * Makefile.am (noinst_HEADERS): Add msgl-check.h.
+ (libgettextsrc_la_SOURCES): Add msgl-check.c, plural-eval.c.
+ (msgfmt_SOURCES): Remove plural-eval.c.
+ * format.c (check_msgid_msgstr_format): Add const to argument type.
+ * format.h (check_msgid_msgstr_format): Add const to argument type.
+ * gettext-po.c: Include msgl-check.h.
+ (po_file_check_all, po_message_check_all): New functions.
+ (po_xerror_logger): Remove function.
+ (po_message_check_format): Use new check_message function.
+ * gettext-po.h (po_file_check_all, po_message_check_all): New
+ declarations.
+ * msgfmt.c: Include msgl-check.h instead of setjmp.h, signal.h,
+ stdarg.h, po-xerror.h, format.h, plural-exp.h, plural-table.h,
+ strstr.h.
+ (SIZEOF, sigjmp_buf, sigsetjmp, siglongjmp, USE_SIGINFO,
+ sigfpe_exit, sigfpe_code, sigfpe_handler, install_sigfpe_handler,
+ uninstall_sigfpe_handler, check_plural_eval, plural_help, check_plural,
+ curr_mp, curr_msgid_pos, formatstring_error_logger, check_pair,
+ check_header_entry): Move definitions to msgl-check.c.
+ (main): Update.
+ (msgfmt_frob_new_message): Call check_message instead of
+ check_header_entry and check_pair.
+
+2005-09-17 Bruno Haible <bruno@clisp.org>
+
+ Use new error handlers in libgettextpo.
+ * gettext-po.h (PO_SEVERITY_WARNING, PO_SEVERITY_ERROR,
+ PO_SEVERITY_FATAL_ERROR): New macros.
+ (po_xerror_handler): New structure type.
+ (po_xerror_handler_t): New type.
+ (po_file_read): Change signature; version 3.
+ (po_file_write): Change signature; version 2.
+ (po_message_check_format): Change signature; version 2.
+ * gettext-po.c: Include po-xerror.h.
+ (po_file_read): New implementation. Renamed old implementation to
+ po_file_read_v2.
+ (po_file_write): New implementation. Keep old implementation.
+ (po_xerror_logger): New function.
+ (po_message_check_format): New implementation. Keep old implementation.
+
+2005-09-17 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_plural): Fix broken determination of max_nplurals.
+
+2005-09-17 Bruno Haible <bruno@clisp.org>
+
+ New, higher-level error message output primitives.
+ * po-xerror.h: New file.
+ * po-xerror.c: New file.
+ * open-po.c: Include xerror.h, po-xerror.h, not error.h.
+ (open_po_file): Use po_xerror instead of error.
+ * po-charset.c: Include po-xerror.h, not po-error.h.
+ (po_lex_charset_set): Use po_xerror instead of po_multiline_warning.
+ * po-lex.c: Include xerror.h, po-error.h, po-xerror.h.
+ (po_gram_error, po_gram_error_at_line): Use po_xerror instead of
+ po_error or po_error_at_line. Don't decrement error_message_count;
+ let the caller do this instead.
+ (mbfile_getc, lex_getc): Use po_xerror instead of po_error.
+ * po-lex.h: Don't include po-error.h.
+ (po_gram_error, po_gram_error_at_line): Remove optimized macros.
+ * read-po-abstract.c: Include xerror.h, po-xerror.h.
+ (po_scan): Use po_xerror instead of po_error.
+ * read-po.c: Include po-xerror.h.
+ (default_add_message): Use po_xerror2 instead of po_gram_error_at_line
+ pair.
+ * read-properties.c: Include xerror.h, po-xerror.h, not exit.h.
+ (phase1_getc, phase4_getuc): Use po_xerror instead of error.
+ * read-stringtable.c: Include xerror.h, po-xerror.h, not exit.h.
+ (phase1_getc, read_string, stringtable_parse): Use po_xerror instead of
+ error.
+ * write-po.c: Include po-xerror.h, not exit.h, po-error.h.
+ (wrap): Pass the entire message as argument. Use po_xerror instead of
+ po_error.
+ (message_print, message_print_obsolete): Update. Use po_xerror instead
+ of po_multiline_warning.
+ (msgdomain_list_print): Use po_xerror instead of po_error or
+ po_error_at_line.
+ * msgl-iconv.h (struct conversion_context): Add 'message' field.
+ * msgl-iconv.c: Include xerror.h, po-xerror.h, not error.h, exit.h.
+ (conversion_error): Use po_xerror instead of error.
+ (iconv_message_list): Likewise. Set the entire message in the
+ conversion context before calling convert_*.
+ (iconv_msgdomain_list): Use po_xerror instead of error.
+ * xgettext.c (from_current_source_encoding): Set context.message.
+ * msgfmt.c: Include po-xerror.h.
+ (check_plural_eval): Pass the entire header as argument. Use po_xerror
+ instead of error_at_line.
+ (plural_help): New function, extracted from check_plural.
+ (check_plural): Change type of local variables has_plural, min_pos,
+ max_pos. Use po_xerror2 instead of error_at_line pairs. Use po_xerror
+ instead of error_at_line.
+ (curr_mp): New variable.
+ (formatstring_error_logger): Use po_xerror instead of vfprintf.
+ (check_pair): Take the entire message as argument. Use po_xerror
+ instead of error_at_line. Set curr_mp before calling
+ check_msgid_msgstr_format.
+ (check_header_entry): Take the entire header message as argument. Use
+ po_xerror instead of multiline_error.
+ (msgfmt_frob_new_message): Update.
+ * msgcmp.c (match_domain): Decrement error_message_count between two
+ error messages that belong together.
+ * msgmerge.c (match_domain): Decrement error_message_count between two
+ error messages that belong together.
+ * Makefile.am (noinst_HEADERS): Add po-xerror.h.
+ (COMMON_SOURCE): Add po-xerror.c.
+
+2005-09-16 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.c (po_file_read): Set gram_max_allowed_errors to a large
+ integer, so that po_gram_error doesn't cause the program to exit().
+
+2005-09-14 Bruno Haible <bruno@clisp.org>
+
+ * x-csharp.c: Include xerror.h.
+
+2005-09-11 Bruno Haible <bruno@clisp.org>
+
+ * user-email.sh.in: Add support for Mozilla Thunderbird and GNOME
+ Evolution 2. Improve mutt support.
+
+2005-08-26 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Add an entry about Romanian.
+ Explanations by Eddy Petrişor <eddy.petrisor@gmail.com>.
+
+2005-08-23 Bruno Haible <bruno@clisp.org>
+
+ * write-mo.h (byteswap): New variable.
+ * write-mo.c: Include byteswap.h.
+ (byteswap): New variable.
+ (BSWAP32): New macro.
+ (write_table): If requested, byteswap all 32-bit values before writing
+ them out.
+ * msgfmt.c (long_options): Add --endianness.
+ (main): Accept --endianness argument and set byteswap.
+
+2005-08-08 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Add support for Old English,
+ Hyam, Jju, Tyap, Maithili, Nahuatl, Low Saxon, Northern Sotho, Páez,
+ Tetum, Zapotec.
+ (englishname_of_language): Add support for Old English, Hyam, Jju,
+ Tyap, Kagoma, Maithili, Mayan, Nahuatl, Low Saxon, Northern Sotho,
+ Páez, Tetum, Zapotec.
+
+2005-05-21 Bruno Haible <bruno@clisp.org>
+
+ New undocumented option, used by glocale PO files.
+ * msgfmt.c (include_fuzzies): Renamed from include_all.
+ (include_untranslated): New variable.
+ (long_options): Add --use-untranslated.
+ (main): Handle --use-untranslated.
+ (msgfmt_frob_new_message): Test include_untranslated.
+ (msgfmt_comment_special): Update.
+
+2005-07-26 Bruno Haible <bruno@clisp.org>
+
+ * user-email.sh.in (datarootdir): New variable.
+
+2005-07-26 Bruno Haible <bruno@clisp.org>
+
+ Fix compilation error on systems without iconv().
+ * xgettext.c (convert_string): Remove fallback macro definition.
+ (from_current_source_encoding) [!HAVE_ICONV]: Just abort here.
+
+2005-07-17 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c (mb_width): Remove gcc warnings on platform with unsigned
+ 'char' type (e.g. Linux/PowerPC).
+ Reported by Jeff Rizzo <riz@netbsd.org>.
+
+2005-07-05 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msgmerge_SOURCES, xgettext_SOURCES, msgattrib_SOURCES,
+ msgcat_SOURCES, msgcomm_SOURCES, msgconv_SOURCES, msgen_SOURCES,
+ msgfilter_SOURCES, msggrep_SOURCES, msguniq_SOURCES): On mingw, use
+ C++ source code.
+ (libgettextsrc_la_LDFLAGS, libgettextpo_la_LDFLAGS): Use LTNOUNDEF.
+ (format_CFLAGS): New variable.
+ (format.lo): New rule.
+ (msg*_CFLAGS, xgettext_CFLAGS): Remove variables.
+ (msg*_CPPFLAGS, xgettext_CPPFLAGS): New variables.
+
+2005-05-27 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Output \a and \v as an escape sequence, like
+ \b, \f, \r.
+ Suggested by Asgeir Frimannsson <asgeirf@redhat.com>.
+
+2005-05-05 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msgfmt_SOURCES): Add hash-string.c.
+ * Makefile.msvc (msgfmt_OBJECTS): Add hash-string.obj.
+ (hash-string.obj): New rule.
+ * Makefile.vms (msgfmt_OBJECTS): Add hash-string.obj.
+ (hash-string.obj): New rule.
+
+2005-05-01 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.c (open_singlequote_terminator): New variable.
+ (saw_opening_singlequote): Set it.
+ (phase2_getc): Handle the case of open_singlequote_terminator == '"'.
+ (read_word): Likewise. Treat i18n quotes like single quotes with
+ terminator '"'.
+ Reported by Jakub Bogusz <qboosh@pld-linux.org>.
+
+2005-05-01 Bruno Haible <bruno@clisp.org>
+
+ Improved error message.
+ * msgl-iconv.h (struct conversion_context): New type.
+ (convert_string): Add context argument.
+ * msgl-iconv.c (conversion_error): New function.
+ (convert_string, convert_string_list, convert_msgid, convert_msgstr):
+ Add context argument.
+ (iconv_message_list): Construct context for them.
+ * xgettext.c (convert_string): Add context argument.
+ (from_current_source_encoding): Construct context for convert_string.
+ Reported by Hans Ulrich Niedermann <debian@n-dimensional.de>.
+
+2005-04-18 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.h (po_gram_error, po_gram_error_at_line): Test for
+ __APPLE_CC__ > 1. Needed because gcc-4.0 defines __APPLE_CC__ and
+ implements __VA_ARGS__ correctly.
+ * po-lex.c (po_gram_error, po_gram_error_at_line): Likewise.
+ * read-stringtable.c (phase2_getc): Optimize UTF-8 code. Avoids
+ gcc-4.0 warnings.
+
+2006-06-21 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.6 released.
+
+2006-06-21 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (CLEANFILES): Add msgfmt.net.exe.mdb,
+ msgunfmt.net.exe.mdb.
+
+2006-06-21 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (LIBGETTEXTPO_VERSION): Bump version number.
+
+2006-06-20 Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c (struct unnumbered_arg): Remove type.
+ (struct numbered_arg): New type.
+ (struct spec): Use it.
+ (isdigit): New macro.
+ (numbered_arg_compare): New function.
+ (format_parse, format_free, format_check, format_print): Update to
+ GCC 4.1 internal format strings.
+ Reported by Göran Uddeborg <goeran@uddeborg.se>.
+
+2005-05-23 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.5 released.
+
+2005-05-23 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (LIBGETTEXTPO_VERSION): Bump version number.
+
+2005-05-21 Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c: Update for GCC 4.0.
+ (FAT_POINTER, FAT_SIZE_LONGLONG, FAT_SIZE_WIDE, FAT_SIZE_MASK): New
+ enum items.
+ (FAT_*): Update.
+ (struct spec): Add uses_err_no field.
+ (format_parse): Initialize uses_err_no field. Handle %q flag. Handle
+ 'll', 'w' size specifiers. Handle %<, %>, %', %m, %p, %J directives.
+ (format_check): Also check that the use of err_no is the same.
+ Based on a patch by Jakub Jelinek <jakub@redhat.com>.
+
+2005-04-11 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.4 released.
+
+2005-04-11 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (LIBGETTEXTPO_VERSION): Bump version number.
+
+2005-03-14 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.3 released.
+
+2005-03-08 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (LIBGETTEXTPO_VERSION): Bump version number.
+
+2005-02-26 Bruno Haible <bruno@clisp.org>
+
+ * format-scheme.c (format_arg_type): Add FAT_COMPLEX. Remove
+ FAT_FUNCTION.
+ (make_intersected_element): Simplify also the intersection with
+ FAT_COMPLEX.
+ (make_union_element): Simplify also the union with FAT_COMPLEX.
+ (IIC): New array.
+ (nocheck_params): Remove function.
+ (parse_upto): Remove handling of ~W, ~<, ~>.
+ Implement non-CL handling of ~C, ~I, ~_, ~/, ~T.
+ Implement handling of ~Y, ~!, ~Q, ~K.
+
+2005-02-26 Bruno Haible <bruno@clisp.org>
+
+ * format-scheme.c (parse_upto): Remove support for ~!.
+ Reported by Kevin Ryde <user42@zip.com.au>.
+
+2005-02-24 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.2 released.
+
+2005-02-12 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (gettextpo.lib): Link with intl.lib as well.
+
+2005-02-12 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (msginit_OBJECTS): Add plural-count.obj.
+ * Makefile.vms (msginit_OBJECTS): Add plural-count.obj.
+
+2005-02-10 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (po_header_set_field): New declaration.
+ * gettext-po.c (po_header_set_field): New function.
+ Suggested by Ross Golder <ross@golder.org>.
+
+2005-02-10 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (LIBGETTEXTPO_VERSION): New macro.
+ (libgettextpo_version): New declaration.
+ * gettext-po.c (libgettextpo_version): New variable.
+
+2005-02-10 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (po_message_remove_filepos, po_message_add_filepos): New
+ declarations.
+ * gettext-po.c (po_message_remove_filepos, po_message_add_filepos): New
+ functions.
+ Suggested by Asgeir Frimannsson <asgeirf@redhat.com>.
+
+2005-02-10 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (po_message_set_extracted_comments): New declaration.
+ * gettext-po.c (po_message_set_extracted_comments): New function.
+ Suggested by Asgeir Frimannsson <asgeirf@redhat.com>.
+
+2005-02-08 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (get_user_email): Cast _() to 'char *', to avoid compiler
+ warning.
+
+2005-02-07 Bruno Haible <bruno@clisp.org>
+
+ Make the tools work in the tr_TR locale.
+ * po-charset.c: Include c-strcase.h instead of strcase.h.
+ (po_charset_canonicalize): Use c_strcasecmp.
+ * msgcmp.c: Include c-strcase.h instead of strcase.h.
+ (compare): Use c_strncasecmp.
+ * msginit.c: Include c-strcase.h instead of strcase.h.
+ (content_type): Use c_strcasecmp.
+ * msgmerge.c: Include c-strcase.h instead of strcase.h.
+ (message_merge, merge): Use c_strncasecmp.
+ * xgettext.c: Include c-strcase.h instead of strcase.h.
+ (language_to_extractor): Use c_strcasecmp.
+ Reported by Recai Oktaş <roktas@omu.edu.tr>.
+
+2005-02-07 Bruno Haible <bruno@clisp.org>
+
+ * project-id: Use LC_ALL=C to protect range expression against
+ Estonian locale.
+
+2005-02-06 Bruno Haible <bruno@clisp.org>
+
+ Leave #: lines with unknown syntax alone instead of turning them into
+ plain comments.
+ * po-hash.h: Remove file.
+ * po-hash-gen.y: Remove file.
+ * read-po-abstract.c: Don't include po-hash.h.
+ (po_parse_comment_filepos, po_parse_comment_solaris_filepos): New
+ functions.
+ (po_callback_comment_dispatcher): Call them instead of the old
+ po_parse_comment_filepos.
+ * read-stringtable.c: Don't include po-hash.h.
+ * Makefile.am (noinst_HEADERS): Remove po-hash.h, po-hash-gen.h.
+ (COMMON_SOURCE): Remove po-hash-gen.y.
+ (BUILT_SOURCES): Remove po-hash-gen.c, po-hash-gen.h.
+ * Makefile.msvc (OBJECTS): Remove po-hash-gen.obj.
+ (po-hash-gen.obj): Remove rule.
+ * Makefile.vms (OBJECTS): Remove po-hash-gen.obj.
+ (po-hash-gen.obj): Remove rule.
+ * FILES: Update.
+ Reported by David Fraser <davidf@sjsoft.com>.
+
+2005-02-04 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Add an entry about Vietnamese.
+ Explanations by Clytie Siddall <clytie@riverland.net.au>.
+
+2005-01-29 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msgcmp_LDADD, msgfmt_LDADD, msgmerge_LDADD,
+ msgunfmt_LDADD, xgettext_LDADD, msgattrib_LDADD, msgcat_LDADD,
+ msgcomm_LDADD, msgconv_LDADD, msgen_LDADD, msgexec_LDADD,
+ msgfilter_LDADD, msggrep_LDADD, msguniq_LDADD): Add INTL_MACOSX_LIBS.
+
+2005-01-27 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (formatstring_error_logger): Cast the fprrintf argument,
+ to make it work on big-endian 64-bit machines.
+
+2005-01-16 Bruno Haible <bruno@clisp.org>
+
+ Support for Scheme.
+ * message.h (format_type): New enum value 'format_scheme'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_scheme entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_scheme): New declaration.
+ * format-scheme.c: New file, based on format-lisp.c.
+ * format.c (formatstring_parsers): Add formatstring_scheme.
+ * x-scheme.h: New file.
+ * x-scheme.c: New file.
+ * xgettext.c: Include x-scheme.h.
+ (flag_table_scheme): New variable.
+ (main): Invoke init_flag_table_scheme, x_scheme_extract_all,
+ x_scheme_keyword.
+ (usage): Mention Scheme source language.
+ (xgettext_record_flag): Handle format_scheme.
+ (language_to_extractor): Add Scheme rule.
+ (extension_to_language): Add Scheme rule.
+ * Makefile.am (noinst_HEADERS): Add x-scheme.h.
+ (FORMAT_SOURCE): Add format-scheme.c.
+ (xgettext_SOURCES): Add x-scheme.c.
+ * Makefile.msvc (OBJECTS): Add format-scheme.obj.
+ (xgettext_OBJECTS): Add x-scheme.obj.
+ (format-scheme.obj, x-scheme.obj): New rules.
+ * Makefile.vms (OBJECTS): Add format-scheme.obj.
+ (xgettext_OBJECTS): Add x-scheme.obj.
+ (format-scheme.obj, x-scheme.obj): New rules.
+ * FILES: Update.
+
+2005-01-08 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (grep_args, grep_path, grep_argv): Remove variables.
+ (struct grep_task): New type.
+ (grep_task): New variable.
+ (main): Initialize grep_task instead of grep_args. Change processing
+ of options 'E', 'F' to set a matcher, of 'e', 'f' to append to the
+ pattern string, and of 'i' to set a boolean. Compile the patterns.
+ (is_string_selected): Execute a compiled pattern instead of calling
+ the grep program.
+ (is_message_selected): Update.
+ * Makefile.am (AM_CPPFLAGS): Add -I option for libgrep.
+ (LIBGREP): New variable.
+ (msggrep_LDADD): Add $(LIBGREP).
+ * Makefile.msvc (INCLUDES): Add libgrep directory.
+ (msggrep.exe): Link with libgrep.
+ * Makefile.vms (INCLUDES): Add libgrep directory.
+ (msggrep.exe): Link with libgrep.
+
+2005-01-09 Bruno Haible <bruno@clisp.org>
+
+ * msgl-ascii.h (is_ascii_msgdomain_list): New declaration.
+ * msgl-ascii.c (is_ascii_msgdomain_list): New function.
+ * x-po.c (header_charset): New variable.
+ (extract_add_message): When seeing the header entry, store the charset
+ in header_charset.
+ (extract): Store the value of header_charset in the new constructed
+ header entry.
+ * xgettext.c (main): Convert the existing PO file to UTF-8 if it was
+ not entirely ASCII.
+ Reported by Jörg Schilling <schilling@fokus.fraunhofer.de>.
+
+2005-01-08 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (usage): Explain COMMENT-PATTERN syntax as well.
+
+2005-01-06 Bruno Haible <bruno@clisp.org>
+
+ * x-tcl.c (read_word): Ignore non-space whitespace at the beginning.
+ Reported by William J Poser <wjposer@ldc.upenn.edu>.
+
+2005-01-06 Bruno Haible <bruno@clisp.org>
+
+ * write-csharp.c (msgdomain_write_csharp): Don't call fclose after
+ fwriteerror.
+ * write-java.c (msgdomain_write_java): Likewise.
+ * write-mo.c (msgdomain_write_mo): Likewise.
+ * write-po.c (msgdomain_list_print): Likewise.
+ * write-qt.c (msgdomain_write_qt): Likewise.
+ * write-resources.c (execute_writing_input): Likewise.
+ * write-tcl.c (msgdomain_write_tcl): Likewise.
+
+2005-01-05 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2005-01-07 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.c (po_file_read): Interpret "-" and "/dev/stdin" as
+ denoting stdin.
+ Suggested by Asgeir Frimannsson <asgeirf@redhat.com>.
+
+2004-11-29 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (libgettextpo_la_LDFLAGS): Add @LTLIBINTL@ @LTLIBICONV@.
+ Reported by Sam Steingold <sds@gnu.org>.
+
+2004-10-01 Guido Flohr <guido@imperia.net>
+
+ * x-perl.c (x_perl_prelex): Recognize function names starting with '-'.
+ Reported by Ryan Anderson <ryan@autoweb.net>.
+
+2004-09-16 Bruno Haible <bruno@clisp.org>
+
+ * format.h (formatstring_error_logger_t): Modify decl for GCC <= 3.0.
+ Reported by Jens A. Tkotz <jens@peino.de>.
+
+2004-09-11 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msginit_SOURCES): Add plural-count.c.
+ * msginit.c (update_msgstr_plurals): New function.
+ (main): Call it.
+ * msgmerge.c (match_domain): Provide the appropriate number of plural
+ forms for messages that don't occur in the Def.po file.
+ Reported by Jens A. Tkotz <jens@peino.de>.
+
+2004-09-06 Bruno Haible <bruno@clisp.org>
+
+ * format.h (formatstring_error_logger_t): New type.
+ (struct formatstring_parser): Change calling convention of 'check'
+ method.
+ (check_msgid_msgstr_format): New declaration.
+ * format-awk.c (format_check): Use error_logger argument instead of
+ noisy and error_at_line.
+ * format-c.c (format_check): Likewise.
+ * format-csharp.c (format_check): Likewise.
+ * format-elisp.c (format_check): Likewise.
+ * format-gcc-internal.c (format_check): Likewise.
+ * format-java.c (format_check): Likewise.
+ * format-librep.c (format_check): Likewise.
+ * format-lisp.c (format_check): Likewise.
+ * format-pascal.c (format_check): Likewise.
+ * format-perl.c (format_check): Likewise.
+ * format-perl-brace.c (format_check): Likewise.
+ * format-php.c (format_check): Likewise.
+ * format-python.c (format_check): Likewise.
+ * format-qt.c (format_check): Likewise.
+ * format-sh.c (format_check): Likewise.
+ * format-tcl.c (format_check): Likewise.
+ * format-ycp.c (format_check): Likewise.
+ * format.c (check_msgid_msgstr_format): New function, extracted from
+ msgfmt.c.
+ * msgfmt.c (curr_msgid_pos): New variable.
+ (formatstring_error_logger): New function.
+ (check_pair): Use check_msgid_msgstr_format, formatstring_error_logger.
+ * msgmerge.c (msgfmt_check_pair_fails): Update.
+ * gettext-po.h (po_message_check_format): New declaration.
+ * gettext-po.c (po_error_logger, po_message_check_format): New
+ functions.
+
+2004-09-05 Bruno Haible <bruno@clisp.org>
+
+ * po-error.h: New file.
+ * po-error.c: New file.
+ * po-charset.c: Include po-error.h.
+ (po_lex_charset_set): Use po_multiline_warning instead of
+ multiline_warning.
+ * po-lex.h: Include po-error.h.
+ (po_gram_error): Use po_error instead of error.
+ (po_gram_error_at_line): Use po_error_at_line instead of error_at_line.
+ * po-lex.c (po_gram_error): Use po_error instead of error.
+ (po_gram_error_at_line): Use po_error_at_line instead of error_at_line.
+ (mbfile_getc, lex_getc): Use po_error instead of error.
+ * read-po-abstract.c (po_scan): Likewise.
+ * write-po.c: Include po-error.h instead of error.h.
+ (wrap): Use po_error instead of error.
+ (message_print, message_print_obsolete): Use po_multiline_warning
+ instead of multiline_warning.
+ (msgdomain_list_print): Use po_error instead of error, po_error_at_line
+ instead of error_at_line.
+ * str-list.h (string_list_join): New declaration.
+ * str-list.c (string_list_join): New function.
+ * message.h (message_list_insert_at): New declaration.
+ * message.c (message_list_insert_at): New function.
+ * gettext-po.h (po_filepos_t): New type.
+ (struct po_error_handler, po_error_handler_t): New types.
+ (po_file_create): New declaration.
+ (po_file_read): Add handler argument.
+ (po_file_write, po_message_insert, po_message_create,
+ po_message_set_msgid, po_message_set_msgid_plural,
+ po_message_set_msgstr, po_message_set_msgstr_plural,
+ po_message_comments, po_message_set_comments,
+ po_message_extracted_comments, po_message_filepos,
+ po_message_set_obsolete, po_message_set_fuzzy, po_message_set_format,
+ po_filepos_file, po_filepos_start_line): New declarations.
+ * gettext-po.c (po_file_create): New function.
+ (po_file_read): Add handler argument. Keep an old version for backward
+ compatibility.
+ (po_file_write): New function.
+ (po_message_iterator): Store more information, to make
+ po_message_insert possible.
+ (po_message_iterator_free): Update.
+ (po_next_message): Don't crash if iterator->mlp is NULL.
+ (po_message_insert): New function.
+ (po_message_create): New function.
+ (po_message_set_msgid, po_message_set_msgid_plural,
+ po_message_set_msgstr, po_message_set_msgstr_plural,
+ po_message_comments, po_message_set_comments,
+ po_message_extracted_comments, po_message_filepos,
+ po_message_set_obsolete, po_message_set_fuzzy, po_message_set_format,
+ po_filepos_file, po_filepos_start_line): New functions.
+ * Makefile.am (noinst_HEADERS): Add po-error.h.
+ (COMMON_SOURCE): Add po-error.c.
+ * Makefile.msvc (OBJECTS): Add po-error.obj.
+ (po-error.obj): New rule.
+ * Makefile.vms (OBJECTS): Add po-error.obj.
+ (po-error.obj): New rule.
+ * FILES: Update.
+
+2004-09-03 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msginit_LDADD): Use the INTL_MACOSX_LDFLAGS.
+
+2004-08-30 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Czech is now like Slovak.
+ Reported by Stepan Kasal <kasal@ucw.cz>.
+
+2004-06-23 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (phase1_getc): Fix phase0_getc invocation.
+ Reported by Matt Dreezer <matthew.dreezer@edl.uk.eds.com>.
+
+2004-05-14 Bruno Haible <bruno@clisp.org>
+
+ * format-java.c (message_format_parse): Fix argument of freesa() calls.
+
+2004-03-19 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (install-exec-clean): Don't remove libgettextsrc.a on
+ AIX.
+ Reported by Kouichi Hashikawa <z01a7ksy@cs.ecip.tohoku.ac.jp>.
+
+2004-03-14 Bruno Haible <bruno@clisp.org>
+
+ * format-lisp.c (parse_upto): Add integer restriction for the dispatch
+ argument in ~[...~].
+
+2004-03-02 Bruno Haible <bruno@clisp.org>
+
+ * read-csharp.c (msgdomain_read_csharp): Extend the lang -> frobbedlang
+ mapping so as to support Serbian and Uzbek locales.
+ * write-csharp.c (msgdomain_write_csharp): Likewise.
+ Reported by Jorn Baayen <jbaayen@dds.nl>.
+
+2004-02-27 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Add an entry about Serbian.
+ Reported by Danilo Segan <danilo@gnome.org>.
+
+2004-01-29 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14.1 released.
+
+2004-01-28 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.14 released.
+
+2004-01-18 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Add entries for Akan, Avaric,
+ Bambara, Cree, Divehi (Maldivian), Éwé, Igbo, Kongo, Kanuri, Ganda,
+ Luba-Katanga, Ojibwa, Venda.
+ (englishname_of_language): Likewise, and also for Fulah (Fulfulde).
+
+2004-01-09 Bruno Haible <bruno@clisp.org>
+
+ * format.h (struct formatstring_parser): Add 'translated' argument to
+ parse function.
+ (get_sysdep_c_format_directives): Renamed from
+ get_c99_format_directives. Add 'translated' argument. Change meaning
+ of endpos in returned array.
+ * format-c.c (struct spec): Rename field c99_directives to
+ sysdep_directives, and c99_directives_count to sysdep_directives_count.
+ (format_parse): Add 'translated' argument. Handle 'I' flag. Store the
+ pointer past the '>' character instead of the pointer to the '>'.
+ (format_c_parse, format_objc_parse): Add 'translated' argument.
+ (get_sysdep_c_format_directives): Renamed from
+ get_c99_format_directives. Add 'translated' argument.
+ * format-awk.c (format_parse): Add 'translated' argument.
+ * format-csharp.c (format_parse): Likewise.
+ * format-elisp.c (format_parse): Likewise.
+ * format-gcc-internal.c (format_parse): Likewise.
+ * format-java.c (format_parse): Likewise.
+ * format-librep.c (format_parse): Likewise.
+ * format-lisp.c (format_parse): Likewise.
+ * format-pascal.c (format_parse): Likewise.
+ * format-perl-brace.c (format_parse): Likewise.
+ * format-perl.c (format_parse): Likewise.
+ * format-php.c (format_parse): Likewise.
+ * format-python.c (format_parse): Likewise.
+ * format-qt.c (format_parse): Likewise.
+ * format-sh.c (format_parse): Likewise.
+ * format-tcl.c (format_parse): Likewise.
+ * format-ycp.c (format_parse): Likewise.
+ * msgfmt.c (check_pair): Update.
+ * msgmerge.c (msgfmt_check_pair_fails): Likewise.
+ * read-mo.c (get_sysdep_string): Don't add '<'...'>' around segments
+ of length 1.
+ (read_mo_file): Treat major revision 1 like major revision 0.
+ * write-mo.c (write_table): Update. Use major revision 1 if "I" occurs
+ among the sysdep segments.
+ * xgettext.c (set_format_flags_from_context, remember_a_message,
+ remember_a_message_plural): Update.
+
+2004-01-14 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (fill_header): Avoid a NULL pointer access when the header
+ has no comment attached to it.
+ Reported by Josep Puigdemont <baldrick@terra.es>.
+
+2004-01-09 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2003-12-29 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h: Include stdlib.h, str-list.h.
+ (struct refcounted_string_list_ty): New type.
+ (add_reference, drop_reference): New functions.
+ (savable_comment, savable_comment_add, savable_comment_reset,
+ savable_comment_to_xgettext_comment): New declarations.
+ * xgettext.c (savable_comment): New variable.
+ (savable_comment_add, savable_comment_reset,
+ savable_comment_to_xgettext_comment): New functions.
+ * x-java.c (struct refcounted_string_list_ty, comment, add_reference,
+ drop_reference, x_java_comment_add, x_java_comment_reset,
+ x_java_comment_to_xgettext_comment): Remove. Use replacement from
+ xgettext.{h,c} instead.
+ * x-csharp.c (struct refcounted_string_list_ty, comment, add_reference,
+ drop_reference, x_csharp_comment_add, x_csharp_comment_reset,
+ x_csharp_comment_to_xgettext_comment): Remove. Use replacement from
+ xgettext.{h,c} instead.
+ * x-c.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (struct token_ty): Add 'comment' field.
+ (free_token): Free it.
+ (phase5_get): Initialize token's 'comment' field.
+ (phase6_get): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (phase8a_get): Initialize token's 'comment' field.
+ (phase8b_get): Call savable_comment_reset instead of
+ xgettext_comment_reset.
+ (phase8c_get): In @"...", use the comment of the first token, not of
+ the second.
+ (struct xgettext_token_ty): Add 'comment' field.
+ (x_c_lex): Deal with the token's comment.
+ (extract_parenthesized): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset. Free the token's comment field.
+ * x-python.c (comment_line_end): Call savable_comment_add instead of
+ xgettext_comment_add.
+ (struct token_ty): Add 'comment' field.
+ (phase5_get): Call savable_comment_reset instead of
+ xgettext_comment_reset. Initialize token's 'comment' field.
+ (extract_parenthesized): Call savable_comment_to_xgettext_comment and
+ savable_comment_reset. Free the token's comment field.
+
+2003-12-28 Bruno Haible <bruno@clisp.org>
+
+ * read-mo.h (read_mo_file): Change 'fn' into 'filename'.
+ * read-mo.c (read_mo_file): Likewise.
+
+2003-12-28 Bruno Haible <bruno@clisp.org>
+
+ * write-resources.h: New file.
+ * write-resources.c: New file.
+ * msgfmt.cs: New file.
+ * msgfmt.c: Include write-resources.h.
+ (csharp_resources_mode): New variable.
+ (long_options): Add --csharp-resources.
+ (main): Handle --csharp-resources. More generic code for detection of
+ contradicting modes. Invoke msgdomain_write_csharp_resources.
+ (usage): Document --csharp-resources option.
+ (msgfmt_set_domain): Update.
+ * read-resources.h: New file.
+ * read-resources.c: New file.
+ * msgunfmt.cs (DumpResource): Add a constructor that dumps a .resources
+ file.
+ (Main): Invoke it when only one argument is given.
+ * msgunfmt.c: Include read-resources.h.
+ (csharp_resources_mode): New variable.
+ (long_options): Add --csharp-resources.
+ (main): Handle --csharp-resources. More generic code for detection of
+ contradicting modes. Invoke read_one_file instead of read_mo_file.
+ (usage): Document --csharp-resources option.
+ (read_one_file): New function.
+ * Makefile.am (noinst_HEADERS): Add read-resources.h and
+ write-resources.h.
+ (msgfmt_SOURCES): Add write-resources.c.
+ (msgunfmt_SOURCES): Add read-resources.c.
+ (EXTRA_DIST): Add msgfmt.cs.
+ (CLEANFILES): Add msgfmt.net.exe.
+ (msgfmt.net.exe): New rule.
+ (all-csharp-yes): Depend on it.
+ (install-exec-csharp-yes): Also install msgfmt.net.exe.
+ (uninstall-csharp-yes): Also uninstall msgfmt.net.exe.
+ * Makefile.msvc (msgfmt_OBJECTS): Add write-resources.obj.
+ (msgunfmt_OBJECTS): Add read-resources.obj.
+ (write-resources.obj, read-resources.obj): New rules.
+ * Makefile.vms (msgfmt_OBJECTS): Add write-resources.obj.
+ (msgunfmt_OBJECTS): Add read-resources.obj.
+ (write-resources.obj, read-resources.obj): New rules.
+ * FILES: Update.
+
+2003-12-26 Bruno Haible <bruno@clisp.org>
+
+ Support for C#.
+ * write-csharp.h: New file.
+ * write-csharp.c: New file.
+ * msgfmt.c: Include write-csharp.h.
+ (csharp_mode, csharp_resource_name, csharp_locale_name,
+ csharp_base_directory): New variables.
+ (long_options): Add option --csharp.
+ (main): Handle option --csharp. Initialize csharp_resource_name,
+ csharp_locale_name, csharp_base_directory. Perform checks for C# mode.
+ Invoke msgdomain_write_csharp.
+ (usage): Document --csharp option and C# mode.
+ (msgfmt_set_domain): Ignore in C# mode.
+ * read-csharp.h: New file.
+ * read-csharp.c: New file.
+ * msgunfmt.cs: New file.
+ * msgunfmt.c: Include read-csharp.h.
+ (csharp_mode, csharp_resource_name, csharp_locale_name,
+ csharp_base_directory): New variables.
+ (long_options): Add option --csharp.
+ (main): Handle option --csharp. Initialize csharp_resource_name,
+ csharp_locale_name, csharp_base_directory. Perform checks for C# mode.
+ Invoke msgdomain_read_csharp.
+ (usage): Document --csharp option and C# mode.
+ * Makefile.am (noinst_HEADERS): Add read-csharp.h, write-csharp.h.
+ (CSHARPCOMP, CSHARPCOMPFLAGS): New variables.
+ (msgfmt_SOURCES): Add write-csharp.c.
+ (msgunfmt_SOURCES): Add read-csharp.c.
+ (EXTRA_DIST): Add msgunfmt.cs.
+ (CLEANFILES): Add msgunfmt.net.exe.
+ (all-csharp-yes, all-csharp-no): New rules.
+ (all-local): Depend on them.
+ (msgunfmt.net.exe): New rule.
+ (install-exec-csharp-yes, install-exec-csharp-no): New rules.
+ (install-exec-local): Depend on them.
+ (installdirs-csharp): New rule.
+ (installdirs-local): Depend on it.
+ (uninstall-csharp-yes, uninstall-csharp-no): New rules.
+ (uninstall-local): Depend on it.
+ * Makefile.msvc (msgfmt_OBJECTS): Add write-csharp.obj.
+ (msgunfmt_OBJECTS): Add read-csharp.obj.
+ (write-csharp.obj, read-csharp.obj): New rules.
+ * Makefile.vms (msgfmt_OBJECTS): Add write-csharp.obj.
+ (msgunfmt_OBJECTS): Add read-csharp.obj.
+ (write-csharp.obj, read-csharp.obj): New rules.
+ * FILES: Update.
+
+2003-12-26 Bruno Haible <bruno@clisp.org>
+
+ * read-java.c (msgdomain_read_java): Relocate also the GETTEXTJAR
+ value.
+ * urlget.c (fetch): Likewise.
+
+2003-12-26 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (write_java_code): Emit a static method
+ 'get_msgid_plural_table' instead of a static field 'plural'.
+ * gnu/gettext/DumpResource.java (DumpResource.dump): Exploit a
+ 'get_msgid_plural_table' method if it exists.
+
+2003-12-14 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_csharp'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_csharp entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_csharp): New declaration.
+ * format-csharp.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_csharp.
+ * x-csharp.h: New file.
+ * x-csharp.c: New file.
+ * xgettext.c: Include x-csharp.h.
+ (flag_table_csharp): New variable.
+ (main): Invoke init_flag_table_csharp, x_csharp_extract_all,
+ x_csharp_keyword.
+ (usage): Mention C# source language.
+ (xgettext_record_flag): Handle format_csharp.
+ (language_to_extractor): Add C# rule.
+ (extension_to_language): Add C# rule.
+ * Makefile.am (noinst_HEADERS): Add x-csharp.h.
+ (FORMAT_SOURCE): Add format-csharp.c.
+ (xgettext_SOURCES): Add x-csharp.c.
+ * Makefile.msvc (OBJECTS): Add format-csharp.obj.
+ (xgettext_OBJECTS): Add x-csharp.obj.
+ (format-csharp.obj, x-csharp.obj): New rules.
+ * Makefile.vms (OBJECTS): Add format-csharp.obj.
+ (xgettext_OBJECTS): Add x-csharp.obj.
+ (format-csharp.obj, x-csharp.obj): New rules.
+ * FILES: Update.
+
+2003-12-28 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Pass ignore_sigpipe = false.
+ * msgfilter.c (process_string): Likewise.
+ * msggrep.c (is_string_selected): Likewise.
+ * msginit.c (project_id, project_id_version, get_user_email,
+ language_team_address): Likewise.
+ * read-java.c (execute_and_read_po_output): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * urlget.c (execute_it): Pass ignore_sigpipe = true.
+ (fetch): Pass ignore_sigpipe = true when fetching the file, = false
+ otherwise.
+
+2003-12-14 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase2_ungetc): Likewise.
+ (phase3_ungetc): Likewise.
+ (phase5_unget): Likewise.
+ (phase6_unget): Likewise.
+ * x-java.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase2_ungetc): Likewise.
+ (phase3_ungetc): Likewise.
+ (phase5_unget): Likewise.
+ (phase6_unget): Likewise.
+ * x-php.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase2_ungetc): Likewise.
+ (phase3_ungetc): Likewise.
+ * x-python.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase5_unget): Likewise.
+ * x-sh.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase2_ungetc): Likewise.
+ * x-smalltalk.c (SIZEOF): New macro.
+ (phase2_unget): Verify that the pushback buffer's size is sufficient.
+ * x-tcl.c (SIZEOF): New macro.
+ (phase1_ungetc): Verify that the pushback buffer's size is sufficient.
+ (phase2_ungetc): Likewise.
+ * x-ycp.c (SIZEOF): New macro.
+ (phase2_unget): Verify that the pushback buffer's size is sufficient.
+
+2003-12-14 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (phase1_pushback): Reduce size to 2.
+ (phase2_pushback): Reduce size to 1.
+ (phase3_pushback): Reduce size to 2.
+ (phase5_pushback): Reduce size to 1.
+ (phase6_pushback): Reduce size to 2.
+ * x-java.c (phase2_pushback): Reduce size to 1.
+ (phase3_pushback): Reduce size to 2.
+ (phase5_pushback): Reduce size to 3.
+ (phase6_pushback): Reduce size to 2.
+ * x-php.c (phase1_pushback): Reduce size to 2.
+ * x-python.c (max): New macro.
+ (phase1_pushback): Reduce size to UNINAME_MAX + 3.
+ (phase5_pushback): Reduce size to 1.
+ * x-sh.c (phase1_pushback): Reduce size to 1.
+ (phase2_pushback): Reduce size to 2.
+ * x-smalltalk.c (phase2_pushback): Reduce size to 1.
+ * x-tcl.c (phase1_pushback): Reduce size to 1.
+ (phase2_pushback): Reduce size to 2.
+
+2003-12-12 Bruno Haible <bruno@clisp.org>
+
+ Assume automake-1.8.
+ * Makefile.am (install-exec-local): Renamed from install-exec-am.
+
+2003-11-30 Bruno Haible <bruno@clisp.org>
+
+ * format-java.c: Include xallocsa.h.
+ (message_format_parse): Use xallocsa instead of alloca.
+ (choice_format_parse): Likewise.
+ * msgl-cat.c: Include xallocsa.h.
+ (catenate_msgdomain_list): Use xallocsa instead of alloca.
+ * msgl-charset.c: Include xallocsa.h.
+ (compare_po_locale_charsets): Use xallocsa instead of alloca.
+ * msgl-iconv.c: Include xallocsa.h.
+ (iconv_message_list): Use xallocsa instead of alloca.
+ * po-charset.c: Include xallocsa.h.
+ (po_lex_charset_set): Use xallocsa instead of alloca.
+ * read-tcl.c: Include xallocsa.h.
+ (msgdomain_read_tcl): Use xallocsa instead of alloca.
+ * write-java.c: Include xallocsa.h.
+ (compute_hashsize, msgdomain_write_java): Use xallocsa instead of
+ alloca.
+ * write-mo.c: Include xallocsa.h.
+ (write_table): Use xallocsa instead of alloca.
+ * write-po.c: Include xallocsa.h.
+ (msgdomain_list_print_po): Use xallocsa instead of alloca.
+ * write-tcl.c: Include xallocsa.h.
+ (msgdomain_write_tcl): Use xallocsa instead of alloca.
+ * msggrep.c: Include xallocsa.h.
+ (is_message_selected): Use xallocsa instead of alloca.
+ * msginit.c: Include xallocsa.h.
+ (subst_string): Use xallocsa instead of alloca.
+ * msgmerge.c: Include obstack.h.
+ (obstack_chunk_alloc, obstack_chunk_free): New macros.
+ (message_merge): Use obstack_alloc instead of alloca.
+ * xgettext.c: Include xallocsa.h.
+ (flag_context_list_table_insert): Use xallocsa instead of alloca.
+
+2003-12-17 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.13.1 released.
+
+2003-12-14 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.c (phase2_getc): Call phase1_ungetc instead of phase2_ungetc:
+ The next phase2_getc invocation must return QUOTED('"'), not '"'.
+
+2003-12-14 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (phase7_getc): Remove dead code.
+ * x-java.c (accumulate_escaped): More precise error message.
+
+2003-12-02 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Treat sr_CS like sr_YU.
+
+2003-11-30 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.13 released.
+
+2003-11-28 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (set_format_flags_from_context): Add casts, to make it
+ compile in C++ mode.
+
+2003-11-22 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (phase4_getc): Skip leading whitespace in C++ style comments
+ as well.
+ * x-sh.c (read_word): Skip leading whitespace in comments.
+ * x-lisp.c (read_object): Skip leading whitespace in single-line
+ comments as well.
+ * x-elisp.c (read_object): Skip leading whitespace in comments.
+ * x-librep.c (read_object): Skip leading whitespace in single-line
+ comments as well.
+ * x-java.c (phase4_getc): Skip leading whitespace in C++ style comments
+ as well.
+ * x-awk.c (phase2_getc): Skip leading whitespace in comments.
+ * x-ycp.c (phase2_getc): Skip leading whitespace in single-line
+ comments as well.
+ * x-tcl.c (read_command): Skip leading whitespace in comments.
+
+2003-11-24 Bruno Haible <bruno@clisp.org>
+
+ * format-lisp.c (check_params): Use ngettext for one of the messages.
+ Reported by Rafał Maszkowski <rzm@icm.edu.pl>.
+
+2003-11-15 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (AM_CPPFLAGS): Renamed from INCLUDES.
+
+2003-11-09 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (main): Drop the blank line in --no-translator mode.
+
+2003-11-07 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (remember_a_message): Omit the programmer comments of a
+ duplicated msgid only if they are redundant.
+ Reported by Christian Neumair <chris@gnome-de.org>.
+
+2003-11-05 Bruno Haible <bruno@clisp.org>
+
+ * user-email.sh.in: Renamed from user-email.in. Internationalize, use
+ localedir and gettext.
+ * FILES: Update.
+
+2003-10-23 Bruno Haible <bruno@clisp.org>
+
+ * read-stringtable.c (read_string, stringtable_parse): Cast
+ gram_pos.line_number to 'unsigned long'.
+ * x-sh.c (read_word): Case line_number to 'unsigned long'.
+
+2003-10-21 Bruno Haible <bruno@clisp.org>
+
+ * message.h (message_list_msgids_changed): New declaration.
+ * message.c (message_list_msgids_changed): New function.
+ * msgl-iconv.c (iconv_message_list): Update the message list's hash
+ table if it happens to contain non-ASCII msgids.
+
+2003-10-19 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_qt'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_qt entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_qt): New declaration.
+ * format-qt.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_qt.
+ * xgettext.c (recognize_format_qt): New variable.
+ (long_options): Add option "--qt".
+ (main): Handle --qt option. Determine the extractor only after option
+ processing is complete.
+ (usage): Document --qt option.
+ (xgettext_record_flag): Handle format_qt.
+ (language_to_extractor): For C++, return a different extractor when
+ --qt was specified.
+ * write-qt.h: New file.
+ * write-qt.c: New file.
+ * msgfmt.c: Include write-qt.h.
+ (qt_mode): New variable.
+ (long_options): Add option "--qt".
+ (main): Handle --qt option. More checks for contradicting options.
+ Call msgdomain_write_qt.
+ (usage): Mention Qt mode.
+ (format_directive_domain): Ignore domain directive if in Qt mode.
+ * Makefile.am (noinst_HEADERS): Add write-qt.h.
+ (FORMAT_SOURCE): Add format-qt.c.
+ (msgfmt_SOURCES): Add write-qt.c.
+ * Makefile.msvc (OBJECTS): Add format-qt.obj.
+ (msgfmt_OBJECTS): Add write-qt.obj.
+ (format-qt.obj, write-qt.obj): New rules.
+ * Makefile.vms (OBJECTS): Add format-qt.obj.
+ (msgfmt_OBJECTS): Add write-qt.obj.
+ (format-qt.obj, write-qt.obj): New rules.
+ * FILES: Update.
+
+2003-10-14 Bruno Haible <bruno@clisp.org>
+
+ Fix compilation errors in C++ mode.
+ * msgfilter.c (process_string): Cast the xrealloc expression.
+ * xgettext.c (language_to_extractor, extension_to_language): Move the
+ typedef after the struct definition.
+
+2003-10-13 Bruno Haible <bruno@clisp.org>
+
+ Support CR/LF line terminators in C sources even on Unix.
+ * x-c.c (phase0_getc, phase0_ungetc): New functions.
+ (phase1_getc): Use them instead of calling getc/ungetc directly.
+ Reported by Christoph Thielecke <u15119@hs-harz.de>.
+
+2003-10-13 Bruno Haible <bruno@clisp.org>
+
+ Support and recognize Objective C specific format strings.
+ * message.h (enum format_type): New item format_objc.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add an entry for format_objc.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_objc): New declaration.
+ * format-c.c (enum format_arg_type): New item FAT_OBJC_OBJECT.
+ (format_parse): Add objc_extensions argument. Handle %@ in ObjC mode.
+ (format_c_parse, format_objc_parse): New functions.
+ (formatstring_c): Use format_c_parse instead of format_parse.
+ (formatstring_objc): New variable.
+ (get_c99_format_directives): Update.
+ * format.c (formatstring_parsers): Add an entry for format_objc.
+ * write-mo.c (write_table): Look for system dependent strings also in
+ ObjectiveC format strings.
+ * x-c.h (SCANNERS_C): Use separate flag_table for ObjectiveC.
+ (x_objc_keyword, init_flag_table_objc): New declarations.
+ (x_c_any_keywords): Remove declaration.
+ * x-c.c (c_keywords): Renamed from keywords.
+ (objc_keywords): New variable.
+ (add_keyword): Renamed from x_c_keyword. Add keywords table argument.
+ (x_c_keyword, x_objc_keyword): New functions.
+ (x_c_any_keywords): Remove function.
+ (init_keywords): Also initialize ObjectiveC keyword table.
+ (init_flag_table_objc): New function.
+ (enum token_type_ty): New item token_type_colon.
+ (phase5_get): Recognize colon.
+ (enum xgettext_token_type_ty): New item xgettext_token_type_colon.
+ (x_c_lex): Use keywords table depending on objc_extensions. Handle
+ colon.
+ (extract_parenthesized): Change the context_iter and inner_context
+ after a keyword/symbol followed by a colon was seen.
+ * xgettext.c (flag_table_objc): New variable.
+ (main): Invoke init_flag_table_objc, x_objc_keyword. Watch out for
+ keywords arguments, instead of calling x_c_any_keywords().
+ (flag_context_list_table_insert): New function, extracted from
+ xgettext_record_flag.
+ (xgettext_record_flag): Call it. For format_c, insert the flags also
+ in the flag_table_objc. Handle format_objc.
+ (remember_a_message): Don't add a heuristic c-format flag to an entry
+ that already carries objc-format.
+ (remember_a_message_plural): Likewise.
+
+2003-10-18 Bruno Haible <bruno@clisp.org>
+
+ Support for GNUstep .strings format.
+ * read-stringtable.h: New file.
+ * read-stringtable.c: New file.
+ * read-po-abstract.h (enum input_syntax_ty): New item
+ syntax_stringtable.
+ (po_callback_comment_dispatcher): Renamed from po_callback_comment.
+ (po_callback_comment, po_callback_comment_special): New declarations.
+ * read-po-abstract.c: Include read-stringtable.h.
+ (po_scan): Add support for syntax_stringtable.
+ (po_callback_comment_dispatcher): Renamed from po_callback_comment.
+ (po_callback_comment, po_callback_comment_dot): New functions.
+ (po_callback_comment_special): New function.
+ * read-po.c (read_po): Set mdlp->encoding for syntax_stringtable too.
+ * po-gram-gen.y: Update po_callback_comment_dispatcher call.
+ * read-properties.c (properties_parse): Likewise.
+ * write-stringtable.h: New file.
+ * write-stringtable.c: New file.
+ * write-po.h (make_format_description_string, significant_format_p):
+ New declarations.
+ (message_print_syntax_stringtable): New declaration.
+ * write-po.c: Include write-stringtable.h.
+ (make_format_description_string, significant_format_p): Make
+ non-static.
+ (use_syntax_stringtable): New variable.
+ (message_print_syntax_stringtable): New function.
+ (msgdomain_list_print): Add check for plural forms for
+ syntax_stringtable too. Add support for writing the .strings format.
+ * msgattrib.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgcat.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. If option --stringtable-output is used, convert to
+ UTF-8 and ignore the to_code.
+ (usage): Document options --stringtable-input/output.
+ * msgcmp.c (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msgcomm.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgconv.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. If option --stringtable-output is used, ignore the
+ to_code.
+ (usage): Document options --stringtable-input/output.
+ * msgen.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgexec.c: Include limits.h.
+ (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msgfilter.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgfmt.c (long_options): Add --stringtable-input.
+ (main): Handle it.
+ (usage): Document option --stringtable-input.
+ * msggrep.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msginit.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * msgmerge.c (long_options): Add --stringtable-input/output.
+ (main): Handle them. In update mode, --stringtable-input implies
+ --stringtable-output.
+ (usage): Document options --stringtable-input/output.
+ * msgunfmt.c (long_options): Add --stringtable-output.
+ (main): Handle it.
+ (usage): Document option --stringtable-output.
+ * msguniq.c (long_options): Add --stringtable-input/output.
+ (main): Handle them.
+ (usage): Document options --stringtable-input/output.
+ * x-stringtable.h: New file.
+ * x-po.c: Include x-stringtable.h.
+ (extract_stringtable): New function.
+ * xgettext.c: Include x-stringtable.h.
+ (long_options): Add --stringtable-output.
+ (main): Handle it.
+ (usage): Document options -L NXStringTable and --stringtable-output.
+ (finalize_header): If --stringtable-output was given, set the charset.
+ (language_to_extractor): Add support for .strings format.
+ (extension_to_language): Likewise.
+ * Makefile.am (noinst_HEADERS): Add read-stringtable.h,
+ write-stringtable.h, x-stringtable.h.
+ (COMMON_SOURCE): Add read-stringtable.c.
+ (libgettextsrc_la_SOURCES): Add write-stringtable.c.
+ * Makefile.msvc (OBJECTS): Add read-stringtable.obj,
+ write-stringtable.obj.
+ (read-stringtable.obj, write-stringtable.obj): New rules.
+ * Makefile.vms (OBJECTS): Add read-stringtable.obj,
+ write-stringtable.obj.
+ (read-stringtable.obj, write-stringtable.obj): New rules.
+ * FILES: Update.
+
+2003-10-21 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (xgethostname): Add support for native Woe32 API.
+
+2003-10-21 Bruno Haible <bruno@clisp.org>
+
+ * message.c (message_list_search): Avoid casting a pointer to a local
+ variable; it violated strict aliasing.
+
+2003-10-21 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c (mkdir): Redefine on mingw.
+
+2003-10-12 Bruno Haible <bruno@clisp.org>
+
+ Improved ObjectiveC support.
+ * x-c.h (extract_objc): New declaration.
+ (SCANNERS_C): For ObjectiveC, use extract_objc.
+ * x-c.c (objc_extensions): New variable.
+ (enum token_type_ty): New item token_type_objc_special.
+ (phase5_get): Recognize '@' for ObjectiveC.
+ (phase8b_get, phase8b_unget): New functions. Handle
+ token_type_white_space and token_type_eoln here instead of in x_c_lex.
+ (phase8c_get, phase8c_unget): New functions.
+ (phase8_get): Rely on phase8c, not phase8a. No more need to care about
+ token_type_white_space and token_type_eoln.
+ (x_c_lex): Drop handling of token_type_white_space and token_type_eoln.
+ (extract_whole_file): Renamed from extract_c.
+ (extract_c, extract_objc): New functions.
+
+2003-10-20 Bruno Haible <bruno@clisp.org>
+
+ * x-java.c (phase3_getc): Fix typo: Use phase2_ungetc, not phase2_getc.
+
+2003-10-20 Bruno Haible <bruno@clisp.org>
+
+ Portability to AIX with cc.
+ * xgettext.h (struct flag_context_ty): Define the bitfields of type
+ 'unsigned int'.
+
+2003-10-20 Bruno Haible <bruno@clisp.org>
+
+ Portability to Solaris with cc.
+ * x-java.c (string_buffer_append): Avoid aggregate initializer
+ containing non-constant expressions.
+ * xgettext.c (flag_context_list_iterator): Likewise.
+ (language_to_extractor): Likewise.
+
+2003-10-11 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (main): Drop suffix ".in" from input file name, if
+ present, before looking at the file extension.
+
+2003-10-09 Bruno Haible <bruno@clisp.org>
+
+ * format-awk.c: Include xalloc.h instead of xmalloc.h.
+ * format-c.c: Likewise.
+ * format-elisp.c: Likewise.
+ * format-gcc-internal.c: Likewise.
+ * format-java.c: Likewise.
+ * format-librep.c: Likewise.
+ * format-lisp.c: Likewise.
+ * format-pascal.c: Likewise.
+ * format-perl.c: Likewise.
+ * format-perl-brace.c: Likewise.
+ * format-php.c: Likewise.
+ * format-python.c: Likewise.
+ * format-sh.c: Likewise.
+ * format-tcl.c: Likewise.
+ * format-ycp.c: Likewise.
+ * gettext-po.c: Likewise.
+ * hostname.c: Likewise.
+ * message.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgfilter.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-english.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * msgmerge.c: Likewise.
+ * open-po.c: Likewise.
+ * po-gram-gen.y: Likewise.
+ * po-hash-gen.y: Likewise.
+ * po-lex.c: Likewise.
+ * read-mo.c: Likewise.
+ * read-po-abstract.c: Likewise.
+ * read-po.c: Likewise.
+ * read-properties.c: Likewise.
+ * str-list.c: Likewise.
+ * write-java.c: Likewise.
+ * write-mo.c: Likewise.
+ * write-po.c: Likewise.
+ * write-properties.c: Likewise.
+ * write-tcl.c: Likewise.
+ * x-awk.c: Likewise.
+ * x-c.c: Likewise.
+ * x-elisp.c: Likewise.
+ * x-glade.c: Likewise.
+ * x-java.c: Likewise.
+ * x-librep.c: Likewise.
+ * x-lisp.c: Likewise.
+ * x-perl.c: Likewise.
+ * x-php.c: Likewise.
+ * x-po.c: Likewise.
+ * x-python.c: Likewise.
+ * x-rst.c: Likewise.
+ * x-sh.c: Likewise.
+ * x-smalltalk.c: Likewise.
+ * x-tcl.c: Likewise.
+ * x-ycp.c: Likewise.
+ * xgettext.c: Likewise.
+
+2003-10-07 Bruno Haible <bruno@clisp.org>
+
+ More reliable subprocess cleanup.
+ * msgexec.c (process_string): Pass slave_process = true to
+ create_pipe_out() and wait_subprocess().
+ * msgfilter.c (process_string): Pass slave_process = true to
+ create_pipe_bidi() and wait_subprocess().
+ * msggrep.c (is_string_selected): Pass slave_process = true to
+ create_pipe_out() and wait_subprocess().
+ * msginit.c (project_id, project_id_version, get_user_email,
+ language_team_address): Pass slave_process = true to create_pipe_in()
+ and wait_subprocess().
+ * read-java.c (execute_and_read_po_output): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * urlget.c (execute_it, fetch): Pass slave_process = true to execute().
+
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * write-java.c: Include fatal-signal.h, not signal.h.
+ (uninstall_handlers): Remove function.
+ (cleanup): Remove signal argument. Don't execute the signal's default
+ action; leave that to the caller.
+ (install_handlers, init_signal_set, block, unblock): Remove functions.
+ (msgdomain_write_java): Invoke at_fatal_signal instead of
+ install_handlers/uninstall_handlers. Invoke [un]block_fatal_signals
+ instead of [un]block.
+
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (substring_match): Remove variable.
+ (long_options): Remove --keyword-substring option.
+ (main): Remove handling of --keyword-substring option.
+
+2003-10-05 Bruno Haible <bruno@clisp.org>
+
+ * message.h (enum is_format): New item yes_according_to_context.
+ * message.c (possible_format_p): Handle also yes_according_to_context.
+ * write-po.c (make_format_description_string): Likewise.
+
+ * xgettext.h (struct flag_context_ty): New type.
+ (null_context, passthrough_context): New declarations.
+ (inherited_context): New declaration.
+ (struct flag_context_list_ty): New type.
+ (struct flag_context_list_iterator_ty): New type.
+ (null_context_list_iterator, passthrough_context_list_iterator): New
+ declarations.
+ (flag_context_list_iterator): New declaration.
+ (flag_context_list_iterator_advance): New declaration.
+ (flag_context_list_table_ty): New type.
+ (flag_context_list_table_lookup): New declaration.
+ (xgettext_record_flag): New declaration.
+ (remember_a_message, remember_a_message_plural): Add context argument.
+ * xgettext.c: Include alloca.h.
+ (flag_table_c, flag_table_gcc_internal, flag_table_sh,
+ flag_table_python, flag_table_lisp, flag_table_elisp,
+ flag_table_librep, flag_table_java, flag_table_awk, flag_table_ycp,
+ flag_table_tcl, flag_table_perl, flag_table_php): New variables.
+ (long_options): Add option --flag.
+ (extractor_func): Add argument flag_table.
+ (struct extractor_ty): New type.
+ (main): Use type 'extractor_ty' instead of 'extractor_func'.
+ Invoke init_flag_table_c(), init_flag_table_gcc_internal(),
+ init_flag_table_sh(), init_flag_table_python(), init_flag_table_lisp(),
+ init_flag_table_elisp(), init_flag_table_librep(),
+ init_flag_table_java(), init_flag_table_awk(), init_flag_table_ycp(),
+ init_flag_table_tcl(), init_flag_table_perl(), init_flag_table_php().
+ Implement option --flag.
+ (usage): Rename a section to "Language specific options". Document
+ the languages to which --extract-all, --keyword, --trigraphs are
+ applicable. Document option --flag.
+ (null_context): New variable.
+ (passthrough_context): New variable.
+ (inherited_context): New function.
+ (null_context_list_iterator): New variable.
+ (passthrough_context_circular_list, passthrough_context_list_iterator):
+ New variables.
+ (flag_context_list_iterator): New function.
+ (flag_context_list_iterator_advance): New function.
+ (flag_context_list_table_lookup): New function.
+ (xgettext_record_flag): New function.
+ (extract_from_file): Change argument type to 'extractor_ty' instead of
+ 'extractor_func'. Set current_formatstring_parser{1,2} before invoking
+ the extractor.
+ (set_format_flags_from_context): New function.
+ (remember_a_message): Add context argument. Set some *-format flag if
+ the context specifies it.
+ (remember_a_message_plural): Likewise.
+ (language_to_extractor): Change return type to 'extractor_ty' instead
+ of 'extractor_func'.
+
+ * x-awk.h (SCANNERS_AWK): Refer to flag_table_awk.
+ (extract_awk): Add argument flag_table.
+ (init_flag_table_awk): New declaration.
+ * x-awk.c (init_flag_table_awk): New function.
+ (enum token_type_ty): New enum item token_type_semicolon.
+ (x_awk_lex): Recognize semicolon.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('. Also
+ recognize argument lists that start without '(', but only up to the
+ next semicolon.
+ (extract_awk): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-c.h (SCANNERS_C): Refer to flag_table_c, flag_table_gcc_internal.
+ (extract_c): Add argument flag_table.
+ (init_flag_table_c): New declaration.
+ (init_flag_table_gcc_internal): New declaration.
+ * x-c.c (init_flag_table_c): New function.
+ (init_flag_table_gcc_internal): New function.
+ (enum xgettext_token_type_ty): New item xgettext_token_type_other.
+ (x_c_lex): For token_type_name, put the string into the resulting
+ token instead of freeing it. Return token type
+ xgettext_token_type_other instead of xgettext_token_type_symbol in
+ some cases.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('.
+ (extract_c): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-elisp.h (SCANNERS_ELISP): Refer to flag_table_elisp.
+ (extract_elisp): Add argument flag_table.
+ (init_flag_table_elisp): New declaration.
+ * x-elisp.c (init_flag_table_elisp): New function.
+ (flag_context_list_table): New variable.
+ (read_object): Add argument outer_context. Implement context handling
+ depending on first symbol after '('.
+ (extract_elisp): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-glade.h (SCANNERS_GLADE): Update.
+ (extract_glade): Add argument flag_table.
+ * x-glade.c (start_element_handler, end_element_handler): Pass null
+ context to remember_a_message.
+ (extract_glade): Add argument flag_table.
+
+ * x-java.h (SCANNERS_JAVA): Refer to flag_table_java.
+ (extract_java): Add argument flag_table.
+ (init_flag_table_java): New declaration.
+ * x-java.c (init_flag_table_java): New function.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('.
+ (extract_java): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-librep.h (SCANNERS_ELISP): Refer to flag_table_librep.
+ (extract_librep): Add argument flag_table.
+ (init_flag_table_librep): New declaration.
+ * x-librep.c (init_flag_table_librep): New function.
+ (flag_context_list_table): New variable.
+ (read_object): Add argument outer_context. Implement context handling
+ depending on first symbol after '('.
+ (extract_librep): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-lisp.h (SCANNERS_LISP): Refer to flag_table_lisp.
+ (extract_lisp): Add argument flag_table.
+ (init_flag_table_lisp): New declaration.
+ * x-lisp.c (init_flag_table_lisp): New function.
+ (flag_context_list_table): New variable.
+ (read_object): Add argument outer_context. Implement context handling
+ depending on first symbol after '('.
+ (extract_lisp): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-perl.h (SCANNERS_PERL): Refer to flag_table_perl.
+ (extract_perl): Add argument flag_table.
+ (init_flag_table_perl): New declaration.
+ * x-perl.c (init_flag_table_java): New function.
+ (flag_context_list_table): New variable.
+ (extract_variable): Update. Implement context handling depending on
+ symbol before '{'...'}'.
+ (interpolate_keywords): Implement context handling depending on symbol
+ before '->' or '{'...'}'.
+ (extract_balanced): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('. Also
+ recognize argument lists that start without '('.
+ (extract_perl): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-php.h (SCANNERS_PHP): Refer to flag_table_php.
+ (extract_php): Add argument flag_table.
+ (init_flag_table_php): New declaration.
+ * x-php.c (init_flag_table_php): New function.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('.
+ (extract_php): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-po.h (SCANNERS_PO): Update.
+ (extract_po): Add argument flag_table.
+ * x-properties.h (SCANNERS_PROPERTIES): Update.
+ (extract_properties): Add argument flag_table.
+ * x-po.c (extract_po): Add argument flag_table.
+ (extract_properties): Add argument flag_table.
+
+ * x-python.h (SCANNERS_PYTHON): Refer to flag_table_python.
+ (extract_python): Add argument flag_table.
+ (init_flag_table_python): New declaration.
+ * x-python.c (init_flag_table_python): New function.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): Add arguments outer_context, context_iter.
+ Implement context handling depending on symbol before '('.
+ (extract_python): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-rst.h (SCANNERS_RST): Update.
+ (extract_rst): Add argument flag_table.
+ * x-rst.c (extract_rst): Add argument flag_table.
+
+ * x-sh.h (SCANNERS_SH): Refer to flag_table_sh.
+ (extract_sh): Add argument flag_table.
+ (init_flag_table_sh): New declaration.
+ * x-sh.c (init_flag_table_sh): New function.
+ (flag_context_list_table): New variable.
+ (read_word): Add context argument.
+ (read_command): Add outer_context argument. Implement context handling
+ depending on first symbol of command.
+ (read_command_list): Add outer_context argument.
+ (extract_sh): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-smalltalk.h (SCANNERS_SMALLTALK): Update.
+ (extract_smalltalk): Add argument flag_table.
+ * x-smalltalk.c (extract_smalltalk): Add argument flag_table.
+
+ * x-tcl.h (SCANNERS_TCL): Refer to flag_table_tcl.
+ (extract_tcl): Add argument flag_table.
+ (init_flag_table_tcl): New declaration.
+ * x-tcl.c (init_flag_table_tcl): New function.
+ (flag_context_list_table): New variable.
+ (accumulate_word): Add context argument.
+ (read_word): Add context argument.
+ (read_command): Add outer_context argument. Implement context handling
+ depending on first symbol of command.
+ (read_command_list): Add outer_context argument.
+ (extract_tcl): Add argument flag_table. Initialize
+ flag_context_list_table.
+
+ * x-ycp.h (SCANNERS_YCP): Refer to flag_table_ycp.
+ (extract_ycp): Add argument flag_table.
+ (init_flag_table_ycp): New declaration.
+ * x-ycp.c (init_flag_table_ycp): New function.
+ (flag_context_list_table): New variable.
+ (extract_parenthesized): New function, split off from extract_ycp.
+ Implement context handling depending on symbol before '('.
+ (extract_ycp): Add argument flag_table. Initialize
+ flag_context_list_table. Call extract_parenthesized to do the work.
+
+ * po-lex.c (mbfile_getc, control_sequence): Remove explicit marking of
+ strings as c-format, now done by xgettext.
+
+2003-09-23 Bruno Haible <bruno@clisp.org>
+
+ * x-awk.c (extract_parenthesized): Remove optimization of the
+ extract_all case.
+ * x-c.c (extract_parenthesized): Likewise.
+ * x-elisp.c (read_object): Likewise.
+ * x-librep.c (read_object): Likewise.
+ * x-lisp.c (read_object): Likewise.
+ * x-perl.c (x_perl_prelex, extract_balanced): Likewise.
+ * x-php.c (extract_parenthesized): Likewise.
+ * x-python.c (extract_parenthesized): Likewise.
+ * x-sh.c (read_command): Likewise.
+
+2003-10-08 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (main): Make option -N work.
+ Reported by Liu Garfield <jackliu9999@hotmail.com>.
+
+2003-10-04 Bruno Haible <bruno@clisp.org>
+
+ New Java backend.
+ * x-java.c: New file.
+ * x-java.l: Remove file.
+ * Makefile.am (xgettext_SOURCES): Add x-java.c, remove x-java.l.
+ (x-java.c): Remove rule.
+ * FILES: Update.
+
+2003-09-22 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.c (start_element_handler): Implement extract_all behaviour.
+
+2003-10-04 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.c (do_extract_glade): Initialize stack_depth.
+ * x-perl.c (extract_perl): Initialize linesize and linepos.
+
+ * x-python.c (phase2_getc): Set last_comment_line.
+ (phase5_get): Set last_non_comment_line.
+
+2003-10-04 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.c: Don't include msgl-ascii.h and msgl-iconv.h.
+ * x-python.c: Likewise.
+ * x-tcl.c: Likewise.
+
+2003-10-06 Guido Flohr <guido@imperia.net>
+ Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (enum symbol_type_ty): New enum.
+ (struct token_ty): Rename field string_type to sub_type and change its
+ type to 'int'.
+ (extract_quotelike_pass3, interpolate_keywords): Update.
+ (x_perl_prelex): Set sub_type also for symbol tokens.
+ (x_perl_lex): Special handling of prototype argument lists.
+ Reported by Crispin Flowerday <cflowerday@zeus.com>.
+
+2003-09-22 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.c (read_word): Warn about $"...".
+
+2003-09-18 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h (po_file_domain_header, po_header_field,
+ po_message_is_obsolete, po_message_is_fuzzy, po_message_is_format): New
+ declarations.
+ * gettext-po.c (po_file_domain_header, po_header_field,
+ po_message_is_obsolete, po_message_is_fuzzy, po_message_is_format): New
+ functions.
+ * Makefile.am (LTV_CURRENT, LTV_REVISION, LTV_AGE): Bump to 1:0:1.
+
+2003-09-14 Bruno Haible <bruno@clisp.org>
+
+ * plural-count.c: Include plural-count.h.
+ * x-po.c: Include x-properties.h.
+ * x-java.l (append_strings): Change argument type to 'const char *'.
+
+2003-09-14 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_plural_eval): Mark some strings as c-format.
+ (msgfmt_add_message): Make static.
+ * po-lex.c (mbfile_getc, control_sequence): Mark some strings as
+ c-format.
+
+2003-09-18 Bruno Haible <bruno@clisp.org>
+
+ * FILES: Update.
+
+2003-09-14 Bruno Haible <bruno@clisp.org>
+
+ More reliable checking for read errors.
+ * po-lex.c (mbfile_getc): Diagnose read errors also in the middle of
+ multibyte characters.
+ (lex_getc): Diagnose read errors also right after backslash.
+ * x-rst.c (extract_rst): Diagnose read errors also inside ConstName
+ and immediately after #.
+
+2003-09-14 Bruno Haible <bruno@clisp.org>
+
+ * write-mo.c: Include fwriterror.h.
+ (msgdomain_write_mo): Use fwriteerror, to get right errno value in
+ error message about write error.
+ * write-po.c: Include fwriterror.h.
+ (msgdomain_list_print): Use fwriteerror, to get right errno value in
+ error message about write error.
+ * write-java.c: Include fwriterror.h.
+ (msgdomain_write_java): Use fwriteerror, to get right errno value in
+ error message about write error.
+ * write-tcl.c: Include fwriterror.h.
+ (msgdomain_write_tcl): Use fwriteerror, to get right errno value in
+ error message about write error.
+
+2003-09-18 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (from_current_source_encoding): Use multiline_error,
+ for a prettier error message.
+
+ * x-perl.c (extract_quotelike_pass1): Store counter_delim instead of
+ EOF as delimiter, to avoid error during from_current_source_encoding.
+ Reported by Crispin Flowerday <cflowerday@zeus.com>.
+
+2003-09-13 Bruno Haible <bruno@clisp.org>
+
+ * format-sh.c (INVALID_SHELL_SYNTAX,
+ INVALID_CONTEXT_DEPENDENT_VARIABLE): New macros.
+ (format_parse): Reject constructs like ${variable-default} and $?.
+
+2003-09-13 Bruno Haible <bruno@clisp.org>
+
+ Fix behaviour of "<program> --help > /dev/full".
+ * hostname.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgattrib.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgcat.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgcmp.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgcomm.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgconv.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgen.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgexec.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgfilter.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgfmt.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msggrep.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msginit.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgmerge.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msgunfmt.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * msguniq.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * urlget.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * xgettext.c: Include closeout.h.
+ (main): Register close_stdout for execution at program exit.
+ * write-po.c (msgdomain_list_print): Don't fclose (stdout) here.
+
+2003-09-11 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c (mbfile_getc): Handle unexpected return value of
+ u8_mbtouc(). Reported by Jochen Hein <jochen@jochen.org>.
+
+2003-09-09 Bruno Haible <bruno@clisp.org>
+
+ * read-properties.c (phase4_getuc): Cast line_number to 'unsigned long'
+ before outputting it.
+
+2003-09-09 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c: Test HAVE_DECL_GETC_UNLOCKED instead of HAVE_GETC_UNLOCKED.
+ * write-po.c: Test HAVE_DECL_PUTC_UNLOCKED instead of
+ HAVE_PUTC_UNLOCKED.
+
+2003-09-09 Guido Flohr <guido@imperia.net>
+
+ * x-perl.c (extract_quotelike_pass3): Change \l handling to no longer
+ eat up non-uppercase characters; likewise for \u. Also make \l and \u
+ work on characters written using hex or octal escape sequence. Fix \Q
+ handling. With option --extract-all, avoid "invalid variable
+ interpolation" warnings.
+ (x_perl_prelex): With option --extract-all, keep variables inside
+ double-quoted strings untouched.
+
+2003-09-03 Bruno Haible <bruno@clisp.org>
+
+ * x-sh.h: New file.
+ * x-sh.c: New file.
+ * xgettext.c: Include x-sh.h.
+ (main): Call x_sh_extract_all, x_sh_keyword.
+ (usage): Mention Shell language.
+ (language_to_extractor): Add Shell rule.
+ (extension_to_language): Add Shell rule.
+ * Makefile.am (noinst_HEADERS): Add x-sh.h.
+ (xgettext_SOURCES): Add x-sh.c.
+ * Makefile.msvc (xgettext_OBJECTS): Add x-sh.obj.
+ (x-sh.obj): New rule.
+ * Makefile.vms (xgettext_OBJECTS): Add x-sh.obj.
+ (x-sh.obj): New rule.
+
+2003-08-31 Bruno Haible <bruno@clisp.org>
+
+ * format-sh.c: New file.
+ * message.h (format_sh): New enum value.
+ (NFORMATS): Increment.
+ * format.h (formatstring_sh): New declaration.
+ * format.c (formatstring_parsers): Add entry for sh.
+ * message.c (format_language, format_language_pretty): Likewise.
+ * Makefile.am (FORMAT_SOURCE): Add format-sh.c.
+ * Makefile.msvc (OBJECTS): Add format-sh.obj.
+ (format-sh.obj): New rule.
+ * Makefile.vms (OBJECTS): Add format-sh.obj.
+ (format-sh.obj): New rule.
+
+2003-08-31 Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c (isdigit): Remove unused macro.
+ * format-perl-brace.c (isdigit): Likewise.
+
+2003-09-01 Guido Flohr <guido@imperia.net>
+
+ * x-perl.c (extract_balanced): Access the global last_token.
+ Initialize last_token and prefer_division_over_regexp here.
+ (extract_perl): Not here.
+
+2003-08-29 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c: Include getline.h.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (format.obj, msgmerge.obj, xgettext.obj, msgattrib.obj,
+ msgcat.obj, msgcomm.obj, msgconv.obj, msgen.obj, msgfilter.obj,
+ msggrep.obj, msguniq.obj): Compile these in C++ mode. This works around
+ the "non-constant initializer" error that we get for addresses of
+ variables in DLLs in C mode.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c (process_message): Cast xrealloc() result.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: On Windows, include <io.h> instead of <unistd.h>.
+ * msggrep.c: Likewise.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * plural-count.h: New file.
+ * plural-count.c: New file, extracted from msgmerge.c.
+ * msgmerge.c: Include plural-count.h instead of plural-exp.h.
+ (match_domain): Use function get_plural_count().
+ * Makefile.am (noinst_HEADERS): Add plural-count.h.
+ (msgmerge_SOURCES): Add plural-count.c.
+ * Makefile.msvc (msgmerge_OBJECTS): Add plural-count.obj.
+ (plural-count.obj): New rule.
+ * Makefile.vms (msgmerge_OBJECTS): Add plural-count.obj.
+ (plural-count.obj): New rule.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ Support for building DLLs on Windows.
+ * format.h (formatstring_c, formatstring_python, formatstring_lisp,
+ formatstring_elisp, formatstring_librep, formatstring_smalltalk,
+ formatstring_java, formatstring_awk, formatstring_pascal,
+ formatstring_ycp, formatstring_tcl, formatstring_perl,
+ formatstring_perl_brace, formatstring_php, formatstring_gcc_internal,
+ formatstring_parsers): Add DLL_VARIABLE attribute.
+ * message.h (format_language, format_language_pretty): Likewise.
+ * msgl-cat.h (more_than, less_than, use_first, msgcomm_mode,
+ omit_header): Likewise.
+ * plural-table.h (plural_table, plural_table_size): Likewise.
+ * po-charset.h (po_charset_ascii, po_charset_utf8, po_lex_charset,
+ po_lex_iconv, po_lex_weird_cjk): Likewise.
+ * po-lex.h (gram_pos, gram_pos_column, gram_max_allowed_errors,
+ pass_obsolete_entries): Likewise.
+ * read-po.h (line_comment, allow_duplicates, input_syntax): Likewise.
+ * write-mo.h (alignment, no_hash_table): Likewise.
+ * Makefile.am (po-gram-gen2.h): Likewise.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (gettext-po.obj, gettextpo.lib, gettextpo.res): New
+ rules.
+ (all): Depend on gettextpo.lib.
+ (install, installdirs, uninstall): Install/uninstall gettext-po.h,
+ gettextpo.dll and gettextpo.lib.
+ * Makefile.vms (gettext-po.obj, gettextpo.olb): New rules.
+ (all): Depend on gettextpo.olb.
+ (install, installdirs, uninstall): Install/uninstall gettext-po.h,
+ gettextpo.olb.
+
+2003-08-24 Bruno Haible <bruno@clisp.org>
+
+ * dir-list.h: Make this file includable in C++ mode: add extern "C".
+ * file-list.h: Likewise.
+ * format.h: Likewise.
+ * message.h: Likewise.
+ * msgl-ascii.h: Likewise.
+ * msgl-cat.h: Likewise.
+ * msgl-charset.h: Likewise.
+ * msgl-english.h: Likewise.
+ * msgl-equal.h: Likewise.
+ * msgl-iconv.h: Likewise.
+ * open-po.h: Likewise.
+ * po-charset.h: Likewise.
+ * po-lex.h: Likewise.
+ * po-time.h: Likewise.
+ * read-po-abstract.h: Likewise.
+ * str-list.h: Likewise.
+ * write-po.h: Likewise.
+ * xgettext.h: Likewise.
+ * read-po.h: Likewise.
+ (this): Redefine to a different symbol.
+ * xgettext.c: Add extern "C" around all "x-*.h" includes.
+
+2003-08-22 Bruno Haible <bruno@clisp.org>
+
+ * format-awk.c: Include error-progname.h instead of progname.h.
+ * format-c.c: Likewise.
+ * format-elisp.c: Likewise.
+ * format-gcc-internal.c: Likewise.
+ * format-java.c: Likewise.
+ * format-librep.c: Likewise.
+ * format-lisp.c: Likewise.
+ * format-pascal.c: Likewise.
+ * format-perl.c: Likewise.
+ * format-perl-brace.c: Likewise.
+ * format-php.c: Likewise.
+ * format-python.c: Likewise.
+ * format-tcl.c: Likewise.
+ * format-ycp.c: Likewise.
+ * po-lex.h: Likewise.
+ * read-properties.c: Likewise.
+ * write-po.c: Likewise.
+ * x-awk.c: Likewise.
+ * x-c.c: Likewise.
+ * x-perl.c: Likewise.
+ * x-python.c: Likewise.
+ * x-rst.c: Likewise.
+ * hostname.c: Include error-progname.h.
+ * msgattrib.c: Likewise.
+ * msgcat.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgen.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgfilter.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * urlget.c: Likewise.
+ * xgettext.c: Likewise.
+ * po-lex.c: Include error-progname.h and pos.h.
+
+2003-08-15 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Add entries for Aragonese,
+ Haitian, Sichuan Yi, Limburgish.
+ (englishname_of_language): Likewise.
+
+2003-08-08 Bruno Haible <bruno@clisp.org>
+
+ * format-gcc-internal.c: New file.
+ * message.h (format_gcc_internal): New enum value.
+ (NFORMATS): Increment.
+ * format.h (formatstring_gcc_internal): New declaration.
+ * format.c (formatstring_parsers): Add entry for gcc_internal.
+ * message.c (format_language, format_language_pretty): Likewise.
+ * x-c.h (SCANNERS_C): Add an entry for GCC-source.
+ * xgettext.c (usage): Mention GCC-source language.
+ * Makefile.am (FORMAT_SOURCE): Add format-gcc-internal.c.
+ * Makefile.msvc (OBJECTS): Add format-gcc-internal.obj.
+ (format-gcc-internal.obj): New rule.
+ * Makefile.vms (OBJECTS): Add format-gcc-internal.obj.
+ (format-gcc-internal.obj): New rule.
+
+2003-08-04 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (extract_quotelike_pass3): Fix \x handling.
+ Reported by Guido Flohr.
+
+2003-07-05 Bruno Haible <bruno@clisp.org>
+
+ * format-perl-brace.c: Renamed from format-perl-bracket.c. Recognize
+ braces instead of brackets.
+ * format.h (formatstring_perl_brace): Renamed from
+ formatstring_perl_bracket.
+ * format.c (formatstring_parsers): Add formatstring_perl_brace, remove
+ formatstring_perl_bracket.
+ * message.h (enum format_type): Add format_perl_brace, remove
+ format_perl_bracket.
+ * message.c (format_language, format_language_pretty): Update.
+ * x-perl.h (SCANNERS_PERL): Update.
+ * Makefile.am (FORMAT_SOURCE): Add format-perl-brace.c, remove
+ format-perl-bracket.c.
+ * Makefile.msvc (OBJECTS): Add format-perl-brace.obj, remove
+ format-perl-bracket.obj.
+ (format-perl-brace.obj): Renamed from format-perl-bracket.obj.
+ * Makefile.vms (OBJECTS): Add format-perl-brace.obj, remove
+ format-perl-bracket.obj.
+ (format-perl-brace.obj): Renamed from format-perl-bracket.obj.
+
+2003-07-03 Bruno Haible <bruno@clisp.org>
+
+ Support for PHP >= 4.2.0.
+ * x-php.c (init_keywords): Add ngettext, dngettext, dcngettext to the
+ built-in keywords.
+ Reported by A. Sopicki <a.sopicki@gmx.de>.
+
+2003-06-27 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h (from_current_source_encoding): New declaraction.
+ * xgettext.c (from_current_source_encoding): New function.
+ (CONVERT_STRING): Use it.
+ * x-perl.c: Include po-charset.h.
+ (get_here_document): Convert each line to UTF-8.
+ (phase2_getc): Convert each comment to UTF-8. Tell xgettext_comment_add
+ to not convert it.
+ (extract_quotelike_pass1_utf8): New function.
+ (extract_quotelike): Use extract_quotelike_pass1_utf8.
+ (extract_triple_quotelike): Likewise.
+ (extract_variable): Tell remember_a_message to not convert the string.
+ (interpolate_keywords): Likewise.
+ (extract_balanced): Tell remember_a_message, remember_a_message_plural
+ to not convert the string.
+
+2003-06-23 Guido Flohr <guido@imperia.net>
+
+ * x-perl.c (extract_quotelike_pass3): Fix handling of doubled
+ backslashes in single-quoted strings.
+
+2003-06-23 Bruno Haible <bruno@clisp.org>
+
+ * format-perl-bracket.c (format_check): Allow additional bracketed
+ items in the msgstr.
+
+2003-06-22 Bruno Haible <bruno@clisp.org>
+
+ * write-properties.c: Don't include exit.h and gettext.h.
+
+2003-06-22 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (phase3_ungetc): Comment out unused function.
+
+2003-06-21 Bruno Haible <bruno@clisp.org>
+
+ * format-perl.c: Complete rewrite.
+ * format-perl-bracket.c: New file.
+ * message.h (format_perl_bracket): New enum value.
+ (NFORMATS): Increment.
+ * format.h (formatstring_perl_bracket): New declaration.
+ * format.c (formatstring_parsers): Add entry for perl_bracket.
+ * message.c (format_language, format_language_pretty): Likewise.
+ * x-perl.h (SCANNERS_PERL): Use formatstring_perl and
+ formatstring_perl_bracket.
+ * Makefile.am (FORMAT_SOURCE): Add format-perl-bracket.c.
+ * Makefile.msvc (OBJECTS): Add format-perl.obj,format-perl-bracket.obj.
+ (xgettext_OBJECTS): Add x-perl.obj.
+ (format-perl.obj, format-perl-bracket.obj, x-perl.obj): New rules.
+ * Makefile.vms (OBJECTS): Add format-perl.obj, format-perl-bracket.obj.
+ (xgettext_OBJECTS): Add x-perl.obj.
+ (format-perl.obj, format-perl-bracket.obj, x-perl.obj): New rules.
+
+ * x-perl.c (extract_quotelike_pass3): Don't give an invalid
+ interpolation error for a backslashed dollar sign.
+
+2003-06-21 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (extract_quotelike_pass3): Fix handling of double backslash.
+
+2003-06-19 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (enum string_type_ty): Remove trailing comma.
+ (interpolate_keywords::enum parser_state): Likewise.
+ (extract_quotelike_pass3): Cast first argument of u8_uctomb().
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * x-awk.c: Reorder definitions.
+ * x-c.c: Reorder definitions.
+ * x-perl.c: Reorder definitions.
+ * x-php.c: Reorder definitions.
+ * x-python.c: Reorder definitions.
+ * x-smalltalk.c: Reorder definitions.
+ * x-ycp.c: Reorder definitions.
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (struct stack_entry): Remove type.
+ (struct stack): Remove type.
+ (struct token_stack_ty): New type.
+ (token_stack): Change type to 'struct token_stack_ty'.
+ (token_stack_dump): Renamed from stack_dump. Update.
+ (token_stack_push): Renamed from stack_unshift. Rewritten.
+ (token_stack_pop): Renamed from stack_shift. Rewritten.
+ (token_stack_peek): Renamed from stack_head. Rewritten.
+ (token_stack_free): Renamed from stack_free. Rewritten.
+ (x_perl_lex, x_perl_unlex, extract_perl): Update.
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (DEBUG_MEMORY): Remove macro.
+ (remember_a_message_debug, remember_a_message_plural_debug,
+ xmalloc_debug, xrealloc_debug, xrealloc_static_debug, xstrdup_debug,
+ free_debug): Remove functions.
+ (xrealloc_static): Remove macro. Use xrealloc instead.
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * x-awk.c (phase2_getc, x_awk_lex): Increase bufmax proportionally.
+ * x-c.c (comment_add, comment_line_end, phase5_get, phase6_get):
+ Likewise.
+ * x-elisp.c (comment_add, comment_line_end): Likewise.
+ * x-librep.c (comment_add, comment_line_end): Likewise.
+ * x-lisp.c (comment_add, comment_line_end): Likewise.
+ * x-php.c (comment_add, comment_line_end, x_php_lex): Likewise.
+ * x-python.c (comment_add, comment_line_end, phase5_get): Likewise.
+ * x-rst.c (extract_rst): Likewise.
+ * x-smalltalk.c (comment_add, comment_line_end, phase2_get): Likewise.
+ * x-tcl.c (comment_add, comment_line_end): Likewise.
+ * x-ycp.c (phase2_getc, x_ycp_lex): Likewise.
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (current_formatstring_parser1,
+ current_formatstring_parser2): New variables, replacing
+ current_formatstring_parser.
+ (remember_a_message, remember_a_message_plural): Try both
+ formatstring parsers.
+ (language_to_extractor): Allow two formatstring parsers per language,
+ not just one.
+ * x-perl.h (SCANNERS_PERL): Add formatstring_c as secondary format
+ string parser.
+ * x-awk.h (SCANNERS_AWK): Update.
+ * x-c.h (SCANNERS_C): Update.
+ * x-elisp.h (SCANNERS_ELISP): Update.
+ * x-glade.h (SCANNERS_GLADE): Update.
+ * x-java.h (SCANNERS_JAVA): Update.
+ * x-librep.h (SCANNERS_LIBREP): Update.
+ * x-lisp.h (SCANNERS_LISP): Update.
+ * x-php.h (SCANNERS_PHP): Update.
+ * x-po.h (SCANNERS_PO): Update.
+ * x-properties.h (SCANNERS_PROPERTIES): Update.
+ * x-python.h (SCANNERS_PYTHON): Update.
+ * x-rst.h (SCANNERS_RST): Update.
+ * x-smalltalk.h (SCANNERS_SMALLTALK): Update.
+ * x-tcl.h (SCANNERS_TCL): Update.
+ * x-ycp.h (SCANNERS_YCP): Update.
+
+2003-06-15 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (case_whitespace): New macro.
+ (is_whitespace): New function.
+ (interpolate_keywords): Add lineno argument. Track newlines to update
+ lineno while processing the string. Use is_whitespace.
+ (extract_triple_quotelike): Update. Use is_whitespace.
+ (extract_variable): Simplify: add final NUL byte only when done. Use
+ is_whitespace.
+ (x_perl_prelex): Use is_whitespace. After reading a here document, use
+ line_number+1, not line_number. Fix buffer overrun in <<XYZ handling.
+ (collect_message): Use is_whitespace.
+ (extract_balanced): Call free_token at the end of the loop, not at the
+ beginning.
+
+2003-06-14 Bruno Haible <bruno@clisp.org>
+
+ * x-perl.c (enum token_type_ty): Reorder, so as to simplify the
+ function free_token().
+ (struct token_ty): Fix comments.
+ (token2string): Add const.
+ (token_stack): Make static.
+ (*_debug, stack_dump): Remove all fflushs.
+ (linebuf, linesize, linepos, linebuf_size, last_token, here_eaten,
+ end_of_file): Make static.
+ (last_string, last_string_finished): Remove unused variables.
+ (phase1_getc, get_here_document, skip_pod): Test getline() result
+ for being < 0, not == EOF.
+ (phase1_ungetc): Signal internal error through abort(), not exit().
+ (get_here_document): Tweak. Increase bufmax proportionally.
+ (phase2_getc): Increase bufmax proportionally.
+ (prefer_division_over_regexp): Fix comment.
+ (extract_hex, extract_oct): Add const. Simplify.
+ (extract_quotelike): Add a safety check.
+ (extract_quotelike_pass1): Increase bufmax proportionally. Fix
+ insufficient memory allocation at "bufpos + len >= bufmax".
+ (extract_quotelike_pass3): Increase bufmax proportionally. Make crs
+ const. After \x{ABC} position crs after the closing brace. Simplify \c
+ code. Reduce memory allocation when \N{ABC} is seen. Fix bug in \l and
+ \u. Fix error message when $ or @ is seen.
+ (extract_variable): Increase bufmax proportionally.
+ (interpolate_keywords): Likewise. Convert c's value to unsigned char.
+ (x_perl_prelex): Increase bufmax proportionally. Compute 4th argument
+ to extract_triple_quotelike completely.
+ (extract_perl): Initialize token_stack correctly.
+
+2003-06-13 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Pass null_stderr=false.
+ * msgfilter.c (process_string): Likewise.
+ * msggrep.c (is_string_selected): Likewise.
+ * read-java.c (execute_and_read_po_output): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * msginit.c (project_id, project_id_version, get_user_email,
+ language_team_address): Return a default value if the subprocess
+ fails, instead of exiting.
+
+2003-06-12 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (noinst_HEADERS): Add x-perl.h.
+ (FORMAT_SOURCE): Add format-perl.c.
+ (xgettext_SOURCES): Add x-perl.c.
+ * message.h (enum format_type): Mention perl between tcl and php.
+ * message.c (format_language, format_language_pretty): Likewise.
+ * format.h (formatstring_perl): Likewise.
+ * format.c (formatstring_parsers): Likewise.
+ * format-perl.c: Use GNU coding style. Prefer xmalloc over xcalloc.
+ * x-perl.h: Assume ANSI C.
+ * x-perl.c: Assume ANSI C. Use GNU coding style. Prefer xmalloc over
+ xcalloc.
+ (token_buf): Remove unused variable.
+ (xcalloc_debug, stack_push, stack_pop): Remove unused functions.
+ * xgettext.c: Mention perl between tcl and php.
+
+2003-06-11 Guido Flohr <guido@imperia.net>
+
+ * message.h (enum format_type): New enum value 'format_perl'.
+ (NFORMATS): Increment.
+ * message.c (format_language, format_language_pretty): Add entry
+ for perl.
+ * format.h (formatstring_perl): New declaration.
+ * format-perl.c: New file.
+ * format.c (formatstring_parsers): Add entry for perl.
+ * x-perl.h: New file.
+ * x-perl.c: New file.
+ * xgettext.c: Include x-perl.h.
+ (main): Call x_perl_extract_all, x_perl_keyword.
+ (usage): Mention Perl language.
+ (language_to_extractor): Add Perl rule.
+ (extension_to_language): Add Perl rule.
+
+2003-06-08 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.vms (LDADD): Take options from .opt files.
+ (libiconv.opt): New rule.
+ (msgcmp.exe, msgfmt.exe, msgmerge.exe, msgunfmt.exe, xgettext.exe,
+ msgattrib.exe, msgcat.exe, msgcomm.exe, msgconv.exe, msgen.exe,
+ msgexec.exe, msgfilter.exe, msggrep.exe, msginit.exe, msguniq.exe):
+ Depend on it.
+ * plural-eval.c: Include "eval-plural.h" without a path, rely on the
+ Makefile's -I options instead.
+ * read-properties.c (conv_from_java): Change type of q to
+ 'unsigned char *'.
+ Reported by Jouk Jansen <joukj@hrem.stm.tudelft.nl>.
+
+2003-05-27 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Correct entry for Slovak.
+ Reported by Marcel Telka <marcel@telka.sk>.
+
+2003-05-24 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.h (EXTENSIONS_GLADE): Also recognize the .glade2 extension.
+ * x-glade.c (start_element_handler): Also extract strings according to
+ Glade 2 conventions.
+
+2003-05-22 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.12.1 released.
+
+2003-05-21 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (main) [Solaris]: Fix syntax error.
+ Reported by Valery Beaud <valery.beaud@art.alcatel.fr>.
+
+2003-05-21 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (output_syntax): New variable.
+ (main): Set it.
+ (finalize_header): If --properties-output was given, set the charset.
+
+2003-05-18 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (DEBUGFLAGS): New variable.
+ (gettextsrc.lib): Use it.
+ (msgcmp.exe, msgfmt.exe, msgmerge.exe, msgunfmt.exe, xgettext.exe,
+ msgattrib.exe, msgcat.exe, msgcomm.exe, msgconv.exe, msgen.exe,
+ msgexec.exe, msgfilter.exe, msggrep.exe, msginit.exe, msguniq.exe):
+ Use it, and MFLAGS too.
+
+2003-05-17 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.12 released.
+
+2003-05-17 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.msvc (msgcmp.exe, msgfmt.exe, msgmerge.exe, msgunfmt.exe,
+ msgattrib.exe, msgcat.exe, msgcomm.exe, msgconv.exe, msgen.exe,
+ msgexec.exe, msgfilter.exe, msggrep.exe, msginit.exe, msguniq.exe):
+ Link with iconv.lib, needed by intl.lib.
+
+2003-05-12 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (F_OK): Provide a fallback value.
+ Reported by Perry Rapp.
+
+2003-05-10 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c (iconv_string): Don't return -1 just because the string
+ is longer than 4 KB.
+ Reported by Denis Barbier <barbier@linuxfr.org>.
+
+2003-05-04 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.h (po_gram_error): Don't use ISO C 99 feature on DEC C.
+ * po-lex.c (po_gram_error): LIkewise.
+
+2003-05-03 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: Include <sys/select.h> also on EMX.
+ Reported by Andreas Buening <andreas.buening@nexgo.de>.
+
+2003-04-26 Bruno Haible <bruno@clisp.org>
+
+ * message.h (msgdomain_list_ty): Add field 'encoding'.
+ * message.c (msgdomain_list_alloc): Initialize it.
+ * read-properties.h: New file.
+ * read-properties.c: New file.
+ * read-po-abstract.h (abstract_po_reader_class_ty): In
+ directive_message field, add force_fuzzy argument.
+ (po_scan_start, po_scan_end): Remove declarations.
+ (input_syntax_ty): New type.
+ (po_callback_message): Add force_fuzzy argument.
+ * read-po-abstract.c (call_directive_message, po_callback_message):
+ Add force_fuzzy argument.
+ (po_scan_start, po_scan_end): Make static.
+ (po_scan): Add support for syntax_properties. Move
+ error_message_count handling to here.
+ (po_callback_message): Move po_lex_charset_set invocation away.
+ * read-po.h (default_po_reader_class_ty): In add_message field, add
+ force_fuzzy argument.
+ (default_directive_message, default_add_message): Add force_fuzzy
+ argument.
+ (inout_syntax): New declaration.
+ * read-po.c (call_add_message): Add force_fuzzy argument.
+ (default_directive_message, default_add_message): Likewise.
+ (input_syntax): New variable.
+ (read_po): Pass input_syntax to po_scan(). Set mdlp->encoding if
+ possible.
+ * po-gram-gen.y: Pass force_fuzzy = false.
+ (do_callback_message): New function. Move po_lex_charset_set
+ invocation to here.
+ * po-lex.c (lex_end): Move error_message_count handling away.
+ * write-properties.h: New file.
+ * write-properties.c: New file.
+ * write-po.h (message_print_syntax_properties): New declaration.
+ * write-po.c (use_syntax_properties): New variable.
+ (message_print_syntax_properties): New function.
+ (msgdomain_list_print_po): New function, extracted from
+ msgdomain_list_print.
+ (msgdomain_list_print): Add check for plural forms. Add support for
+ writing the .properties format.
+ (extract_po): Renamed from extract.
+ * write-java.h (msgdomain_write_java): Add canon_encoding argument.
+ * write-java.c (msgdomain_write_java): Likewise.
+ * write-tcl.h (msgdomain_write_tcl): Likewise.
+ * write-tcl.c (msgdomain_write_tcl): Likewise.
+ * msgl-cat.c (catenate_msgdomain_list): Use mdlp->encoding if there
+ is no header entry. Set total_mdlp->encoding if possible.
+ * msgl-iconv.c (iconv_msgdomain_list): Use mdlp->encoding if there
+ is no header entry. Set mdlp->encoding.
+ * msgattrib.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msgcat.c (long_options): Add --properties-input/output.
+ (main): Handle them. If option -p is used, convert to UTF-8 and
+ ignore the to_code.
+ (usage): Document options -P and -p.
+ * msgcomm.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msgconv.c (long_options): Add --properties-input/output.
+ (main): Handle them. If option -p is used, ignore the to_code.
+ (usage): Document options -P and -p.
+ * msgen.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msgfilter.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msggrep.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msginit.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msgmerge.c (long_options): Add --properties-input/output.
+ (main): Handle them. In update mode, --properties-input implies
+ --properties-output.
+ (usage): Document options -P and -p.
+ (merge): Set result->encoding if possible.
+ * msguniq.c (long_options): Add --properties-input/output.
+ (main): Handle them.
+ (usage): Document options -P and -p.
+ * msgcmp.c (long_options): Add --properties-input.
+ (main): Handle it.
+ (usage): Document option -P.
+ * msgexec.c (long_options): Add --properties-input.
+ (main): Handle it.
+ (usage): Document option -P.
+ * msgfmt.c (long_options): Add --properties-input.
+ (main): Handle it. Pass the known canon_encoding to
+ msgdomain_write_java and msgdomain_write_tcl.
+ (usage): Document option -P.
+ (msgfmt_add_message): Add force_fuzzy argument.
+ (read_po_file_msgfmt): Pass input_syntax to po_scan().
+ * msgunfmt.c (long_options): Add --properties-output.
+ (main): Handle it.
+ (usage): Document option -p.
+ * x-properties.h: New file.
+ * x-po.c (extract_add_message): Add force_fuzzy argument.
+ (extract): Renamed from extract_po. Pass input_syntax to po_scan().
+ (extract_po): New function.
+ (extract_properties): New function.
+ * xgettext.h: Include read-po.h.
+ * xgettext.c (long_options): Add --properties-output.
+ (main): Handle it.
+ (usage): Document options -L JavaProperties and --properties-output.
+ (exclude_directive_message): Add force_fuzzy argument.
+ (read_exclusion_file): Pass input_syntax to po_scan().
+ (table): Add support for .properties format.
+ * Makefile.am (noinst_HEADERS): Add read-properties.h,
+ write-properties.h, x-properties.h.
+ (COMMON_SOURCE): Add read-properties.c.
+ (libgettextsrc_la_SOURCES): Add write-properties.c.
+ * Makefile.msvc (OBJECTS): Add read-properties.obj,
+ write-properties.obj.
+ (read-properties.obj, write-properties.obj): New rules.
+ * Makefile.vms (OBJECTS): Add read-properties.obj,
+ write-properties.obj.
+ (read-properties.obj, write-properties.obj): New rules.
+ * FILES: Update.
+
+2003-04-21 Bruno Haible <bruno@clisp.org>
+
+ * read-po-abstract.h (po_scan_file): Remove declaration.
+ * read-po-abstract.c (po_scan_file): Remove function.
+ * read-po.c (read_po_file): Call read_po.
+ * msgfmt.c (read_po_file_msgfmt): Call open_po_file. Invoke po_scan
+ instead of po_scan_file.
+ * xgettext.c (read_exclusion_file): Call open_po_file. Invoke po_scan
+ instead of po_scan_file.
+
+2003-04-20 Bruno Haible <bruno@clisp.org>
+
+ * open-po.h (open_po_file): Add argument 'exit_on_error'.
+ * open-po.c (try_open_po_file): Renamed from open_po_file.
+ (open_po_file): New function.
+ * po-lex.h (lex_end): Change return type to void.
+ (lex_open, lex_close): Remove declarations.
+ * po-lex.c (lex_end): Change return type to void.
+ (lex_open, lex_close): Remove functions.
+ * read-po-abstract.c (po_scan_file): Inline lex_open and lex_close.
+ Reuse po_scan code.
+
+2003-04-14 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (usage): Split strings: use one string per option
+ description.
+ * msgattrib.c (usage): Likewise.
+ * msgcat.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * msgcomm.c (usage): Likewise.
+ * msgconv.c (usage): Likewise.
+ * msgen.c (usage): Likewise.
+ * msgexec.c (usage): Likewise.
+ * msgfilter.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * msggrep.c (usage): Likewise.
+ * msginit.c (usage): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * msguniq.c (usage): Likewise.
+ * urlget.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+
+2003-04-13 Bruno Haible <bruno@clisp.org>
+
+ Unify three PO file parsers.
+ * read-po-abstract.h: Renamed from po.h.
+ (abstract_po_reader_class_ty): Renamed from po_method_ty.
+ (abstract_po_reader_ty): Renamed from po_ty.
+ (ABSTRACT_PO_READER_TY): Renamed from PO_BASE_TY.
+ (po_reader_alloc): Renamed from po_alloc.
+ (po_reader_free): Renamed from po_free.
+ * read-po-abstract.c: Renamed from po.c.
+ (po_reader_alloc): Renamed from po_alloc.
+ (po_reader_free): Renamed from po_free.
+ (call_parse_brief): Renamed from po_parse_brief.
+ (call_parse_debrief): Renamed from po_parse_debrief.
+ (call_directive_domain): Renamed from po_directive_domain.
+ (call_directive_message): Renamed from po_directive_message.
+ (call_comment): Renamed from po_comment.
+ (call_comment_dot): Renamed from po_comment_dot.
+ (call_comment_filepos): Renamed from po_comment_filepos.
+ (call_comment_special): Renamed from po_comment_special.
+ * read-po.h: Include read-po-abstract.h.
+ (default_po_reader_class_ty, default_po_reader_ty): New types.
+ (ABSTRACT_PO_READER_TY): New macro.
+ (default_constructor, default_destructor, default_parse_brief,
+ default_parse_debrief, default_directive_domain,
+ default_directive_message, default_comment, default_comment_dot,
+ default_comment_filepos, default_comment_special, default_set_domain,
+ default_add_message): New declarations.
+ (default_po_reader_alloc): New declaration.
+ * read-po.c (default_po_reader_ty): Renamed from readall_class_ty.
+ Add fields handle_comments, handle_filepos_comments,
+ allow_domain_directives, allow_duplicates,
+ allow_duplicates_if_same_msgstr.
+ (call_set_domain, call_add_message, call_frob_new_message): New
+ functions.
+ (default_constructor): Renamed from readall_constructor. Don't set
+ this->mdlp and this->mlp.
+ (default_destructor): Renamed from readall_destructor.
+ (default_parse_brief): Renamed from readall_parse_brief.
+ (default_parse_debrief): New function.
+ (default_copy_comment_state, default_reset_comment_state): New
+ functions.
+ (default_directive_domain): Renamed from readall_directive_domain.
+ Call set_domain method.
+ (default_directive_message): Renamed from readall_directive_message.
+ Call add_message method.
+ (default_comment): Renamed from readall_comment.
+ (default_comment_dot): Renamed from readall_comment_dot.
+ (default_comment_filepos): Renamed from readall_comment_filepos.
+ (default_comment_special): Renamed from readall_comment_special.
+ (default_set_domain, default_add_message): New functions.
+ (default_methods): Renamed from readall_methods.
+ (default_po_reader_alloc): New function.
+ (read_po, read_po_file): Update.
+ * msgfmt.c: Include read-po.h instead of po.h.
+ (msgfmt_po_reader_ty): Renamed from msgfmt_class_ty. Inherit
+ from default_po_reader_ty.
+ (main): Drop po_lex_pass_comments call, done by default_parse_brief.
+ (msgfmt_constructor): Renamed from format_constructor. Call superclass
+ function.
+ (msgfmt_parse_debrief): Renamed from format_debrief. Call superclass
+ function.
+ (format_directive_domain): Remove function.
+ (msgfmt_set_domain): New function.
+ (format_directive_message): Remove function.
+ (msgfmt_add_message, msgfmt_frob_new_message): New functions.
+ (msgfmt_comment_special): Renamed from format_comment_special.
+ (msgfmt_methods): Renamed from format_methods.
+ (read_po_file_msgfmt): Renamed from read_po_file.
+ * x-po.c: Include read-po.h instead of po.h.
+ (extract_class_ty, extract_constructor, extract_directive_domain,
+ extract_directive_message, extract_parse_brief, extract_comment,
+ extract_comment_dot, extract_comment_filepos, extract_comment_special):
+ Remove functions.
+ (extract_add_message): New function.
+ (extract_methods): Update.
+ (extract_po): Update.
+ * xgettext.c: Include read-po-abstract.h instead of po.h.
+ (exclude_directive_domain, exclude_directive_message, exclude_methods,
+ read_exclusion_file): Update.
+ * msgcmp.c: Don't include po.h.
+ * msgmerge.c: Likewise.
+ * po-gram-gen.y: Include read-po-abstract.h instead of po.h.
+ * po-hash-gen.y: Likewise.
+ (po_parse_comment_filepos): Renamed from po_hash.
+ * po-hash.h (po_parse_comment_filepos): Renamed from po_hash.
+ * Makefile.am (noinst_HEADERS): Remove po.h, add read-po-abstract.h.
+ (COMMON_SOURCE): Remove po.c, add read-po-abstract.c.
+ * Makefile.msvc (OBJECTS): Remove po.obj, add read-po-abstract.obj.
+ (read-po-abstract.obj): Renamed from po.obj.
+ * Makefile.vms (OBJECTS): Remove po.obj, add read-po-abstract.obj.
+ (read-po-abstract.obj): Renamed from po.obj.
+
+2003-04-13 Bruno Haible <bruno@clisp.org>
+
+ * read-po.c (readall_directive_message): Separate accumulation and
+ reset.
+ * x-po.c (extract_directive_message): Likewise.
+
+2003-04-13 Bruno Haible <bruno@clisp.org>
+
+ * write-po.h (message_print_comment, message_print_comment_dot,
+ message_print_comment_filepos, message_print_comment_flags): New
+ declarations.
+ * write-po.c: Reorder functions.
+ (message_print_comment, message_print_comment_dot,
+ message_print_comment_filepos, message_print_comment_flags): New
+ functions, extracted from message_print.
+ (message_print): Use them.
+ (message_print_obsolete): Use message_print_comment.
+
+2003-04-22 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.c (plural_table): Add an entry about Faroese.
+ Reported by Jacob Sparre Andersen <sparre@crs4.it>.
+
+2003-04-13 Bruno Haible <bruno@clisp.org>
+
+ * po.h (po_scan_start, po_scan_end): New declarations.
+ * po.c: Reorder functions.
+ (po_scan_start, po_scan_end): New functions.
+ (po_scan, po_scan_file): Use them.
+
+2003-04-12 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.vms: New variables ABIFLAGS, DEFS. Avoid rules with no
+ lines. Don't use the force target. Correct wildcard syntax.
+ Suggested by Jouk Jansen <joukj@hrem.stm.tudelft.nl>.
+
+2003-04-13 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (construct_header): Remove spurious comma.
+
+2003-03-30 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.vms: New file.
+ * Makefile.am (EXTRA_DIST): Add Makefile.vms.
+ (x-java.c): Prepend a few VMS specific lines.
+ * po-lex.c (mbfile_getc): Change element type of scatchbuf to
+ 'unsigned char'.
+ * write-java.c (string_hashcode, write_java_string): Cast argument of
+ u8_mbtouc.
+ * write-tcl.c (write_tcl_string): Likewise.
+ * x-python.c (phase7_getuc): Cast argument of unicode_name_character.
+ * msgfilter.c (write): #undef before redefining it. Needed for VMS.
+ (select): Likewise.
+
+2003-03-30 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (noinst_HEADERS): Add po-gram-gen2.h.
+ (BUILT_SOURCES): Add po-gram-gen2.h.
+ (DISTCLEANFILES): Remove po-gram-gen2.h.
+ (po-gram-gen2.h): Simplify rule.
+
+2003-03-17 Bruno Haible <bruno@clisp.org>
+
+ Native Woe32/MSVC support.
+ * Makefile.msvc: New file.
+ * Makefile.am (EXTRA_DIST): Add it.
+ * msgfilter.c: Include <sys/time.h> only if it exists.
+ (process_string): Mark as NYI on Woe32.
+ * msgfmt.c (sigjmp_buf, sigsetjmp, siglongjmp): Provide fallback
+ definitions for Woe32.
+ * msginit.c: Include <fcntl.h>, for F_OK. Include <pwd.h> only if it
+ exists.
+ (get_user_pwd, get_user_fullname): Adapt for when <pwd.h> is missing.
+ (project_id, project_id_version, get_user_email,
+ language_team_address): Use DEV_NULL instead of "/dev/null".
+ * msggrep.c (is_string_selected): Likewise.
+ * read-java.c (execute_and_read_po_output): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * write-mo.c: Include <sys/param.h> only if it exists.
+
+2003-03-28 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (main): Use copy_file_preserving instead of copy_file.
+
+2003-03-16 Bruno Haible <bruno@clisp.org>
+
+ * str-list.h (string_list_join): Comment out.
+ * str-list.c (string_list_join): Comment out.
+
+2003-03-10 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (no_pass): Mention option -C.
+ (usage): Document option -C completely.
+ Reported by Martin Quinson <Martin.Quinson@tuxfamily.org>.
+
+2003-02-28 Bruno Haible <bruno@clisp.org>
+
+ Support for relocatable installation.
+ * msgattrib.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgcat.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgcmp.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgcomm.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgconv.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgen.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgexec.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgfilter.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgfmt.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msggrep.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msginit.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ (project_id, project_id_version, get_user_email): Relocate LIBDIR
+ value.
+ (language_team_address): Relocate LIBDIR and PROJECTSDIR values.
+ * msgmerge.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msgunfmt.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * msguniq.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * xgettext.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value.
+ * read-java.c: Include relocatable.h.
+ (msgdomain_read_java): Relocate GETTEXTJEXEDIR value.
+ * read-tcl.c: Include relocatable.h.
+ (msgdomain_read_tcl): Relocate GETTEXTDATADIR value.
+ * hostname.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value. Update copyright year.
+ * urlget.c: Include relocatable.h.
+ (main): Relocate LOCALEDIR value. Update copyright year.
+ (fetch): Relocate GETTEXTJEXEDIR value.
+ * user-email.in: Relocate $libdir.
+ * Makefile.am (aliaspath): New variable.
+ (DEFS): Define LOCALE_ALIAS_PATH.
+ (msginit_SOURCES): Add .../localealias.c.
+ (msginit_LDADD): Remove .../localealias.lo.
+ (msgcmp_CFLAGS, msgfmt_CFLAGS, msgmerge_CFLAGS, msgunfmt_CFLAGS,
+ xgettext_CFLAGS, msgattrib_CFLAGS, msgcat_CFLAGS, msgcomm_CFLAGS,
+ msgconv_CFLAGS, msgen_CFLAGS, msgexec_CFLAGS, msgfilter_CFLAGS,
+ msggrep_CFLAGS, msginit_CFLAGS, msguniq_CFLAGS, hostname_CFLAGS,
+ urlget_CFLAGS): New variables.
+ (msgcmp_LDFLAGS, msgfmt_LDFLAGS, msgmerge_LDFLAGS, msgunfmt_LDFLAGS,
+ xgettext_LDFLAGS, msgattrib_LDFLAGS, msgcat_LDFLAGS, msgcomm_LDFLAGS,
+ msgconv_LDFLAGS, msgen_LDFLAGS, msgexec_LDFLAGS, msgfilter_LDFLAGS,
+ msggrep_LDFLAGS, msginit_LDFLAGS, msguniq_LDFLAGS, hostname_LDFLAGS,
+ urlget_LDFLAGS): New variables.
+ (install-exec-java-yes): Don't ignore INSTALL_PROGRAM_ENV.
+ (RELOCATABLE_LIBRARY_PATH, RELOCATABLE_SRC_DIR, RELOCATABLE_BUILD_DIR,
+ RELOCATABLE_CONFIG_H_DIR): New variables.
+
+2003-03-13 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (main): On Solaris, prefer /usr/xpg4/bin/grep to
+ /usr/bin/grep. Needed for Solaris 2.8 and 2.9.
+ Reported by Sven Utcke <utcke@informatik.uni-hamburg.de>.
+
+2003-03-03 Bruno Haible <bruno@clisp.org>
+
+ * msgen.c (usage): Don't say that the entries are marked fuzzy.
+ Reported by Karl Eichwalder <ke@suse.de>.
+
+2003-02-23 Bruno Haible <bruno@clisp.org>
+
+ Improve error messages for invalid format strings.
+ * format-invalid.h: New file.
+ * format.h (struct formatstring_parser): Add invalid_reason argument
+ to 'parse' field.
+ * format-awk.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-c.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (INVALID_C99_MACRO): New macro.
+ (format_parse): Add invalid_reason argument.
+ (get_c99_format_directives): Update.
+ * format-elisp.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-java.c: Include xerror.h, format-invalid.h.
+ (message_format_parse, choice_format_parse, format_parse): Add
+ invalid_reason argument.
+ (choice_format_parse): Return false if a choice contains an empty
+ number part.
+ * format-librep.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-lisp.c: Include xerror.h, format-invalid.h.
+ (check_params, nocheck_params): Add directives, invalid_reason
+ arguments.
+ (parse_upto, format_parse): Add invalid_reason argument.
+ * format-pascal.c: Include xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-php.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-python.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (INVALID_MIXES_NAMED_UNNAMED): New macro.
+ (format_parse): Add invalid_reason argument.
+ * format-tcl.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * format-ycp.c: Include c-ctype.h, xerror.h, format-invalid.h.
+ (format_parse): Add invalid_reason argument.
+ * msgfmt.c (check_pair): Output invalid_reason returned for msgstr.
+ * msgmerge.c (msgfmt_check_pair_fails): Update.
+ * xgettext.c (remember_a_message, remember_a_message_plural): Update.
+ * Makefile.am (FORMAT_SOURCE): Add format-invalid.h.
+
+2003-02-22 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c (init_keywords): Add u*gettext variants and plural
+ handling functions added in Python 2.3.
+
+2003-02-22 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (installdirs): Remove dependency, redundant with
+ automake >= 1.6.
+
+2003-02-20 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (libgettextpo_la_DEPENDENCIES): New variable.
+ Reported by Jim Meyering <jim@meyering.net>.
+
+2003-02-19 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (xgettext_LDADD): Mention $(LIBUNINAME) before
+ libgettextsrc.la, not after it. Needed avoid link error on OSF/1 4.0:
+ libtool produces a link command line that contains "-lc" where a
+ shared library was used, and on OSF/1 4.0 "-lc" must not be used
+ before objects that use integer division.
+
+2003-02-18 Bruno Haible <bruno@clisp.org>
+
+ * message.c (msgdomain_list_free): Uncomment this function.
+ * Makefile.am (libgettextpo_la_LDFLAGS): Add ../lib/libgettextlib.la.
+ Needed for Solaris 2.7.
+
+2003-02-16 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (message_merge): Add support for Report-Msgid-Bugs-To
+ field.
+ * xgettext.c (msgid_bugs_address): New variable.
+ (long_options): Add option --msgid-bugs-address.
+ (main): Handle option --msgid-bugs-address.
+ (usage): Document option --msgid-bugs-address.
+ (construct_header): Add 'Report-Msgid-Bugs-To:' field. Warn if
+ msgid_bugs_address is empty.
+
+2003-02-15 Bruno Haible <bruno@clisp.org>
+
+ * user-email.in: Add support for GNOME evolution, OpenOffice and
+ StarOffice with nonstandard installation directory.
+
+2003-02-15 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (main): Update year in --version output.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2003-02-12 Bruno Haible <bruno@clisp.org>
+
+ Big restructuring.
+ * gettext.c: Remove file.
+ * ngettext.c: Remove file.
+ * Makefile.am (bin_PROGRAMS): Remove gettext, ngettext.
+ (EXTRA_DIST): Add ChangeLog.0.
+ (pkgdatadir): Define using 'gettext' instead of @PACKAGE@.
+ (pkglibdir): Likewise.
+ (INCLUDES): Refer to intl in gettext-runtime directory.
+ (gettext_SOURCES, ngettext_SOURCES, gettext_LDADD, ngettext_LDADD):
+ Remove variables.
+ (install-exec-local, installdirs-local, uninstall-local,
+ install-exec-java-yes, installdirs-java-yes, uninstall-java-yes): Use
+ $(pkglibdir) instead of $(libdir)/$(PACKAGE).
+ * plural.c: Update for changed directory structure.
+ * user-email.in: Use 'gettext' instead of @PACKAGE@.
+
+
+See ChangeLog.0 for earlier changes.
diff --git a/gettext-tools/src/ChangeLog.0 b/gettext-tools/src/ChangeLog.0
new file mode 100644
index 0000000..a049391
--- /dev/null
+++ b/gettext-tools/src/ChangeLog.0
@@ -0,0 +1,5247 @@
+2003-01-31 Bruno Haible <bruno@clisp.org>
+
+ * gettext-po.h: New file.
+ * gettext-po.c: New file.
+ * Makefile.am (lib_LTLIBRARIES): Add libgettextpo.la.
+ (include_HEADERS): New variable.
+ (libgettextpo_la_SOURCES, libgettextpo_la_LDFLAGS): New variables.
+ (LTV_CURRENT, LTV_REVISION, LTV_AGE): New variables.
+
+2003-01-24 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (long_options): Add --only-file and --ignore-file.
+ (main): Handle options --only-file and --ignore-file.
+ (usage): Document options --only-file and --ignore-file.
+ (process_message_list): Add arguments only_mdlp, ignore_mdlp.
+ (process_msgdomain_list): Likewise.
+
+2003-01-23 Bruno Haible <bruno@clisp.org>
+
+ * po-gram.h: Don't include <sys/types.h>.
+
+2003-01-23 Bruno Haible <bruno@clisp.org>
+
+ * msgmerge.c (use_fuzzy_matching): New variable.
+ (long_options): Add option -N/--no-fuzzy-matching.
+ (main, match_domain): Implement it.
+ (usage): Document it.
+
+2003-01-23 Bruno Haible <bruno@clisp.org>
+
+ * write-mo.c (write_table): Use xmalloc/free instead of alloca/freea
+ for most allocations. Needed for platforms with a small stack, such
+ as Woe32 with a default stack size of 1.2 MB.
+ Reported by Xiong Jiang <jxiong@offtopic.org>.
+
+2003-01-23 Bruno Haible <bruno@clisp.org>
+
+ * format-c.c (get_c99_format_directives): Free the allocated
+ descriptor after use.
+
+2003-01-18 Bruno Haible <bruno@clisp.org>
+
+ * FILES: Update.
+
+2003-01-12 Bruno Haible <bruno@clisp.org>
+
+ * msgconv.c: Include localcharset.h.
+ (locale_charset): Remove declaration.
+ * msginit.c: Include localcharset.h.
+ (locale_charset): Remove declaration.
+ * msgl-charset.c: Include localcharset.h.
+ (locale_charset): Remove declaration.
+
+2003-01-12 Bruno Haible <bruno@clisp.org>
+
+ * format-java.c: Include alloca.h instead of liballoca.h.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-charset.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * msgmerge.c: Likewise.
+ * po-charset.c: Likewise.
+ * read-tcl.c: Likewise.
+ * write-java.c: Likewise.
+ * write-mo.c: Likewise.
+ * write-po.c: Likewise.
+ * write-tcl.c: Likewise.
+
+2003-01-12 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am: Make use of += for variables.
+
+2003-01-12 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Add an assertion, to protect against Solaris 2.9
+ iconv() bug.
+
+2003-01-10 Bruno Haible <bruno@clisp.org>
+
+ * po-charset.c (po_lex_charset_set): Work around Solaris 2.9 iconv()
+ bug.
+ * write-po.c (wrap): Likewise.
+
+2003-01-08 Bruno Haible <bruno@clisp.org>
+
+ * project-id: Make it work with configure files that use
+ AM_INIT_AUTOMAKE from automake-1.7.2.
+
+2002-12-04 Bruno Haible <bruno@clisp.org>
+
+ Fix handling of references to filenames starting with a digit.
+ * po-hash-gen.y (last_was_colon): New variable.
+ (yylex): Update it for each token. Recognize numbers only immediately
+ after a colon.
+ (po_hash): Move function. Initialize last_was_colon.
+ Reported by Yanko Kaneti <yaneti@declera.com> and
+ Jordi Mallach <jordi@sindominio.net>.
+
+2002-11-22 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Change test of full_write return value.
+ * msggrep.c (is_string_selected): Likewise.
+ * urlget.c (cat_file): Likewise.
+
+2002-11-14 Bruno Haible <bruno@clisp.org>
+
+ * x-php.c (phase3_getc): Initialize variable last_was_qmark.
+
+2002-11-13 Bruno Haible <bruno@clisp.org>
+
+ Assume ANSI C.
+ * dir-list.h (dir_list_append, dir_list_nth, dir_list_save_reset,
+ dir_list_restore): Use ANSI C function declarations.
+ * dir-list.c (dir_list_append, dir_list_nth, dir_list_restore):
+ Likewise.
+ * file-list.h (read_names_from_file): Likewise.
+ * file-list.c (read_names_from_file): Likewise.
+ * format.h (struct formatstring_parser, get_c99_format_directives):
+ Likewise.
+ * format-awk.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-c.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check,
+ get_c99_format_directives, format_print): Likewise.
+ * format-elisp.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-java.c (message_format_parse, date_format_parse,
+ number_format_parse, choice_format_parse, numbered_arg_compare,
+ format_parse, format_free, format_get_number_of_directives,
+ format_check, format_print): Likewise.
+ * format-librep.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-lisp.c (verify_element, verify_list, free_element, free_list,
+ copy_element, copy_list, equal_element, equal_list,
+ ensure_initial_alloc, grow_initial_alloc, ensure_repeated_alloc,
+ grow_repeated_alloc, normalize_outermost_list, normalize_list,
+ is_empty_list, unfold_loop, rotate_loop, initial_splitelement,
+ initial_unshare, shift_list, make_intersected_element,
+ append_repeated_to_initial, backtrack_in_initial,
+ make_intersected_list, make_intersection_with_empty_list,
+ intersection, make_union_element, make_union_list,
+ make_union_with_empty_list, union, is_required,
+ add_required_constraint, add_end_constraint, add_type_constraint,
+ add_listtype_constraint, add_req_type_constraint,
+ add_req_listtype_constraint, make_repeated_list_of_lists,
+ make_repeated_list, check_params, nocheck_params, parse_upto,
+ format_parse, format_free, format_get_number_of_directives,
+ format_check, print_element, print_list, format_print): Likewise.
+ * format-pascal.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-php.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-python.c (named_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-tcl.c (numbered_arg_compare, format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * format-ycp.c (format_parse, format_free,
+ format_get_number_of_directives, format_check, format_print): Likewise.
+ * gettext.c (main, usage, expand_escape): Likewise.
+ * hostname.c (main, usage): Likewise.
+ * message.h (possible_format_p, message_alloc, message_free,
+ message_comment_append, message_comment_dot_append,
+ message_comment_filepos, message_copy, message_list_alloc,
+ message_list_free, message_list_append, message_list_prepend,
+ message_list_delete_nth, message_predicate_ty,
+ message_list_remove_if_not, message_list_search,
+ message_list_search_fuzzy, message_list_list_alloc,
+ message_list_list_free, message_list_list_append,
+ message_list_list_append_list, message_list_list_search,
+ message_list_list_search_fuzzy, msgdomain_alloc, msgdomain_free,
+ msgdomain_list_alloc, msgdomain_list_free, msgdomain_list_append,
+ msgdomain_list_append_list, msgdomain_list_sublist,
+ msgdomain_list_search, msgdomain_list_search_fuzzy): Likewise.
+ * message.c (possible_format_p, message_alloc, message_free,
+ message_comment_append, message_comment_dot_append,
+ message_comment_filepos, message_copy, message_list_alloc,
+ message_list_free, message_list_append, message_list_prepend,
+ message_list_delete_nth, message_list_remove_if_not,
+ message_list_search, message_list_search_fuzzy_inner,
+ message_list_search_fuzzy, message_list_list_free,
+ message_list_list_append, message_list_list_append_list,
+ message_list_list_search, message_list_list_search_fuzzy,
+ msgdomain_alloc, msgdomain_free, msgdomain_list_alloc,
+ msgdomain_list_free, msgdomain_list_append,
+ msgdomain_list_append_list, msgdomain_list_sublist,
+ msgdomain_list_search, msgdomain_list_search_fuzzy): Likewise.
+ * msgattrib.c (main, usage, is_message_selected, process_message_list,
+ process_msgdomain_list): Likewise.
+ * msgcat.c (main, usage): Likewise.
+ * msgcmp.c (main, usage, is_message_selected, remove_obsoletes,
+ match_domain, compare): Likewise.
+ * msgcomm.c (main, usage): Likewise.
+ * msgconv.c (locale_charset, main, usage): Likewise.
+ * msgen.c (main, usage): Likewise.
+ * msgexec.c (main, usage, nonintr_close, process_string,
+ process_message, process_message_list, process_msgdomain_list):
+ Likewise.
+ * msgfilter.c (main, usage, nonintr_close, nonintr_read, nonintr_write,
+ nonintr_select, process_string, process_message, process_message_list,
+ process_msgdomain_list): Likewise.
+ * msgfmt.c (main, usage, add_mo_suffix, new_domain, is_nonobsolete,
+ sigfpe_handler, check_plural_eval, check_plural, check_pair,
+ check_header_entry, format_constructor, format_debrief,
+ format_directive_domain, format_directive_message,
+ format_comment_special, read_po_file): Likewise.
+ * msggrep.c (main, no_pass, usage, filename_list_match, nonintr_close,
+ is_string_selected, is_message_selected, process_message_list,
+ process_msgdomain_list): Likewise.
+ * msginit.c (locale_charset, _nl_locale_name, _nl_expand_alias, main,
+ usage, catalogname_for_locale, language_of_locale, po_revision_date,
+ content_type, fields, get_field, put_field, subst_string,
+ subst_string_list, fill_header): Likewise.
+ * msgl-ascii.h (is_ascii_string, is_ascii_string_list,
+ is_ascii_message, is_ascii_message_list): Likewise.
+ * msgl-ascii.c (is_ascii_string, is_ascii_string_list,
+ is_ascii_message, is_ascii_message_list): Likewise.
+ * msgl-cat.h (catenate_msgdomain_list): Likewise.
+ * msgl-cat.c (is_message_selected, is_message_needed,
+ is_message_first_needed, catenate_msgdomain_list): Likewise.
+ * msgl-charset.h (compare_po_locale_charsets): Likewise.
+ * msgl-charset.c (locale_charset, compare_po_locale_charsets):
+ Likewise.
+ * msgl-english.h (msgdomain_list_english): Likewise.
+ * msgl-english.c (msgdomain_list_english): Likewise.
+ * msgl-equal.h (string_list_equal, message_equal, message_list_equal,
+ msgdomain_list_equal): Likewise.
+ * msgl-equal.c (msgstr_equal, msgstr_equal_ignoring_potcdate,
+ pos_equal, string_list_equal, message_equal, message_list_equal,
+ msgdomain_equal, msgdomain_list_equal): Likewise.
+ * msgl-iconv.h (convert_string, iconv_message_list,
+ iconv_msgdomain_list): Likewise.
+ * msgl-iconv.c (iconv_string, convert_string, convert_string_list,
+ convert_msgid, convert_msgstr, iconv_message_list,
+ iconv_msgdomain_list): Likewise.
+ * msgmerge.c (main, usage, compendium, msgfmt_check_pair_fails,
+ message_merge, match_domain, merge): Likewise.
+ * msgunfmt.c (main, usage): Likewise.
+ * msguniq.c (main, usage): Likewise.
+ * ngettext.c (main, usage, expand_escape): Likewise.
+ * open-po.h (open_po_file): Likewise.
+ * open-po.c (open_po_file): Likewise.
+ * po-charset.h (po_charset_canonicalize, po_charset_ascii_compatible,
+ po_is_charset_weird, po_is_charset_weird_cjk, po_lex_charset_init,
+ po_lex_charset_set, po_lex_charset_close): Likewise.
+ * po-charset.c (po_charset_canonicalize, po_charset_ascii_compatible,
+ po_is_charset_weird, po_is_charset_weird_cjk, po_lex_charset_set):
+ Likewise.
+ * po-gram.h (po_gram_parse): Likewise.
+ * po-hash-gen.y (yyerror, yylex, po_hash): Likewise.
+ * po-hash.h (po_hash): Likewise.
+ * po-lex.h (lex_start, lex_end, lex_open, lex_close, po_gram_lex,
+ po_lex_pass_comments, po_lex_pass_obsolete_entries, po_gram_error,
+ po_gram_error_at_line): Likewise.
+ * po-lex.c: Include <stdarg.h> instead of libstdarg.h.
+ (ALERT_CHAR): Remove macro.
+ (po_gram_error, po_gram_error_at_line): Use ANSI C function
+ declarations. Use va_start instead of VA_START.
+ (memcpy_small, mb_iseof, mb_ptr, mb_len, mb_iseq, mb_isnul, mb_cmp,
+ mb_equal, mb_isascii, mb_width, mb_putc, mb_setascii, mb_copy,
+ mbfile_init, mbfile_getc, mbfile_ungetc, lex_start, lex_open,
+ lex_getc, lex_ungetc, keyword_p, po_lex_pass_comments,
+ po_lex_pass_obsolete_entries): Use ANSI C function declarations.
+ * po-time.h (po_strftime): Likewise.
+ * po-time.c (difftm, po_strftime): Likewise.
+ * po.h (struct po_method_ty, po_alloc, po_scan, po_scan_file, po_free,
+ po_callback_domain, po_callback_message, po_callback_comment,
+ po_callback_comment_dot, po_callback_comment_filepos,
+ po_parse_comment_special): Likewise.
+ * po.c (po_alloc, po_free, po_parse_brief, po_parse_debrief, po_scan,
+ po_scan_file, po_directive_domain, po_callback_domain,
+ po_directive_message, po_callback_message, po_comment_special,
+ po_comment, po_comment_dot, po_callback_comment, po_comment_filepos,
+ po_callback_comment_filepos, po_parse_comment_special): Likewise.
+ * read-java.h (msgdomain_read_java): Likewise.
+ * read-java.c (execute_and_read_po_output, msgdomain_read_java):
+ Likewise.
+ * read-mo.h (read_mo_file): Likewise.
+ * read-mo.c (read_binary_mo_file, get_uint32, get_string,
+ get_sysdep_string, read_mo_file): Likewise.
+ * read-po.h (read_po, read_po_file): Likewise.
+ * read-po.c (readall_constructor, readall_destructor,
+ readall_directive_domain, readall_directive_message,
+ readall_parse_brief, readall_comment, readall_comment_dot,
+ readall_comment_special, readall_comment_filepos, read_po,
+ read_po_file): Likewise.
+ * read-tcl.h (msgdomain_read_tcl): Likewise.
+ * read-tcl.c (msgdomain_read_tcl): Likewise.
+ * str-list.h (string_list_init, string_list_alloc, string_list_append,
+ string_list_append_unique, string_list_destroy, string_list_free,
+ string_list_concat, string_list_concat_destroy, string_list_join,
+ string_list_member): Likewise.
+ * str-list.c (string_list_init, string_list_append,
+ string_list_append_unique, string_list_destroy, string_list_free,
+ string_list_concat, string_list_concat_destroy, string_list_join,
+ string_list_member): Likewise.
+ * urlget.c (main, usage, cat_file, execute_it, fetch): Likewise.
+ * write-java.h (msgdomain_write_java): Likewise.
+ * write-java.c (check_resource_name, string_hashcode,
+ compute_hashsize, compare_index, compute_table_items,
+ write_java_string, write_java_msgstr, write_lookup_code,
+ is_expression_boolean, write_java_expression, write_java_code,
+ cleanup, msgdomain_write_java): Likewise.
+ * write-mo.h (msgdomain_write_mo): Likewise.
+ * write-mo.c (compare_id, write_table, msgdomain_write_mo): Likewise.
+ * write-po.h (message_page_width_set, message_page_width_ignore,
+ message_print_style_indent, message_print_style_uniforum,
+ message_print_style_escape, msgdomain_list_print,
+ msgdomain_list_sort_by_msgid, msgdomain_list_sort_by_filepos):
+ Likewise.
+ * write-po.c (message_page_width_set, message_print_style_escape,
+ make_format_description_string, significant_format_p,
+ has_significant_format_p, make_c_width_description_string,
+ memcpy_small, wrap, print_blank_line, message_print,
+ message_print_obsolete, msgdomain_list_print, cmp_by_msgid,
+ msgdomain_list_sort_by_msgid, cmp_filepos,
+ msgdomain_list_sort_filepos, cmp_by_filepos,
+ msgdomain_list_sort_by_filepos): Likewise.
+ * write-tcl.h (msgdomain_write_tcl): Likewise.
+ * write-tcl.c (write_tcl_string, write_msg, msgdomain_write_tcl):
+ Likewise.
+ * x-awk.h (extract_awk, x_awk_keyword, x_awk_extract_all): Likewise.
+ * x-awk.c (ALERT_CHAR): Remove macro.
+ (x_awk_keyword, phase1_ungetc, phase2_ungetc, free_token, x_awk_lex,
+ extract_parenthesized, extract_awk): Use ANSI C function declarations.
+ * x-c.h (extract_c, x_c_extract_all, x_c_keyword, x_c_any_keywords,
+ x_c_trigraphs): Likewise.
+ * x-c.c (ALERT_CHAR): Remove macro.
+ (x_c_keyword, phase1_ungetc, phase2_ungetc, phase3_ungetc,
+ comment_add, comment_line_end, phase4_ungetc, phase7_ungetc,
+ free_token, phase5_get, phaseX_get, phase6_get, phase6_unget,
+ is_inttypes_macro, phase8a_get, phase8a_unget, phase8_get, x_c_lex,
+ extract_parenthesized, extract_c): Use ANSI C function declarations.
+ * x-elisp.h (extract_elisp, x_elisp_extract_all, x_elisp_keyword):
+ Likewise.
+ * x-elisp.c (ALERT_CHAR): Remove macro.
+ (x_elisp_keyword, do_ungetc, init_token, free_token, grow_token,
+ is_integer, is_float, read_token, comment_add, comment_line_end,
+ free_object, string_of_object, do_getc_escaped, read_object,
+ extract_elisp): Use ANSI C function declarations.
+ * x-glade.h (extract_glade, x_glade_extract_all, x_glade_keyword):
+ Likewise.
+ * x-glade.c (x_glade_keyword, XML_StartElementHandler,
+ XML_EndElementHandler, XML_CharacterDataHandler, XML_CommentHandler,
+ p_XML_ParserCreate, p_XML_SetElementHandler,
+ p_XML_SetCharacterDataHandler, p_XML_SetCommentHandler, p_XML_Parse,
+ p_XML_GetErrorCode, p_XML_GetCurrentLineNumber,
+ p_XML_GetCurrentColumnNumber, p_XML_ParserFree, p_XML_ErrorString,
+ ensure_stack_size, start_element_handler, end_element_handler,
+ character_data_handler, comment_handler, do_extract_glade,
+ extract_glade): Likewise.
+ * x-java.h (extract_java, x_java_keyword, x_java_extract_all):
+ Likewise.
+ * x-java.l (append_char_buf, get_string, destroy_charbuf,
+ update_line_no, strip_ending_spaces, append_strings, isplus, isdot,
+ translate_esc, object_list_destroy, get_num_objects, get_object,
+ add_object, alloc_keyword, tailcmp, do_compare, is_keyword,
+ x_java_keyword, extract_java): Likewise.
+ * x-librep.h (extract_librep, x_librep_extract_all, x_librep_keyword):
+ Likewise.
+ * x-librep.c (ALERT_CHAR): Remove macro.
+ (x_librep_keyword, do_ungetc, init_token, free_token, grow_token,
+ read_token, comment_add, comment_line_end, free_object,
+ string_of_object, do_getc_escaped, read_object, extract_librep): Use
+ ANSI C function declarations.
+ * x-lisp.h (extract_lisp, x_lisp_extract_all, x_lisp_keyword):
+ Likewise.
+ * x-lisp.c (x_lisp_keyword, do_ungetc, syntax_code_of,
+ read_char_syntax, attribute_of, init_token, free_token, grow_token,
+ read_token, has_a_dot, all_a_number, a_letter_to_digit, has_a_digit,
+ has_adjacent_letters, is_potential_number, is_number, upcase_token,
+ downcase_token, case_convert_token, comment_add, comment_line_end,
+ free_object, string_of_object, read_object, extract_lisp): Likewise.
+ * x-php.h (extract_php, x_php_keyword, x_php_extract_all): Likewise.
+ * x-php.c (x_php_keyword, phase1_ungetc, phase2_ungetc, comment_add,
+ comment_line_end, phase3_ungetc, free_token, x_php_lex,
+ extract_parenthesized, extract_php): Likewise.
+ * x-po.h (extract_po): Likewise.
+ * x-po.c (extract_constructor, extract_directive_domain,
+ extract_directive_message, extract_parse_brief, extract_comment,
+ extract_comment_dot, extract_comment_filepos, extract_comment_special,
+ extract_po): Likewise.
+ * x-python.h (extract_python, x_python_keyword, x_python_extract_all):
+ Likewise.
+ * x-python.c (ALERT_CHAR): Remove macro.
+ (x_python_keyword, phase1_ungetc, comment_add, phase2_ungetc,
+ phase7_getuc, phase5_get, phase5_unget, x_python_lex,
+ extract_parenthesized, extract_python): Use ANSI C function
+ declarations.
+ * x-rst.h (extract_rst): Likewise.
+ * x-rst.c (extract_rst): Likewise.
+ * x-smalltalk.h (extract_smalltalk): Likewise.
+ * x-smalltalk.c (phase1_ungetc, comment_add, phase2_get, phase2_unget,
+ x_smalltalk_lex, extract_smalltalk): Likewise.
+ * x-tcl.h (extract_tcl, x_tcl_extract_all, x_tcl_keyword): Likewise.
+ * x-tcl.c (ALERT_CHAR): Remove macro.
+ (x_tcl_keyword, do_ungetc, phase1_ungetc, phase2_pop, phase2_ungetc,
+ init_token, free_token, grow_token, comment_add, free_word,
+ string_of_word, accumulate_word, read_word, read_command,
+ read_command_list, extract_tcl): Use ANSI C function declarations.
+ * x-ycp.h (extract_ycp): Likewise.
+ * x-ycp.c (phase1_ungetc, phase2_ungetc, x_ycp_lex, extract_ycp):
+ Likewise.
+ * xgettext.h (split_keywordspec, xgettext_comment_add,
+ xgettext_comment, xgettext_comment_reset, remember_a_message,
+ remember_a_message_plural): Likewise.
+ * xgettext.c (extractor_func, main, usage, exclude_directive_domain,
+ exclude_directive_message, read_exclusion_file, split_keywordspec,
+ xgettext_comment_add, xgettext_comment, xgettext_open,
+ extract_from_file, remember_a_message, remember_a_message_plural,
+ finalize_header, language_to_extractor, extension_to_language):
+ Likewise.
+
+2002-11-05 Bruno Haible <bruno@clisp.org>
+
+ Allow non-ASCII msgids in POT files.
+ * po-charset.h (po_charset_utf8): New declaration.
+ * po-charset.c (utf8, po_charset_utf8): New variables.
+ (po_charset_canonicalize): Use po_charset_utf8.
+ * msgl-iconv.h: Include iconv.h.
+ (convert_string): New declaration.
+ * msgl-iconv.c (convert_string): Export function.
+ (convert_msgid): New function.
+ (iconv_message_list): Call it.
+ * xgettext.h: Include iconv.h.
+ (xgettext_global_source_encoding, xgettext_global_source_iconv,
+ xgettext_current_source_encoding, xgettext_current_source_iconv): New
+ declarations.
+ * xgettext.c (xgettext_global_source_encoding,
+ xgettext_global_source_iconv, xgettext_current_source_encoding,
+ xgettext_current_source_iconv): New variables.
+ (long_options): New option --from-code.
+ (main): Initialize xgettext_global_source_encoding. Handle option
+ --from-code. Initialize and destroy xgettext_global_source_iconv.
+ (usage): Document option --from-code.
+ (extract_from_file): Set xgettext_current_source_encoding and
+ xgettext_current_source_iconv.
+ (CONVERT_STRING): New macro.
+ (remember_a_message, remember_a_message_plural): Call CONVERT_STRING.
+ (finalize_header): Set the charset in the header here.
+ * x-glade.c (do_extract_glade): Set xgettext_current_source_encoding.
+ Don't set the result's header charset; this is now done in xgettext.c.
+ * x-python.c (extract_python): Likewise.
+ * x-tcl.c (extract_tcl): Likewise.
+ * write-po.c (message_print, message_print_obsolete): Don't warn about
+ non-ASCII msgids if the file's encoding is UTF-8.
+ * msginit.c (content_type): Add header argument. Use charset UTF-8
+ if that was already the POT file's encoding.
+ (fields): Update.
+ * msgmerge.c (merge): If the POT file was in UTF-8, convert the
+ definitions to UTF-8.
+ * msgcmp.c (compare): Likewise.
+
+2002-11-01 Bruno Haible <bruno@clisp.org>
+
+ * msgcmp.c: Include read-po.h.
+ (is_message_selected, remove_obsoletes): New functions.
+ (grammar, compare_constructor, compare_destructor,
+ compare_directive_domain, compare_directive_message): Remove functions.
+ (compare_class_ty): Remove type.
+ (compare_methods): Remove variable.
+ (compare): Use read_po_file instead.
+
+2002-10-14 Bruno Haible <bruno@clisp.org>
+
+ * project-id: When testing for "GNU $package", ignore libtool, because
+ libtool.m4 calls a package "GNU $package" even if it is not GNU.
+ Reported by Andrew Suffield <asuffield@debian.org>.
+
+2002-10-03 Bruno Haible <bruno@clisp.org>
+
+ * msgl-equal.h (message_equal, message_list_equal,
+ msgdomain_list_equal): Add 'ignore_potcdate' argument.
+ * msgl-equal-c (msgstr_equal, msgstr_equal_ignoring_potcdate): New
+ functions.
+ (message_equal, message_list_equal, msgdomain_equal,
+ msgdomain_list_equal): Add 'ignore_potcdate' argument.
+ * msgmerge.c (main): Call msgdomain_list_equal with ignore_potcdate =
+ true.
+
+2002-09-09 Bruno Haible <bruno@clisp.org>
+
+ * x-smalltalk.h: New file.
+ * x-smalltalk.c: New file.
+ * xgettext.c: Include x-smalltalk.h.
+ (usage): Mention Smalltalk language.
+ (language_to_extractor): Add Smalltalk support.
+ (extension_to_language): Likewise.
+ * Makefile.am (noinst_HEADERS): Add x-smalltalk.h.
+ (xgettext_SOURCES): Add x-smalltalk.c.
+
+2002-08-18 Bruno Haible <bruno@clisp.org>
+
+ * message.h (enum format_type): New enum value 'format_php'.
+ (NFORMATS): Increment.
+ * message.c (format_language, format_language_pretty): Add entry
+ for php.
+ * format.h (formatstring_php): New declaration.
+ * format-php.c: New file.
+ * format.c (formatstring_parsers): Add entry for php.
+ * x-php.h: New file.
+ * x-php.c: New file.
+ * xgettext.c: Include x-php.c.
+ (main): Call x_php_extract_all, x_php_keyword.
+ (language_to_scanner): Add PHP rule.
+ (extension_to_language): Add PHP rule.
+ * Makefile.am (noinst_HEADERS): Add x-php.h.
+ (FORMAT_SOURCE): Add format-php.c.
+ (xgettext_SOURCES): Add x-php.c.
+
+2002-08-17 Bruno Haible <bruno@clisp.org>
+
+ * urlget.c (fetch): Also try invoking the 'curl' program.
+
+2002-08-06 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11.5 released.
+
+2002-08-02 Bruno Haible <bruno@clisp.org>
+
+ * read-mo.c (get_sysdep_string): Make the error message easier to
+ translate.
+
+2002-07-25 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11.4 released.
+
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * format.h (struct interval): New type.
+ (get_c99_format_directives): New declaration.
+ * format-c.c (FAT_SIZE_8_T, FAT_SIZE_16_T, FAT_SIZE_32_T,
+ FAT_SIZE_64_T, FAT_SIZE_LEAST8_T, FAT_SIZE_LEAST16_T,
+ FAT_SIZE_LEAST32_T, FAT_SIZE_LEAST64_T, FAT_SIZE_FAST8_T,
+ FAT_SIZE_FAST16_T, FAT_SIZE_FAST32_T, FAT_SIZE_FAST64_T,
+ FAT_SIZE_INTPTR_T): New enum values.
+ (FAT_SIZE_MASK): Update.
+ (struct spec): New fields c99_directives_count, c99_directives.
+ (format_parse): Recognize C 99 <inttypes.h> macros. Set the new
+ fields c99_directives_count, c99_directives in the result.
+ (get_c99_format_directives): New function.
+ (format_print): Update.
+ * x-c.c (is_inttypes_macro, phase8a_get, phase8a_unget): New functions.
+ (phase8_get): Use phase8a_get instead of phase6_get.
+ * write-mo.c: Include format.h, xmalloc.h.
+ (freea): New macro.
+ (struct pre_string): New type.
+ (struct pre_message): Renamed from struct id_str_pair.
+ (compare_id): Update.
+ (struct pre_sysdep_segment): New type.
+ (struct pre_segment_pair): New type.
+ (struct pre_sysdep_string): New type.
+ (struct pre_sysdep_message): New type.
+ (write_table): Rewritten.
+ * read-mo.c (endian): Remove variable.
+ (read32, seek32, string32): Remove functions.
+ (struct binary_mo_file): New type.
+ (read_binary_mo_file, get_uint32, get_string, get_sysdep_string): New
+ functions.
+ (read_mo_file): Reworked to work in-memory, without fseek/lseek calls.
+ * msgfmt.c (format_directive_message): Copy the is_format array.
+ Needed by write-mo.c.
+
+2002-07-20 Andreas Schwab <schwab@suse.de>
+
+ * plural-eval.c: Include <signal.h> if !INTDIV0_RAISES_SIGFPE.
+
+2002-07-17 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11.3 released.
+
+2002-07-17 Bruno Haible <bruno@clisp.org>
+
+ * dir-list.c: Include stdlib.h.
+
+2002-07-16 Bruno Haible <bruno@clisp.org>
+
+ * read-java.c (msgdomain_read_java): Pass $GETTEXTJEXEDIR to
+ execute_java_class.
+ * urlget.c (fetch): Likewise.
+ * Makefile.am (DEFS): Define USEJEXE and GETTEXTJEXEDIR.
+ (GCJ, GCJFLAGS, USEJEXE): New variables.
+ (all-java*): Renamed to take into account BUILDJAVAEXE.
+ (install-data-java*): Likewise.
+ (installdirs-java*): Likewise.
+ (uninstall-java*): Likewise.
+ (install-exec-*): New rules.
+ (gnu.gettext.DumpResource, gnu.gettext.GetURL): New rules.
+ (CLEANFILES): Add them.
+
+2002-07-16 Bruno Haible <bruno@clisp.org>
+
+ * project-id: Make it work when the configure file was generated by
+ autoconf-2.52 with AC_INIT and AM_INIT_AUTOMAKE(...).
+
+2002-06-14 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c (mbfile_getc): Handle the case where iconv converts
+ some character and returns -1 at once; this can happen immediately
+ after an invalid multibyte sequence.
+ Reported by Andrew V. Samoilov <kai@cmail.ru>.
+
+2002-06-08 Bruno Haible <bruno@clisp.org>
+
+ * user-email.in: Look at /etc/sysconfig/mail, found on SuSE Linux.
+ Suggested by Karl Eichwalder <ke@suse.de>.
+
+2002-06-07 Bruno Haible <bruno@clisp.org>
+
+ * user-email.in: Look at /etc/mailname, found on Debian systems.
+ Suggested by Mark W. Eichin <eichin@thok.org>.
+
+2002-06-07 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (install_sigfpe_handler): Assume the signal is always
+ SIGFPE, regardless of the platform.
+ (uninstall_sigfpe_handler): Likewise.
+
+2002-05-29 Bruno Haible <bruno@clisp.org>
+
+ * dir-list.h (dir_list_save_reset, dir_list_restore): New declarations.
+ * dir-list.c (dir_list_save_reset, dir_list_restore): New functions.
+ * xgettext.c (main): Temporarily reset the dir_list while opening
+ reading the contents of the output file.
+ Reported by Mark Eichin <eichin@thok.org>.
+
+2002-05-22 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.h (iconv_message_list): Add from_filename argument.
+ (iconv_msgdomain_list): Likewise.
+ * msgl-iconv.c (iconv_message_list): Add from_filename argument.
+ Don't give an error for POT files containing charset=CHARSET.
+ (iconv_msgdomain_list): Add from_filename argument.
+ * msgl-cat.c (catenate_msgdomain_list): Don't give an error for
+ POT files containing charset=CHARSET. Pass input file name to
+ iconv_message_list.
+ * msgconv.c (main): Pass input file name to iconv_msgdomain_list.
+ * write-java.c (msgdomain_write_java): Update.
+ * write-tcl.c (msgdomain_write_tcl): Update.
+ * x-glade.c (do_extract_glade): Update.
+ * x-python.c (extract_python): Update.
+ * x-tcl.c (extract_tcl): Update.
+
+2002-05-18 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (RM): New variable.
+ (install-exec-am): Depend on install-exec-clean.
+ (install-exec-clean): New rule.
+
+2002-05-14 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c (wrap): Canonicalize the charset. Handle the case when
+ po_charset_canonicalize returns NULL.
+ Reported by Jan-Marek Glogowski <glogow@stud.fbi.fh-darmstadt.de>.
+
+2002-05-12 Bruno Haible <bruno@clisp.org>
+
+ * po-charset.c (po_charset_canonicalize): Add ISO-8859-14, KOI8-T,
+ GEORGIAN-PS to the list of allowed encodings.
+
+2002-04-30 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (ipv6_ntop): Provide a fallback definition for systems
+ that define s6_addr but no s6_addr16 element of 'struct in6_addr'.
+ Reported by Perry Rapp <PRapp@smartronix.com>.
+
+2002-04-28 Bruno Haible <bruno@clisp.org>
+
+ * project-id: Recognize package name and version when given by a
+ two-argument AC_INIT invocation (autoconf 2.52 and newer).
+
+2002-04-24 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11.2 released.
+
+2002-04-23 Bruno Haible <bruno@clisp.org>
+
+ * write-po.h (message_page_width_ignore): New declaration.
+ * write-po.c (wrap_strings): New variable.
+ (message_page_width_ignore): New function.
+ (wrap): Ignore the do_wrap argument if wrap_strings is false.
+ * msgattrib.c (long_options): Add --no-wrap option.
+ (main): Handle option --no-wrap.
+ (usage): Document option --no-wrap.
+ * msgcat.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgen.c: Likewise.
+ * msgfilter.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * xgettext.c: Likewise.
+
+2002-04-19 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (catalogname_for_locale): Add wa_BE. Change Javanese from
+ jw to jv.
+
+2002-04-15 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.h (po_gram_error): Treat Apple's version of GCC like non-GCC.
+ * po-lex.c (po_gram_error): Likewise.
+ Reported by Richard S. Blake <blakers@presence-group.com>.
+
+2002-04-04 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.h (po_gram_error): Use __VA_ARGS__ + 0 instead of __VA_ARGS__.
+ (po_gram_error_at_line): LIkewise.
+ Fixes syntax error when __VA_ARGS__ is empty.
+ Reported by Trond Eivind Glomsrød <teg@redhat.com>.
+
+2002-04-04 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (find_pot): Add dummy return, to avoid gcc warning.
+
+2002-03-12 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11.1 released.
+
+2002-03-10 Bruno Haible <bruno@clisp.org>
+
+ * x-python.c (phase7_getuc): Change type of buf to 'unsigned char[]'.
+
+2002-03-07 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (msginit_LDADD): Use @INTL_LIBTOOL_SUFFIX_PREFIX@o
+ instead of $(OBJEXT).
+
+2002-03-05 Bruno Haible <bruno@clisp.org>
+
+ * hostname.c (s6_addr16): Define if not defined by the system.
+ (ipv6_ntop): Use s6_addr16 instead of in6_u.u6_addr16.
+
+2002-03-05 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (usage): Mention languages Python, awk, Tcl, RST, Glade.
+
+2002-03-03 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_tcl'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_tcl entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_tcl): New declaration.
+ * format-tcl.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_tcl.
+ * x-tcl.h: New file.
+ * x-tcl.c: New file.
+ * xgettext.c: Include x-tcl.h.
+ (main): Call x_tcl_extract_all, x_tcl_keyword.
+ (language_to_scanner): Add Tcl rule.
+ (extension_to_language): Add Tcl rule.
+ * write-tcl.h: New file.
+ * write-tcl.c: New file.
+ * msgfmt.c: Include write-tcl.h.
+ (tcl_mode, tcl_locale_name, tcl_base_directory): New variables.
+ (long_options): Add option "--tcl".
+ (main): Handle --tcl option. Set tcl_mode, tcl_locale_name,
+ tcl_base_directory. More checks for contradicting options. Call
+ msgdomain_write_tcl.
+ (usage): Mention Tcl mode.
+ (format_directive_domain): Ignore domain directive if in Tcl mode.
+ * read-tcl.h: New file.
+ * read-tcl.c: New file.
+ * msgunfmt.c: Include read-tcl.h.
+ (tcl_mode, tcl_locale_name, tcl_base_directory): New variables.
+ (long_options): Add option "--tcl".
+ (main): Handle --tcl and -d options. Set tcl_mode, tcl_locale_name,
+ tcl_base_directory. More checks for contradicting options. Call
+ msgdomain_read_tcl.
+ (usage): Mention Tcl mode.
+ * msgunfmt.tcl: New file.
+ * Makefile.am (noinst_HEADERS): Add read-tcl.h, write-tcl.h, x-tcl.h.
+ (EXTRA_DIST): Add msgunfmt.tcl.
+ (DEFS): Add -DGETTEXTDATADIR.
+ (FORMAT_SOURCE): Add format-tcl.c.
+ (msgfmt_SOURCES): Add write-tcl.c.
+ (msgunfmt_SOURCES): Add read-tcl.c.
+ (xgettext_SOURCES): Add x-tcl.c.
+ (install-tcl, installdirs-tcl, uninstall-tcl): New targets.
+
+2002-03-02 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_pair): Don't count "&&" as an accelerator designator,
+ for consistency with Qt and KDE.
+
+2002-02-24 Bruno Haible <bruno@clisp.org>
+
+ * po-lex.c: Include libstdarg.h.
+ (VA_START, va_alist, va_dcl): Remove macros.
+ (po_gram_error): Modernize varargs handling. Fix memory leak.
+ (po_gram_error_at_line): Likewise.
+
+2002-02-21 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c: Include liballoca.h.
+ (grep_args): Add a third one.
+ (grep_argv): Likewise.
+ (long_options): Add --comment option.
+ (main): Handle option --comment/-C.
+ (usage): Document option -C.
+ (is_message_selected): If option -C is given, concatenate the
+ translator comments and run a third grep pass on the result.
+
+2002-02-09 Bruno Haible <bruno@clisp.org>
+
+ * message.h (possible_format_p): Change return type to bool.
+ * message.c (possible_format_p): Likewise.
+
+2002-01-14 Bruno Haible <bruno@clisp.org>
+
+ * x-glade.h: New file.
+ * x-glade.c: New file.
+ * xgettext.c: Include x-glade.h.
+ (main): Call x_glade_extract_all, x_glade_keyword.
+ (language_to_scanner): Add glade rule.
+ (extension_to_language): Add glade rule.
+ * Makefile.am (noinst_HEADERS): Add x-glade.h.
+ (xgettext_SOURCES): Add x-glade.c.
+ (xgettext_LDADD): Add @LTLIBEXPAT@.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_awk'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_awk entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_awk): New declaration.
+ * format-awk.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_awk.
+ * x-awk.h: New file.
+ * x-awk.c: New file.
+ * xgettext.c: Include x-awk.h.
+ (main): Call x_awk_extract_all, x_awk_keyword.
+ (language_to_scanner): Add awk rule.
+ (extension_to_language): Add awk rule.
+ * Makefile.am (noinst_HEADERS): Add x-awk.h.
+ (FORMAT_SOURCE): Add format-awk.c.
+ (xgettext_SOURCES): Add x-awk.c.
+
+2002-02-02 Bruno Haible <bruno@clisp.org>
+
+ * x-python.h: New file.
+ * x-python.c: New file.
+ * xgettext.c: Include x-python.h.
+ (main): Call x_python_extract_all, x_python_keyword.
+ (language_to_scanner): Add Python rule.
+ (extension_to_language): Add Python rule.
+ * Makefile.am (noinst_HEADERS): Add x-python.h.
+ (INCLUDES): Add -I$(top_srcdir)/libuniname.
+ (LIBUNINAME): New variable.
+ (xgettext_SOURCES): Add x-python.c.
+ (xgettext_LDADD): Add $(LIBUNINAME).
+
+2002-02-03 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_header_entry): Terminate the error strings with
+ newlines.
+
+2002-02-03 Bruno Haible <bruno@clisp.org>
+
+ * plural-table.h: New file.
+ * plural-table.c: New file.
+ * msginit.c: Include plural-table.h.
+ (plural_forms): Use plural_table.
+ * msgfmt.c: Include plural-table.h.
+ (check_plural): Print a recommendation about the right plural formula.
+ * Makefile.am (noinst_HEADERS): Add plural-table.h.
+ (libgettextsrc_la_SOURCES): Add plural-table.c.
+
+2002-02-03 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (finalize_header): New function.
+ (main): Call it.
+
+2002-02-03 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (fill_header): Also replace "PACKAGE" in the comment.
+
+2002-02-02 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (INCLUDES): Add -I../lib. Needed for builds with
+ builddir != srcdir on platforms that don't have fnmatch and stdbool.h.
+
+2002-01-31 Bruno Haible <bruno@clisp.org>
+
+ * gettext-0.11 released.
+
+2002-01-31 Bruno Haible <bruno@clisp.org>
+
+ * plural-eval.h: Include eval-plural.h instead of plural-eval.c.
+
+2002-01-30 Bruno Haible <bruno@clisp.org>
+
+ Make the gettext and ngettext programs independent of libgettextlib.
+ * Makefile.am (gettext_LDADD, ngettext_LDADD): New variables.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * x-ycp.c (enum token_type_ty): New enum value token_type_symbol.
+ (x_ycp_lex): Assign a value to tp->string if and only if the type is
+ token_type_string_literal or token_type_symbol.
+ (extract_ycp): Simplify cleanup.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * x-java.l (extract_java): Don't create a plural message if the
+ function can take two string argument but actually has only one
+ string argument.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * x-lisp.c (extract_lisp): Free memory allocated in toplevel_object.
+ * x-elisp.c (string_of_object): Use memcpy.
+ (extract_elisp): Free memory allocated in toplevel_object.
+ * x-librep.c (string_of_object): Use memcpy.
+ (extract_librep): Free memory allocated in toplevel_object.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (ALERT_CHAR): New macro.
+ (phase7_getc): Accept \a and \v as well.
+
+ * x-c.c (free_token): New inline function.
+ (phase6_get): Use it.
+
+2002-01-27 Bruno Haible <bruno@clisp.org>
+
+ * format-elisp.c (format_check): Don't use Java syntax for format
+ argument number in error message.
+ * format-librep.c (format_check): Likewise.
+ * format-pascal.c (format_check): Likewise.
+
+2002-01-24 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (LDADD): Use @LTLIBINTL@ instead of @INTLLIBS@.
+ (libgettextsrc_la_LDFLAGS): Likewise. Use @LTLIBICONV@ instead of
+ @LIBICONV@.
+
+2002-01-21 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c (main): On SunOS4, don't pass option "-q" to grep.
+
+2002-01-19 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_header_entry): Emit error messages including the
+ file name.
+
+2002-01-19 Bruno Haible <bruno@clisp.org>
+
+ * open-po.c (open_po_file): Use 'size_t' instead of 'int'.
+ * x-java.l (translate_esc): Likewise.
+ * read-mo.c (read_mo_file): Use 'unsigned int' instead of 'int'.
+
+2002-01-13 Bruno Haible <bruno@clisp.org>
+
+ * msgl-iconv.c (iconv_message_list): Don't complain by a nonportable
+ encoding name if it has been overridden by an argument canon_from_code.
+
+2002-01-13 Bruno Haible <bruno@clisp.org>
+
+ * msgl-ascii.c (is_ascii_message): Don't consider a message as being
+ ASCII if its msgid or msgid_plural is not ASCII.
+
+2002-01-12 Bruno Haible <bruno@clisp.org>
+
+ * gettext.c (main): Update year in --version output.
+ * ngettext.c (main): Likewise.
+ * msgattrib.c (main): Likewise.
+ * msgcat.c (main): Likewise.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msgfilter.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msginit.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+2002-01-11 Bruno Haible <bruno@clisp.org>
+
+ * write-po.c: Include po-charset.h.
+
+2002-01-11 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (install_sigfpe_handler, uninstall_sigfpe_handler): On AIX,
+ treat SIGTRAP like SIGFPE.
+
+2002-01-09 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (USE_SIGINFO): New macro.
+ (sigfpe_code, sigfpe_handler, install_sigfpe_handler,
+ uninstall_sigfpe_handler, check_plural_eval): Use USE_SIGINFO instead
+ of HAVE_SIGINFO.
+ (install_sigfpe_handler, uninstall_sigfpe_handler): On Irix, treat
+ SIGTRAP like SIGFPE.
+
+2002-01-09 Bruno Haible <bruno@clisp.org>
+
+ * x-java.l (strip_ending_spaces): Fix logic.
+
+2002-01-09 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.l: Avoid endless loop when encountering unterminated string.
+
+2002-01-08 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_elisp'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_elisp entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_elisp): New declaration.
+ * format-elisp.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_elisp.
+ * x-elisp.h: New file.
+ * x-elisp.c: New file.
+ * xgettext.c: Include x-elisp.h.
+ (main): Call x_elisp_extract_all, x_elisp_keyword.
+ (usage): Mention the languages Lisp, EmacsLisp, librep.
+ (language_to_scanner): Add elisp rule.
+ (extension_to_language): Add elisp rule.
+ * Makefile.am (noinst_HEADERS): Add x-elisp.h.
+ (FORMAT_SOURCE): Add format-elisp.c.
+ (xgettext_SOURCES): Add x-elisp.c.
+
+2002-01-06 Bruno Haible <bruno@clisp.org>
+
+ * x-librep.c (read_object): Fix handling of #[ and #(.
+
+2002-01-06 Bruno Haible <bruno@clisp.org>
+
+ * x-lisp.c (read_object): Treat ',.' like ',@'.
+
+2002-01-05 Bruno Haible <bruno@clisp.org>
+
+ Make "msgmerge --update" work better on CJK files even if iconv() is
+ not available.
+ * po-charset.h (po_is_charset_weird): New declaration.
+ (po_is_charset_weird_cjk): Likewise..
+ (po_lex_weird_cjk): New variable declaration.
+ * po-charset.c (po_is_charset_weird): New function, extracted from
+ po_lex_charset_set.
+ (po_is_charset_weird_cjk): New function.
+ (po_lex_weird_cjk): New variable.
+ (po_lex_charset_init): Initialize po_lex_weird_cjk.
+ (po_lex_charset_set): Call po_is_charset_weird and
+ po_is_charset_weird_cjk. Set po_lex_weird_cjk.
+ (po_lex_charset_close): Reset po_lex_weird_cjk.
+ * po-lex.c (mbfile_getc): If po_lex_weird_cjk is set, possibly return
+ a double byte instead of single byte.
+ * write-po.c (wrap): Call po_is_charset_weird_cjk. If it returns true,
+ group double bytes where possible.
+
+2002-01-05 Bruno Haible <bruno@clisp.org>
+
+ * gettext.c: TESTS version is now separate.
+
+2002-01-05 Bruno Haible <bruno@clisp.org>
+
+ * x-java.l (extract_java): Avoid "uninitialized variable" warnings.
+ Free a temporary string.
+
+2002-01-05 Bruno Haible <bruno@clisp.org>
+
+ * Makefile.am (libgettextsrc_la_LDFLAGS): Add -lc. Needed on AIX.
+
+2002-01-05 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (buflen): Make static.
+ * x-librep.c (buflen): Likewise.
+ * x-lisp.c (buflen): Likewise.
+
+2001-12-23 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_string): Use xsetenv instead of setenv.
+ * msginit.c (canonical_locale_charset): Likewise.
+ (get_title): Likewise.
+
+2001-12-22 Bruno Haible <bruno@clisp.org>
+
+ * dir-list.c: Include <stddef.h> instead of <stdlib.h>.
+ * xgettext.h: Likewise.
+ * file-list.c: Include exit.h instead of system.h.
+ * gettext.c: Likewise.
+ * hostname.c: Likewise.
+ * msgattrib.c: Likewise.
+ * msgcat.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * ngettext.c: Likewise.
+ * read-java.c: Likewise.
+ * x-ycp.c: Likewise.
+ * msgcmp.c: Include exit.h instead of system.h. Don't include
+ str-list.h.
+ * msgfilter.c: Likewise.
+ * msgen.c: Include exit.h instead of system.h. Don't include xmalloc.h.
+ * msgfmt.c: Include exit.h, stpcpy.h instead of system.h. Don't
+ include <errno.h>, getline.h.
+ * msggrep.c: Include exit.h instead of system.h. Don't include
+ <fcntl.h>.
+ * msginit.c: Include liballoca.h, <limits.h>, exit.h, pathname.h
+ instead of system.h.
+ * msgl-cat.c: Include liballoca.h, exit.h instead of system.h.
+ Include <stddef.h> instead of <stdlib.h>.
+ * msgl-charset.c: Likewise.
+ * msgl-iconv.c: Include liballoca.h, exit.h instead of system.h.
+ * write-po.c: Likewise.
+ * msgmerge.c: Include liballoca.h, exit.h, strcase.h, stpcpy.h,
+ stpncpy.h instead of system.h.
+ * format-java.c: Include liballoca.h instead of system.h.
+ * format-lisp.c: Don't include system.h.
+ * open-po.c: Include pathname.h, xmalloc.h instead of system.h.
+ Don't include <sys/types.h>, error.h.
+ * po-charset.c: Include liballoca.h, string.h, strcase.h instead of
+ system.h. Don't include error.h. Include progname.h.
+ (program_name): Remove declaration.
+ * po-gram-gen.y: Include <string.h>.
+ * po-hash-gen.y: Include <string.h>.
+ * po-lex.c: Include exit.h instead of system.h. Don't include
+ <sys/types.h>.
+ * read-mo.c: Include binary-io.h, exit.h instead of system.h.
+ Include <stddef.h> instead of <stdlib.h>.
+ * urlget.c: Include <unistd.h>, binary-io.h, exit.h instead of
+ system.h.
+ (O_BINARY): Remove fallback definition.
+ * write-java.c: Include liballoca.h, pathname.h instead of system.h.
+ * write-mo.c: Include liballoca.h, binary-io.h, exit.h instead of
+ system.h. Don't include msgfmt.h.
+ * x-c.c: Include exit.h instead of system.h. Don't include <ctype.h>.
+ * x-librep.c: Include exit.h instead of system.h. Include hash.h.
+ * x-lisp.c: Likewise.
+ * x-rst.c: Include exit.h instead of system.h. Include <stddef.h>
+ instead of <stdlib.h>.
+ * xgettext.c: Include <string.h>, exit.h, pathname.h, strcase.h,
+ stpcpy.h instead of system.h. Include str-list.h. Don't include
+ <sys/param.h>, <pwd.h>, <sys/types.h>, <unistd.h>, getline.h.
+ (getpwuid): Remove declaration.
+
+2001-12-21 Bruno Haible <bruno@clisp.org>
+
+ * format-lisp.c: Include minmax.h.
+
+2001-12-21 Bruno Haible <bruno@clisp.org>
+
+ * file-list.c: Include gettext.h instead of libgettext.h.
+ * format-c.c: Likewise.
+ * format-java.c: Likewise.
+ * format-librep.c: Likewise.
+ * format-lisp.c: Likewise.
+ * format-pascal.c: Likewise.
+ * format-python.c: Likewise.
+ * format-ycp.c: Likewise.
+ * gettext.c: Likewise.
+ * hostname.c: Likewise.
+ * msgattrib.c: Likewise.
+ * msgcat.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgen.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgfilter.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msggrep.c: Likewise.
+ * msginit.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-charset.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * ngettext.c: Likewise.
+ * open-po.c: Likewise.
+ * po-charset.c: Likewise.
+ * po-gram-gen.y: Likewise.
+ * po-lex.c: Likewise.
+ * read-java.c: Likewise.
+ * read-mo.c: Likewise.
+ * read-po.c: Likewise.
+ * urlget.c: Likewise.
+ * write-java.c: Likewise.
+ * write-mo.c: Likewise.
+ * write-po.c: Likewise.
+ * x-c.c: Likewise.
+ * x-librep.c: Likewise.
+ * x-lisp.c: Likewise.
+ * x-po.c: Likewise.
+ * x-rst.c: Likewise.
+ * x-ycp.c: Likewise.
+ * xgettext.c: Likewise.
+
+2001-12-21 Bruno Haible <bruno@clisp.org>
+
+ * message.h (message_merge): Remove declaration.
+ * message.c (msgfmt_check_pair_fails, message_merge): Move to
+ msgmerge.c.
+ * msgmerge.c (msgfmt_check_pair_fails, message_merge): Moved to here
+ from message.c.
+
+2001-12-20 Bruno Haible <bruno@clisp.org>
+
+ * format-java.c: Include system.h.
+ * format-lisp.c (parse_upto): Fix prototype. char promotes to int.
+ * po-lex.c (mb_iseq, mb_setascii): Likewise.
+ * po-hash-gen.y: alloca fix for AIX 3.
+ * x-lisp.c (syntax_code_of, read_char_syntax, attribute_of, is_number):
+ Move prototypes, to avoid compilation errors on AIX 3.
+ (syntax_code_of, attribute_of): Fix prototype. unsigned char promotes
+ to int.
+ * x-java.l (TOKEN_TYPE): Remove trailing comma in enum.
+ (PARSER_STATE): Likewise.
+ * msgfilter.c: Include <sys/select.h> on AIX.
+
+2001-12-12 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c: Include <limits.h>.
+ (copyright_holder): New variable.
+ (foreign_user): Remove variable.
+ (long_options): Add --copyright-holder option.
+ (main): Implement --copyright-holder option.
+ (usage): Document --copyright-holder option.
+ (construct_header): Use the copyright_holder variable. If it is
+ empty, add a "public domain" comment.
+
+2001-12-17 Bruno Haible <bruno@clisp.org>
+
+ * msggrep.c: Include <fnmatch.h>.
+ (filename_list_match): New function.
+ (is_message_selected): Use it instead of string_list_member.
+
+2001-12-16 Bruno Haible <bruno@clisp.org>
+
+ * message.h (format_type): New enum value 'format_librep'.
+ (NFORMATS): Increment.
+ * message.c (format_language): Add format_librep entry.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_librep): New declaration.
+ * format-librep.c: New file.
+ * format.c (formatstring_parsers): Add formatstring_librep.
+ * x-librep.h: New file.
+ * x-librep.c: New file.
+ * xgettext.c: Include x-librep.h.
+ (main): Call x_librep_extract_all, x_librep_keyword.
+ (language_to_scanner): Add librep rule.
+ (extension_to_language): Add librep rule.
+ * Makefile.am (noinst_HEADERS): Add x-librep.h.
+ (FORMAT_SOURCE): Add format-librep.c.
+ (xgettext_SOURCES): Add x-librep.c.
+
+2001-12-15 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (check_plural): Use ngettext for two messages.
+ Reported by Primoz Peterlin <primoz.peterlin@biofiz.mf.uni-lj.si>.
+
+2001-12-15 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (remember_a_message_plural): Mark the message as xx-format
+ if the English plural form has xx format syntax and the singular form
+ may or may not have xx format syntax.
+
+2001-12-15 Bruno Haible <bruno@clisp.org>
+
+ * format.h (struct formatstring_parser): Add 'equality' and
+ 'pretty_msgstr' arguments to 'check' field.
+ * format-c.c (format_check): Add 'equality' and 'pretty_msgstr'
+ arguments.
+ * format-java.c (format_check): Likewise.
+ * format-lisp.c (format_check): Likewise.
+ * format-pascal.c (format_check): Likewise.
+ * format-python.c (format_check): Likewise.
+ * format-ycp.c (format_check): Likewise.
+ * msgfmt.c (check_pair): Check messages with plural forms as well.
+ * message.c (msgfmt_check_pair_fails): Add msgid_plural, msgstr_len
+ arguments. Check messages with plural forms as well.
+ (message_merge): Check messages with plural forms as well.
+
+2001-12-11 Bruno Haible <bruno@clisp.org>
+
+ * x-java.l (strip_ending_spaces): Fix isspace call.
+
+ * xgettext.h (xgettext_omit_header): Renamed from omit_header.
+ * xgettext.c (xgettext_omit_header): Likewise.
+ (long_options, main, remember_a_message): Update.
+ * x-po.c (extract_directive_message): Update.
+
+2001-12-09 Bruno Haible <bruno@clisp.org>
+
+ * x-lisp.h: New file.
+ * x-lisp.c: New file.
+ * xgettext.c: Include x-lisp.h.
+ (main): Call x_lisp_extract_all, x_lisp_keyword.
+ (language_to_scanner): Add Lisp rule. Remove preliminary Lisp rule.
+ (extension_to_language): Add Lisp rule.
+ * Makefile.am (noinst_HEADERS): Add x-lisp.h.
+ (xgettext_SOURCES): Add x-lisp.c.
+
+2001-12-09 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.h (split_keywordspec): New declaration.
+ * xgettext.c (split_keywordspec): New function.
+ * x-c.c (x_c_keyword): Use split_keywordspec.
+ * x-java.l (extract_keyword, extract_msgid_arg,
+ extract_msgid_plural_arg): Remove functions.
+ (x_java_keyword): Use split_keywordspec.
+
+2001-12-09 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c: Reorder.
+
+2001-12-08 Bruno Haible <bruno@clisp.org>
+
+ * msgattrib.c (process_message_list): Never make the header entry
+ obsolete.
+
+2001-12-08 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c (main): Fix getopt_long string: -i takes an argument.
+
+2001-12-08 Bruno Haible <bruno@clisp.org>
+
+ * xgettext.c (remember_a_message): Don't print the line number if it
+ is = (size_t)(-1).
+
+2001-12-08 Bruno Haible <bruno@clisp.org>
+
+ * msgfilter.c: Renamed from msgexec.c.
+ * msgexec.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add msgfilter.
+ (msgfilter_SOURCES): New variable.
+ (msgfilter_LDADD): New variable.
+
+2001-12-07 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (keep_header): New variable.
+ (long_options): New option --keep-header.
+ (usage): Document option --keep-header.
+ (process_message): Implement --keep-header.
+
+2001-12-06 Bruno Haible <bruno@clisp.org>
+
+ * format.h (struct formatstring_parser): Add 'noisy' argument to
+ 'check' field.
+ * format-c.c (format_check): Add 'noisy' argument.
+ * format-java.c (format_check): Likewise.
+ * format-lisp.c (format_check): Likewise.
+ * format-pascal.c (format_check): Likewise.
+ * format-python.c (format_check): Likewise.
+ * format-ycp.c (format_check): Likewise.
+ * msgfmt.c (check_pair): Pass noisy=true.
+ * message.c: Include format.h.
+ (msgfmt_check_pair_fails): New function.
+ (message_merge): Set fuzzy flag when adding a -format specifier that
+ needs the translator's attention.
+
+2001-12-10 Bruno Haible <bruno@clisp.org>
+
+ * x-java.l (tailcmp): If s1 ends with s2, it must be either equal to
+ s2 or have a dot right before s2.
+
+2001-12-01 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (project_id): Make it possible to specify the location
+ of the project-id script through an environment variable.
+ (project_id_version): Likewise.
+
+2001-12-03 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.l (tailcmp): New function.
+ (do_compare): Use it instead of strcmp.
+ (extract_java): After seeing an invocation, don't set the state to
+ STATE_NONE.
+
+2001-11-30 Bruno Haible <bruno@clisp.org>
+
+ * message.c (message_merge): Determine whether postprocessing is
+ needed.
+ * msgmerge.c (match_domain): Postprocess some problematic cases.
+ * Makefile.am (libgettextsrc_la_SOURCES): Add plural.c.
+ (msgfmt_SOURCES): Remove plural.c.
+
+2001-11-29 Bruno Haible <bruno@clisp.org>
+
+ * plural-eval.c: Include config.h and plural-exp.h.
+
+2001-11-27 Bruno Haible <bruno@clisp.org>
+
+ * msgfmt.c (struct msgfmt_class_ty): New field
+ 'has_nonfuzzy_header_entry'.
+ (format_constructor): Initialize it.
+ (format_debrief): Give a warning if the header entry exists but is
+ fuzzy.
+ (format_directive_message): Update has_nonfuzzy_header_entry.
+
+2001-11-25 Bruno Haible <bruno@clisp.org>
+
+ * x-c.c (comment_start, comment_add, comment_line_end): New inline
+ functions.
+ (phase4_getc): Use them.
+
+2001-11-25 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (no_translator): New variable.
+ (long_options): Add option --no-translator.
+ (main): Handle option --no-translator.
+ (usage): Document option --no-translator.
+ (po_revision_date): Add header argument. Return POT's creation date
+ if no_translator.
+ (last_translator): Return a dummy string if no_translator.
+ (language_team): Likewise.
+ (fields): Allow getter with 0 or 1 argument.
+ (get_field): New function.
+ (fill_header): Call getter with 0 or 1 argument.
+
+2001-11-25 Bruno Haible <bruno@clisp.org>
+
+ * msginit.c (canonical_locale_charset): Return the charset of 'locale'
+ if available.
+ (get_title): Return a correct value even if 'locale' is a nonexistent
+ locale.
+
+2001-11-24 Bruno Haible <bruno@clisp.org>
+
+ * urlget.c (fetch): Call execute_java_class with quiet=true. If Java
+ program failed, try wget or lynx.
+
+ * read-java.c (msgdomain_read_java): Update for changed
+ execute_java_class parameter list.
+
+2001-11-24 Bruno Haible <bruno@clisp.org>
+
+ * msgexec.c (process_message): Really process each plural form once.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (is_nonobsolete): New function.
+ (main): Remove obsolete messages after reading all input files.
+ (check_header_entry): New function, extracted from
+ format_directive_message.
+ (format_directive_message): Perform duplicate checking also on
+ obsolete, untranslated and fuzzy messages, like msgmerge and the
+ other tools do. Put also obsolete, untranslated and fuzzy messages
+ on the result list, but mark them obsolete.
+ (read_po_file): Call po_lex_pass_obsolete_entries.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c (po_callback_message): Ignore obsolete header entries, for
+ consistency with msgdomain_list_print(), compare_po_locale_charsets(),
+ iconv_message_list(), catenate_msgdomain_list().
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (struct msg_domain): Remove symbol_tab field.
+ (main, new_domain): Update.
+ (format_directive_message): Use message_list_search instead of
+ explicit hash table calls.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h: Include hash.h.
+ (message_list_ty): Add use_hashtable, htable fields.
+ (message_list_alloc): Add 'use_hashtable' argument.
+ (msgdomain_alloc): Likewise.
+ (msgdomain_list_ty): Add use_hashtable field.
+ (msgdomain_list_alloc): Add 'use_hashtable' argument.
+ * message.c: Include hash.h.
+ (message_list_alloc): Add 'use_hashtable' argument.
+ (message_list_free): Delete the hash table if present.
+ (message_list_append): If a hash table is in use, verify no duplicates.
+ (message_list_prepend): Likewise.
+ (message_list_remove_if_not): Remove hash table after removal.
+ (message_list_search): Use hash table if present.
+ (msgdomain_alloc): Add 'use_hashtable' argument.
+ (msgdomain_list_alloc): Add 'use_hashtable' argument.
+ (msgdomain_list_sublist): Use 'use_hashtable'.
+ * msgcmp.c (compare): Update.
+ (compare_constructor): Allocate a message list with hash table.
+ * msgfmt.c (new_domain): Allocate a message list with hash table.
+ Remove impossible error message.
+ * msgl-cat.c (catenate_msgdomain_list): Allocate message lists with
+ hash table.
+ * msgmerge.c (merge): Update.
+ * msgunfmt.c (main): Update.
+ * read-po.c (readall_constructor): Allocate a message list with hash
+ table if and only if duplicates will lead to an error message.
+ * xgettext.c (main): Allocate a message list with hash table, to
+ speed up insertion.
+ (exclude_directive_message): Allocate a message list with hash table,
+ to speed up exclusion test.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (msgdomain_list_sublist): Change last argument to bool.
+ * message.c (msgdomain_list_sublist): Likewise.
+ * msgcmp.c (compare): Update msgdomain_list_sublist calls.
+ (compare_constructor, compare_directive_message): Likewise.
+ * msgl-cat.c (catenate_msgdomain_list): Likewise.
+ * msgmerge.c (merge): Likewise.
+ * read-po.c (readall_constructor, readall_directive_message): Likewise.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (format_directive_message): Finish 2001-08-30 patch.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (accelerator_char): New variable.
+ (long_options): --check-accelerators has optional argument.
+ (main): Store --check-accelerators value in accelerator_char.
+ (usage): --check-accelerators has optional argument.
+ (check_pair): Use accelerator_char instead of hardwired '&'.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * msginit.c (main): Print a blank line.
+ (get_title): Always use the English title first.
+ (fill_header): Update accordingly.
+
+2001-11-17 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (NFORMATS): Fix misapplied patch.
+
+2001-11-11 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (check_accelerators): New variable.
+ (long_options): Add --check-accelerators.
+ (main): Handle --check-accelerators.
+ (usage): Document --check-accelerators option.
+ (check_pair): Implement check_accelerators handling.
+
+2001-11-11 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-cat.c (catenate_msgdomain_list): Fix a typo in 2001-11-01 patch.
+ Reported by Karl Eichwalder.
+
+2001-11-11 Bruno Haible <haible@clisp.cons.org>
+
+ * msginit.c (verbose): Remove variable.
+ (long_options): Remove --verbose.
+ (main): Remove --verbose option handling.
+ (usage): Remove --verbose documentation.
+ (language_team_address): Don't pass a verbose option to the subprocess.
+
+2001-11-08 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h: New enum value format_smalltalk.
+ * message.c (format_language): Add value for format_smalltalk.
+ (format_language_pretty): Likewise.
+ * format.h (formatstring_smalltalk): New declaration.
+ * format-ycp.c (formatstring_smalltalk): New variable.
+ * format.c (formatstring_parsers): Add value for format_smalltalk.
+
+2001-11-05 Bruno Haible <haible@clisp.cons.org>
+
+ * format-lisp.c (parse_upto): Remove 'base' argument. It is always 0.
+ (format_parse): Update.
+
+ * msgl-equal.c: Include string.h.
+ * str-list.c: Likewise.
+
+2001-11-03 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (line_comment): Remove variable, so default value is
+ always 1.
+
+2001-11-03 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.c (extract_parenthesized): New function, extracted from
+ extract_c.
+ (extract_c): Call extract_parenthesized.
+
+2001-11-03 Bruno Haible <haible@clisp.cons.org>
+
+ Add YCP plural form support.
+ * x-ycp.c (enum token_type_ty): New value token_type_comma.
+ (x_ycp_lex): Recognize comma.
+ (extract_ycp): Also recognize the _("singular","plural" pattern.
+
+2001-11-01 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (check_plural_eval): Fix #if mistake.
+
+2001-11-01 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-iconv.h (iconv_message_list): Add argument 'canon_from_code'.
+ * msgl-iconv.c (iconv_message_list): Add argument 'canon_from_code'.
+ (iconv_msgdomain_list): Update.
+ * write-java.c (msgdomain_write_java): Update.
+ * msgl-cat.c (catenate_msgdomain_list): Pass the canon_charsets[n][k]
+ to iconv_message_list; the header entry that contained the charset
+ specification may already have been removed from the message list.
+
+2001-11-01 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-equal.h (string_list_equal): New declaration.
+ * msgl-equal.c (string_list_equal): Export function.
+ * message.h (struct altstr): New fields 'comment', 'comment_dot'.
+ * msgl-cat.c: Include msgl-equal.h.
+ (catenate_msgdomain_list): If all alternative comments are equal,
+ use one copy of them, instead of concatenating them. Likewise for dot
+ comments.
+ * Makefile.am (libgettextsrc_la_SOURCES): Add msgl-equal.c.
+ (msgmerge_SOURCES): Remove msgl-equal.c.
+
+2001-10-31 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (noinst_HEADERS): Add read-java.h, write-java.h,
+ format.h, msgunfmt.h.
+ (EXTRA_DIST): Add project-id, gnu/gettext/*.java.
+ * message.h (NFORMATS): Define via #define, not via an enum value.
+ * format.h (struct formatstring_parser): Use PARAMS.
+ * format-c.c (FAT_SIZE_MASK): Remove extraneous backslashes.
+ * po-charset.c (po_lex_charset_set): Fix syntax error.
+ * write-java.c (write_java_string): Drop hexdigit[] size.
+ * x-java.l (add_object): Remove C++ style comment.
+
+2001-10-28 Bruno Haible <haible@clisp.cons.org>
+
+ * hostname.c: Include "progname.h".
+ (program_name): Remove variable.
+ (main): Call set_program_name instead of setting program_name.
+
+2001-10-28 Bruno Haible <haible@clisp.cons.org>
+
+ Create a libgettextsrc shared library.
+ * Makefile.am (lib_LTLIBRARIES): New variable.
+ (libgettextsrc_la_SOURCES): New variable.
+ (msgcmp_SOURCES, msgfmt_SOURCES, msgmerge_SOURCES, msgunfmt_SOURCES,
+ xgettext_SOURCES, msgattrib_SOURCES, msgcat_SOURCES, msgcomm_SOURCES,
+ msgconv_SOURCES, msgen_SOURCES, msgexec_SOURCES, msggrep_SOURCES,
+ msginit_SOURCES, msguniq_SOURCES): Remove source files listed in
+ libgettextsrc_la_SOURCES.
+ (libgettextsrc_la_LDFLAGS): New variable.
+ (msgcmp_LDADD, msgfmt_LDADD, msgmerge_LDADD, msgunfmt_LDADD,
+ xgettext_LDADD, msgattrib_LDADD, msgcat_LDADD, msgcomm_LDADD,
+ msgconv_LDADD, msgen_LDADD, msgexec_LDADD, msggrep_LDADD,
+ msginit_LDADD, msguniq_LDADD): Add libgettextsrc.la. Remove all
+ dependencies already listed in libgettextsrc_la_LDFLAGS.
+ (po-lex.lo): Depend on po-gram-gen2.h.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (LDADD): Replace libnlsut.a with libgettextlib.la.
+ (*_LDADD): Likewise.
+
+2001-10-26 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.h (EXTENSIONS_C): Add ".hxx".
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msgmerge.c (match_domain): Change type of 'processed' argument to
+ 'unsigned int *'.
+ (merge): Change type of 'processed' to 'unsigned int'.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ Avoid warnings on platforms where size_t is larger than int.
+ * msgfmt.c (check_pair): Use an 'unsigned int' variable instead of
+ 'size_t', to avoid warnings.
+ * po-lex.h (po_gram_error): Cast line number to 'unsigned long'.
+ * po-lex.c (po_gram_error): Likewise.
+ * x-c.c (extract_c): Likewise.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msgexec.c [BeOS]: Fix #ifdef conditional.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * read-java.c (execute_and_read_po_output): Return false, not 0.
+
+2001-10-11 Bruno Haible <haible@clisp.cons.org>
+
+ * msgattrib.c (usage): Fix: --sort-output doesn't deal with duplicates.
+ * msgcat.c (usage): Likewise.
+ * msgcomm.c (usage): Likewise.
+ * msgconv.c (usage): Likewise.
+ * msgen.c (usage): Likewise.
+ * msgexec.c (usage): Likewise.
+ * msggrep.c (usage): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * msguniq.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+
+2001-10-09 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (memcpy_small): New function.
+ (wrap): Use it instead of memcpy. Optimize handling of escape
+ characters.
+
+2001-10-09 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (putc): Define as putc_unlocked if available.
+
+2001-10-09 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.c (getc): Define as getc_unlocked if available.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msginit.c: New file.
+ * project-id: New file.
+ * Makefile.am (bin_PROGRAMS): Add msginit.
+ (projectsdir): New variable.
+ (DEFS): Add -DLIBDIR and -DPROJECTSDIR.
+ (msginit_SOURCES): New variable.
+ (msginit_LDADD): New variable.
+ (install-exec-local): Also install project-id.
+ (uninstall-local): Also uninstall project-id.
+
+2001-10-21 Bruno Haible <haible@clisp.cons.org>
+
+ * gnu/gettext/GetURL.java: New file.
+ * urlget.c: New file.
+ * Makefile.am (noinst_PROGRAMS): Add urlget.
+ (urlget_SOURCES): New variable.
+ (install-exec-local): Also install urlget.
+ (uninstall-local): Also uninstall urlget.
+ (gnu/gettext/GetURL.class): New rule.
+ (gettext.jar): Also include gnu/gettext/GetURL*.class.
+
+2001-10-08 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-english.h: New file.
+ * msgl-english.c: New file, extracted from msgen.c.
+ * msgen.c: Include msgl-english.h.
+ (english): Move to msgl-english.c.
+ (main): Update accordingly.
+ * Makefile.am (noinst_HEADERS): Add msgl-english.h.
+ (msgen_SOURCES): Add msgl-english.c.
+
+2001-10-08 Bruno Haible <haible@clisp.cons.org>
+
+ * po-time.h: New file.
+ * po-time.c: New file, extracted from xgettext.c.
+ * xgettext.c: Include po-time.h.
+ (difftm): Move to po-time.c.
+ (construct_header): Move some code to po-time.c. Call po_strftime.
+ * Makefile.am (noinst_HEADERS): Add po-time.h.
+ (xgettext_SOURCES): Add po-time.c.
+
+2001-09-29 Bruno Haible <haible@clisp.cons.org>
+
+ * user-email.in: New file.
+ * Makefile.am (install-exec-local): Install user-email.
+ (uninstall-local): Uninstall user-email.
+ (DISTCLEANFILES): Add user-email.
+
+2001-09-29 Bruno Haible <haible@clisp.cons.org>
+
+ * hostname.c: New file.
+ * Makefile.am (noinst_PROGRAMS, hostname_SOURCES): New variables.
+ (install-exec-local, installdirs-local, uninstall-local): New rules.
+
+2001-10-10 Bruno Haible <haible@clisp.cons.org>
+
+ * msgexec.c (process_string): Update for changed create_pipe_bidi(),
+ wait_subprocess().
+ * msggrep.c (is_string_selected): Update for changed create_pipe_out(),
+ wait_subprocess().
+ * read-java.c (execute_and_read_po_output): Update for changed
+ create_pipe_in(), wait_subprocess().
+
+2001-10-08 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (remember_a_message): When the comment tag is seen,
+ remember all remaining comment lines, not just one.
+
+2001-09-25 Bruno Haible <haible@clisp.cons.org>
+
+ Upgrade to automake-1.5.
+ * Makefile.am (AUTOMAKE_OPTIONS): Add 'no-dependencies'.
+ (COMMON_SOURCE): Renamed from COMMON_SOURCES.
+ (FORMAT_SOURCE): Renamed from FORMAT_SOURCES.
+ * ylwrap: Remove file.
+
+2001-09-22 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (extractor_func): New typedef.
+ (scanner_fp): Remove typedef.
+ (main): Use extractor_func and call it via extract_from_file.
+ (extract_from_file): New function.
+ (scan_c_file, scan_po_file, scan_java_file, scan_ycp_file,
+ scan_rst_file): Remove functions.
+ (language_to_extractor): Renamed from language_to_scanner.
+ * x-c.h (SCANNERS_C): Use extract_c instead of scan_c_file.
+ * x-java.h (SCANNERS_JAVA): Use extract_java instead of scan_java_file.
+ * x-po.h (SCANNERS_PO): Use extract_po instead of scan_po_file.
+ * x-rst.h (SCANNERS_RST): Use extract_rst instead of scan_rst_file.
+ * x-ycp.h (SCANNERS_YCP): Use extract_ycp instead of scan_ycp_file.
+
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * plural-eval.c: New file.
+ * msgfmt.c: Include plural-exp.h.
+ (sigfpe_exit, sigfpe_code): New variables.
+ (sigfpe_handler, install_sigfpe_handler, uninstall_sigfpe_handler,
+ check_plural_eval, check_plural): New functions.
+ (main): Call it if --check-header is enabled.
+ * Makefile.am (msgfmt_SOURCES): Add plural-eval.c.
+
+2001-10-04 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.l (object_list): New type.
+ (INITIAL_OBJECT_LIST_SIZE, OBJECT_LIST_GROWTH): New macros.
+ (java_keyword): New type.
+ (strip_ending_spaces): New function.
+ (object_list_alloc, object_list_destroy, get_num_objects, get_object,
+ add_object, alloc_keyword): New functions.
+ (java_keywords): Change type.
+ (extract_keyword, extract_msgid_arg, extract_msgid_plural_arg): New
+ functions.
+ (is_keyword): Change return type.
+ (x_java_keyword): Update.
+ (extract_java): Count arguments, and support plural keywords like
+ ngettext.
+
+2001-09-19 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.h (omit_header): New declaration.
+ * xgettext.c (omit_header): Make global.
+ * x-po.c (extract_directive_message): Don't throw the header entry
+ away if omit_header is true.
+
+2001-09-19 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (msgdomain_list_print): Treat charset=CHARSET as if it
+ were absent, i.e. like ASCII, not like an unknown 8-bit encoding.
+
+2001-09-18 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-equal.h: New file.
+ * msgl-equal.c: New file.
+ * msgmerge.c: Include msgl-equal.h, backupfile.h, copy-file.h.
+ (update_mode, version_control_string, backup_suffix_string): New
+ variables.
+ (long_options): Add --backup, --suffix, --update.
+ (main): Accept options --backup, --suffix, -U/--update. If in update
+ mode, compare old and new message lists, backup the def.po file, and
+ overwrite the def.po file.
+ (usage): Document --backup, --suffix, -U/--update.
+ (DOT_FREQUENCY): Renamed from DOT_FREQUENCE.
+ (merge): Also return def.po message list. Don't share messages between
+ def and result; copy instead.
+ * Makefile.am (noinst_HEADERS): Add msgl-equal.h.
+ (msgmerge_SOURCES): Add msgl-equal.c.
+
+2001-09-19 Bruno Haible <haible@clisp.cons.org>
+
+ * po-hash-gen.y (number): Change type from 'int' to 'size_t'.
+ (filepos): Accept strings without line number.
+ (yylex): Change n from 'int' to 'size_t'.
+ * po.h (po_method_ty): In field comment_filepos, change line's type
+ from 'int' to 'size_t'.
+ (po_callback_comment_filepos): Change line's type from 'int' to
+ 'size_t'.
+ * read-po.c (readall_comment_filepos): Likewise.
+ * x-po.c (extract_comment_filepos): Likewise.
+ * po.c (po_comment_filepos): Likewise.
+ (po_callback_comment_filepos): Likewise.
+ (po_callback_comment): Reduce the possibility that po_hash succeeds
+ if the comment is not a Sun file position comment.
+
+2001-09-16 Bruno Haible <haible@clisp.cons.org>
+
+ * x-rst.h: New file.
+ * x-rst.c: New file.
+ * xgettext.c: Include x-rst.h.
+ (scan_rst_file): New function.
+ (language_to_scanner): Add RST rule.
+ (extension_to_language): Add RST rule.
+ * write-po.c (message_print): Don't print the line number if it is
+ = (size_t)(-1).
+ * Makefile.am (noinst_HEADERS): Add x-rst.h.
+ (xgettext_SOURCES): Add x-rst.c.
+
+2001-09-20 Bruno Haible <haible@clisp.cons.org>
+
+ * format-pascal.c: New file.
+ * format.h (formatstring_pascal): New declaration.
+ * message.h (enum format_type): Add format_pascal.
+ * message.c (format_language): Add format_pascal entry.
+ (format_language_pretty): Likewise.
+ * format.c (formatstring_parsers): Add formatstring_pascal.
+ * Makefile.am (FORMAT_SOURCES): Add format-pascal.c.
+
+2001-09-16 Bruno Haible <haible@clisp.cons.org>
+
+ * x-ycp.h: New file.
+ * x-ycp.c: New file.
+ * xgettext.c: Include x-ycp.h.
+ (usage): Mention YCP.
+ (scan_ycp_file): New function.
+ (language_to_scanner): Add YCP rule. Remove preliminary YCP rule.
+ (extension_to_language): Add YCP rule.
+ * Makefile.am (noinst_HEADERS): Add x-ycp.h.
+ (xgettext_SOURCES): Add x-ycp.c.
+
+2001-09-17 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c: Include basename.h.
+ * ngettext.c: Likewise.
+ * msgattrib.c: Likewise.
+ * msgcat.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgconv.c: Likewise.
+ * msgen.c: Likewise.
+ * msgexec.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msggrep.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-charset.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msguniq.c: Likewise.
+ * xgettext.c: Likewise.
+ * po-charset.c: Include <stdlib.h> and basename.h.
+
+2001-09-16 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.c (phase4_getc): Rename local variable 'state' to
+ 'last_was_star'.
+
+2001-09-11 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (COMMON_SOURCES, FORMAT_SOURCES): New variables.
+ (msgcmp_SOURCES, msgfmt_SOURCES, msgmerge_SOURCES, msgunfmt_SOURCES,
+ xgettext_SOURCES, msgattrib_SOURCES, msgcat_SOURCES, msgcomm_SOURCES,
+ msgconv_SOURCES, msgen_SOURCES, msgexec_SOURCES, msggrep_SOURCES,
+ msguniq_SOURCES): Simplify.
+
+2001-09-09 Bruno Haible <haible@clisp.cons.org>
+
+ * file-list.c: Include system.h.
+ * format-c.c: Include xmalloc.h instead of system.h.
+ * format-ycp.c: Likewise.
+ * read-po.c: Likewise.
+ * x-po.c: Likewise.
+ * format-java.c: Include xmalloc.h.
+ * format-lisp.c: Likewise.
+ * format-python.c: Likewise.
+ * gettext.c: Likewise.
+ * msgen.c: Likewise.
+ * msgexec.c: Likewise.
+ * msggrep.c: Likewise.
+ * ngettext.c: Likewise.
+ * po-lex.c: Likewise.
+ * read-mo.c: Likewise.
+ * x-c.c: Likewise.
+ * message.c: Include xmalloc.h and strstr.h.
+ * msgfmt.c: Likewise.
+ * write-po.c: Likewise.
+ * msgl-cat.c: Include string.h, xmalloc.h and strstr.h.
+ * msgl-iconv.c: Likewise.
+ * msgl-charset.c: Include strstr.h.
+ * po-charset.c: Likewise.
+ * po-gram-gen.y: Include stdlib.h, xmalloc.h instead of system.h.
+ * po-hash-gen.y: Likewise.
+ * str-list.c: Likewise.
+ * po.c: Include string.h, xmalloc.h instead of system.h.
+ * write-java.c: Include string.h, xmalloc.h.
+ * x-java.l: Include stdlib.h, xmalloc.h, strstr.h instead of system.h.
+ * xgettext.c: Include xmalloc.h, strstr.h, xerror.h.
+ (main, construct_header): Use xasprintf instead of asprintf.
+
+2001-09-24 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.c (mb_iseq): Compare the byte sequence, not the Unicode
+ equivalent. This is necessary for non-ASCII-compatible encodings like
+ SHIFT_JIS.
+
+2001-09-08 Bruno Haible <haible@clisp.cons.org>
+
+ * msgunfmt.h: New file.
+ * msgunfmt.c (verbose): New variable.
+ (java_mode, java_resource_name, java_locale_name): New variables.
+ (long_options): Add --java, --locale, --resource, --verbose.
+ (main): Recognize new options -j, -l, -r, -v. Handle java mode
+ differently.
+ (usage): Document --java, --resource, --locale, --verbose.
+ * read-java.h: New file.
+ * read-java.c: New file.
+ * read-po.h (read_po): New declaration.
+ * read-po.c (read_po): New function.
+ * gnu/gettext/DumpResource.java: New file.
+ * Makefile.am (jardir): New variable.
+ (DEFS): Define GETTEXTJAR, for read-java.c.
+ (JAR): New variable.
+ (JAVACOMP): New variable.
+ (msgunfmt_SOURCES): Add open-po.c, po-gram-gen.y, po-hash-gen.y,
+ po-charset.c, po-lex.c, po.c, read-po.c, dir-list.c, read-java.c.
+ (all-local, all-java-no, all-java-yes, gnu/gettext/DumpResource.class,
+ gettext.jar, install-data-local, install-java-no, install-java-yes,
+ installdirs-local, installdirs-java-no, installdirs-java-yes,
+ uninstall-local, uninstall-java-no, uninstall-java-yes): New rules.
+ (CLEANFILES): New variable.
+
+2001-09-08 Bruno Haible <haible@clisp.cons.org>
+
+ * msgexec.c (nonintr_select): Add function prototype.
+
+ * xgettext.c (scan_java_file): Add function prototype.
+
+2001-09-23 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (remember_a_message): Warn about empty msgid if it
+ conflicts with construct_header().
+
+2001-09-08 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcat.c (main): Move the option checking after the handling of
+ --version and --help, but before opening of any file.
+ * msgcomm.c (main): Move the option checking after the handling of
+ --version and --help.
+ * msguniq.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+ * msgconv.c (main): Additional option checking for --sort-by-file.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+
+2001-09-08 Bruno Haible <haible@clisp.cons.org>
+
+ * dir-list.c: Include specification header before all other headers.
+ * file-list.c: Likewise.
+ * message.c: Likewise.
+ * msgl-cat.c: Likewise.
+ * msgl-charset.c: Likewise.
+ * msgl-iconv.c: Likewise.
+ * open-po.c: Likewise.
+ * po.c: Likewise.
+ * po-gram-gen.y: Likewise.
+ * po-hash-gen.y: Likewise.
+ * po-lex.c: Likewise.
+ * read-po.c: Likewise.
+ * str-list.c: Likewise.
+ * write-mo.c: Likewise.
+ * write-po.c: Likewise.
+ * msgfmt.h: Don't include message.h.
+ * open-po.h: Include <stdio.h>, for FILE.
+ * write-mo.h: Include message.h and <stddef.h>.
+
+2001-09-21 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.l (create_char_buf): Fix problem with empty messages.
+ (//): Remove leading "//" from result.
+ (x_java_keyword): Handle NULL argument.
+
+2001-09-19 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.l (is_keyword): Swap arguments of do_compare.
+
+2001-09-02 Bruno Haible <haible@clisp.cons.org>
+
+ * write-java.h: New file.
+ * write-java.c: New file.
+ * plural.c: New file.
+ * msgfmt.c: Include write-java.h.
+ (java_mode, assume_java2, java_resource_name, java_locale_name,
+ java_class_directory): New variables.
+ (long_options): Add --java, --java2, --locale, --resource.
+ (main): Add command line option -d, -j, -l, -r. Handle them and
+ --java2. Forbid some options depending on whether --java was given
+ or not. In java_mode, call msgdomain_write_java.
+ (usage): Update.
+ (format_directive_domain): In java_mode, ignore domain directives.
+ * Makefile.am (msgfmt_SOURCES): Add write-java.c, msgl-ascii.c,
+ msgl-iconv.c, plural.c.
+
+2001-09-16 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (noinst_HEADERS): Add x-java.h.
+
+2001-09-14 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.h (x_java_extract_all): New declaration.
+ * x-java.l (extract_all_strings): New variable.
+ (x_java_extract_all): New function.
+ (extract_java): Test extract_all_strings.
+ * xgettext.c (main): Call x_java_extract_all.
+
+2001-09-03 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.h (EXTENSIONS_C, SCANNERS_C): Remove extraneous commas.
+ * x-po.h (EXTENSIONS_PO, SCANNERS_PO): Likewise.
+
+2001-09-11 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c: Set msgstr_prefix to "", not NULL.
+ Reported by Martin Quinson <Martin.Quinson@ens-lyon.fr>.
+
+2001-09-10 Bruno Haible <haible@clisp.cons.org>
+
+ * msgattrib.c (main): Call set_program_name instead of assigning
+ program_name directly.
+ * msgcat.c (main): Likewise.
+ * msgconv.c (main): Likewise.
+ * msgen.c (main): Likewise.
+ * msgexec.c (main): Likewise.
+ * msggrep.c (main): Likewise.
+ * msguniq.c (main): Likewise.
+
+2001-09-08 Bruno Haible <haible@clisp.cons.org>
+
+ * msgattrib.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add msgattrib.
+ (msgattrib_SOURCES, msgattrib_LDADD): New variables.
+
+2001-09-02 Bruno Haible <haible@clisp.cons.org>
+
+ * format-lisp.c (gcd): Remove function. Include "gcd.h" instead.
+
+2001-09-03 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (usage): Mention ObjectiveC and Java.
+ * xgettext.h (substring_match): New declaration.
+ * Makefile.am (x-java.c): New rule. Automake's .l.c rules doesn't
+ work if $(LEX) is defined to ":".
+
+2001-09-03 Tommy Johansson <tommy.johansson@kanalen.org>
+
+ * x-java.h: New file.
+ * x-java.l: New file.
+ * xgettext.c: Include x-java.h.
+ (substring_match): New variable.
+ (long_options): Add --keyword-substring.
+ (main): For -k, also call x_java_keyword. Treat --keyword-substring.
+ (scan_java_file): New function.
+ (language_to_scanner): Add Java rule. Remove preliminary Java rule.
+ (extension_to_language): Add Java rule.
+ * Makefile.am (xgettext_SOURCES): Add x-java.l.
+
+2001-09-02 Bruno Haible <haible@clisp.cons.org>
+
+ * read-mo.h: New file.
+ * read-mo.c: New file, extracted from msgunfmt.c.
+ (read_mo_file): Require non-null message list argument. Return void.
+ * msgunfmt.c (endian, read32, seek32, string32, read_mo_file): Move to
+ read-mo.c.
+ (main): Pass a fresh list, not NULL, to read_mo_file.
+ * Makefile.am (noinst_HEADERS): Add read-mo.h.
+ (msgunfmt_SOURCES): Add read-mo.c.
+
+2001-09-02 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.h (struct hashtable_entry): Remove type.
+ (verbose): New declaration.
+ * msgfmt.c (msg_domain): Add a message list field.
+ (verbose): Make non-static.
+ (main): Pass the message list, not the hash table, to
+ msgdomain_write_mo. Free the message list when done.
+ (new_domain): Initialize the message list to empty.
+ (format_directive_message): Change type of entry to 'message_ty *'.
+ Store msgid in it.
+ * write-mo.h: Don't include hash.h.
+ (msgdomain_write_mo): Change argument type from hash table to message
+ list.
+ * write-mo.c (id_str_pair): Add const.
+ (write_table): Change argument type from hash table to message list.
+ Use 'message_ty' instead of 'struct hashtable_entry'.
+ (msgdomain_write_mo): Change argument type from hash table to message
+ list.
+
+2001-09-01 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.h: New file, extracted from msgfmt.c.
+ * write-mo.h: New file, extracted from msgfmt.c.
+ * write-mo.c: New file, extracted from msgfmt.c.
+ (write_table): Don't call delete_hash here.
+ (msgdomain_write_mo): Add check against writing on full disk.
+ * msgfmt.c (struct id_str_pair): Move to write-mo.c.
+ (struct hashtable_entry): Move to msgfmt.h.
+ (alignment): Move to write-mo.h and write-mo.c.
+ (no_hash_table): Likewise. Change type to boolean.
+ (long_options): Don't take &no_hash_table.
+ (roundup): Move to write-mo.c.
+ (main): Set no_hash_table here. Call msgdomain_write_mo and
+ delete_hash.
+ (compare_id): Move to write-mo.c.
+ (write_table): Likewise.
+ (read_po_file): Renamed from grammar.
+ * Makefile.am (noinst_HEADERS): Add msgfmt.h, write-mo.h.
+ (msgfmt_SOURCES): Add write-mo.c.
+
+2001-09-01 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (msgdomain_list_print): Before fclose, check for ferror.
+
+2001-09-01 Bruno Haible <haible@clisp.cons.org>
+
+ * message.c (message_list_list_search): If the first match has an
+ empty translation but there is later in the list a non-empty
+ translation, return the latter one. This fixes the msgmerge-8 test.
+
+2001-08-30 Bruno Haible <haible@clisp.cons.org>
+
+ Reorganize msgfmt checking options.
+ * msgfmt.c: Include limits.h.
+ (verbose): Renamed from verbose_level.
+ (check_format_strings): Renamed from do_check.
+ (check_header, check_domain, check_compatibility): New variables.
+ (long_options): Add --check-domain, --check-format, --check-header,
+ --check-compatibility.
+ (main): Recognize options -C, --check-compatibility, --check-domain,
+ --check-format, --check-header. Option -c implies the last three.
+ (usage): Update.
+ (format_debrief): Test check_header, not verbose_level.
+ (format_directive_domain): Test check_domain, not verbose_level.
+ (format_directive_message): Test check_compatibility and check_header,
+ not verbose_level. Always perform the check for duplicates.
+ (format_comment_special): Test check_compatibility, not verbose_level.
+ (check_pair): Error if check_compatibility is true an msgid_plural is
+ used.
+
+2001-08-26 Bruno Haible <haible@clisp.cons.org>
+
+ * format.h: New file.
+ * format-c.c: New file.
+ * format-java.c: New file.
+ * format-lisp.c: New file.
+ * format-python.c: New file.
+ * format-ycp.c: New file.
+ * format.c: New file.
+ * Makefile.am (msgfmt_SOURCES): Add format.c, format-c.c,
+ format-java.c, format-lisp.c, format-python.c, format-ycp.c.
+ (xgettext_SOURCES): Likewise.
+ * message.h (enum format_type): New type.
+ (format_language, format_language_pretty): New declarations.
+ (parse_c_format_description_string): Remove declaration.
+ (possible_format_p): Renamed from possible_c_format_p.
+ (struct message_ty): Change field 'is_c_format' to an array and
+ rename it to 'is_format'.
+ * message.c (parse_c_format_description_string): Remove function.
+ (format_language): New array.
+ (format_language_pretty): New array.
+ (possible_format_p): Renamed from possible_c_format_p.
+ (parse_c_width_description_string): Remove function.
+ (message_alloc): Update for is_format array.
+ (message_copy): Likewise.
+ (message_merge): Likewise.
+ * po.h (po_parse_comment_special): New declaration.
+ * po.c (po_parse_comment_special): New function.
+ * msgl-cat.c (catenate_msgdomain_list): Update for is_format array.
+ * read-po.c (struct readall_class_ty): Change field 'is_c_format' to
+ an array and rename it to 'is_format'.
+ (readall_constructor): Update for is_format array.
+ (readall_directive_message): Likewise.
+ (readall_comment_special): Call po_parse_comment_special instead of
+ parse_c_format_description_string and parse_c_width_description_string.
+ * write-po.c (make_format_description_string): Renamed from
+ make_c_format_description_string. Add a language argument. Remove the
+ impossible and undecided cases.
+ (significant_format_p): Renamed from significant_c_format_p.
+ (has_significant_format_p): New function.
+ (message_print): Update for is_format array.
+ * msgfmt.c: Include format.h instead of printf.h.
+ (struct msgfmt_class_ty): Change field 'is_c_format' to an array and
+ rename it to 'is_format'.
+ (format_constructor): Update for is_format array.
+ (format_directive_message): Likewise.
+ (format_comment_special): Call po_parse_comment_special instead of
+ parse_c_format_description_string and parse_c_width_description_string.
+ (check_pair): Change is_format argument to an array. Call language
+ dependent format string checking routines.
+ * x-po.c (struct extract_class_ty): Change field 'is_c_format' to
+ an array and rename it to 'is_format'.
+ (extract_constructor): Update for is_format array.
+ (extract_directive_message): Likewise.
+ (extract_comment_special): Call po_parse_comment_special instead of
+ parse_c_format_description_string and parse_c_width_description_string.
+ * x-c.h (SCANNERS_C): Refer to formatstring_c.
+ * x-po.h (SCANNERS_PO): Add NULL formatstring reference.
+ * xgettext.c: Include format.h instead of printf-parse.h.
+ (test_whether_c_format): Remove function.
+ (current_formatstring_parser): New variable.
+ (remember_a_message): Update for is_format array. Call
+ po_parse_comment_special instead of parse_c_format_description_string
+ and parse_c_width_description_string. Decide whether format string
+ depending on current_formatstring_parser.
+ (language_to_scanner): Also set current_formatstring_parser. Add
+ dummy table entries for Python, Lisp, Java, YCP.
+
+2001-08-12 Bruno Haible <haible@clisp.cons.org>
+
+ * pos.h: Include <stddef.h>.
+
+2001-08-12 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.c (real_file_name): Renamed from file_name.
+ (last_comment_line, last_non_comment_line, newline_count): Don't
+ initialize statically.
+ (phase1_getc): Update.
+ (x_c_lex): Don't reset last_comment_line, last_non_comment_line,
+ newline_count upon EOF.
+ (extract_c): Reset them here. Update.
+
+ * x-c.c (x_c_lex): Move lazy keyword initialization...
+ (init_keywords): ... to here. New function.
+ (extract_c): Call init_keywords.
+
+ * x-c.c (x_c_lex): Look up token without NUL in hash table.
+ (x_c_keyword): Store keywords without NUL in hash table.
+
+2001-07-28 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c (usage): If IN_HELP2MAN is set, output template for
+ localedir, not its value.
+ * ngettext.c (usage): Likewise.
+
+2001-07-28 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.c: Include c-ctype.h instead of <ctype.h>.
+ (control_sequence): Use C locale character classifications.
+ * xgettext.c (main): Cast isspace argument to 'unsigned char'.
+
+2001-07-28 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.h (lex_start, lex_end): New declarations.
+ * po-lex.c (lex_start): New function.
+ (lex_end): New function.
+ (lex_open): Call lex_start.
+ (lex_close): Call lex_end.
+ * open-po.h (open_po_file): Nop.
+ * open-po.c (open_po_file): Small optimization.
+ * po.h (po_scan): New declaration.
+ (po_scan_file): Renamed from po_scan.
+ * po.c (po_scan): New function.
+ (po_scan_file): Renamed from po_scan.
+ * msgcmp.c (grammar): Update po_scan_file call.
+ * msgfmt.c (grammar): Likewise.
+ * read-po.c (read_po_file): Likewise.
+ * x-po.h: New file.
+ * x-po.c: New file.
+ (extract_class_ty, extract_constructor, extract_directive_domain,
+ extract_directive_message, extract_parse_brief, extract_comment,
+ extract_comment_dot, extract_comment_filepos, extract_comment_special,
+ extract_methods): Moved here from xgettext.c.
+ (extract_po): New function, extracted from read_po_file.
+ * xgettext.h (line_comment, exclude): New declarations.
+ * xgettext.c: Include x-po.h.
+ (line_comment): Make non-static.
+ (exclude): Likewise.
+ (main): Update scan_po_file call.
+ (read_exclusion_file): Update po_scan_file call.
+ (extract_class_ty, extract_constructor, extract_directive_domain,
+ extract_directive_message, extract_parse_brief, extract_comment,
+ extract_comment_dot, extract_comment_filepos, extract_comment_special,
+ extract_methods): Move to x-po.c.
+ (scan_po_file): Renamed from read_po_file. Call xgettext_open and
+ extract_po.
+ (language_to_scanner): Use SCANNERS_PO.
+ (extension_to_language): Use EXTENSIONS_PO.
+ * Makefile.am (noinst_HEADERS): Add x-po.h.
+ (xgettext_SOURCES): Add x-po.c.
+
+2001-07-27 Bruno Haible <haible@clisp.cons.org>
+
+ * x-c.h: New file.
+ * x-c.c: New file.
+ (xgettext_token_type_ty, xgettext_token_ty): Moved here from
+ xget-lex.h. In xgettext_token_ty, unify line_number and file_name into
+ lex_pos_ty.
+ (token_type_ty, token_ty): Moved here from xget-lex.c.
+ (extract_all): Moved here from xgettext.c.
+ (keywords, default_keywords, trigraphs, file_name, logical_file_name,
+ line_number, fp, last_comment_line, last_non_comment_line,
+ newline_count): Moved here from xget-lex.c.
+ (phase1_pushback, phase1_pushback_length, phase1_getc, phase1_ungetc,
+ phase2_pushback, phase2_pushback_length, phase2_getc, phase2_ungetc,
+ phase3_pushback, phase3_pushback_length, phase3_getc, phase3_ungetc,
+ phase4_getc, phase4_ungetc, phase7_getc, phase7_ungetc,
+ phase5_pushback, phase5_pushback_length, phase5_get, phase5_unget,
+ phaseX_get, phase6_pushback, phase6_pushback_length, phase6_get,
+ phase6_unget, phase8_get): Moved here from xget-lex.c. Use
+ xgettext_comment_add instead of accessing 'comment'. Don't free
+ logical_file_name; it is still used as file_name of messages in mdlp.
+ (x_c_lex): Moved here from xget-lex.c, renamed from xgettext_lex.
+ (extract_c): Moved here from xgettext.c, renamed from scan_c_file.
+ Change arguments from filename to FILE * and two filenames. Don't call
+ xgettext_lex_open and xgettext_lex_close.
+ (x_c_extract_all): New function.
+ (x_c_keyword): Moved here from xget-lex.c, renamed from
+ xgettext_lex_keyword.
+ (x_c_any_keywords): Moved here from xget-lex.c, renamed from
+ xgettext_any_keywords.
+ (x_c_trigraphs): Moved here from xget-lex.c, renamed from
+ xgettext_lex_trigraphs.
+ * xgettext.h: New file.
+ * xgettext.c (extract_all): Move to x-c.c.
+ (comment): Moved here from xget-lex.c.
+ (xgettext_comment_add): New function.
+ (xgettext_comment): Moved here from xget-lex.c, renamed from
+ xgettext_lex_comment.
+ (xgettext_comment_reset): Moved here from xget-lex.c, renamed from
+ xgettext_lex_comment_reset.
+ (xgettext_open): New function, mostly taken from xgettext_lex_open
+ in xget-lex.c.
+ (remember_a_message): Make non-static. Replace xgettext_token_ty arg
+ with lex_pos_ty argument.
+ (remember_a_message_plural): Likewise.
+ (scan_c_file): Moved the body to x-c.c:extract_c.
+ (language_to_scanner): Use SCANNERS_C.
+ (extension_to_language): Use EXTENSIONS_C.
+ * xget-lex.h: Remove file.
+ * xget-lex.c: Remove file.
+ * Makefile.am (noinst_HEADERS): Remove xget-lex.h. Add xgettext.h and
+ x-c.h.
+ (xgettext_SOURCES): Remove xget-lex.c. Add x-c.c.
+
+2001-07-26 Bruno Haible <haible@clisp.cons.org>
+
+ * file-list.h: New file.
+ * file-list.c: New file, extracted from msgcomm.c.
+ * msgcat.c: Include file-list.h.
+ (main): Call read_names_from_file instead of read_name_from_file.
+ (read_name_from_file): Remove function.
+ * msgcomm.c: Include file-list.h.
+ (main): Call read_names_from_file instead of read_name_from_file.
+ (read_name_from_file): Remove function.
+ * xgettext.c: Include file-list.h.
+ (main): Call read_names_from_file instead of read_name_from_file.
+ (read_name_from_file): Remove function.
+ * Makefile.am (noinst_HEADERS): Add file-list.h.
+ (xgettext_SOURCES): Add file-list.c.
+ (msgcat_SOURCES): Likewise.
+ (msgcomm_SOURCES): Likewise.
+
+2001-07-23 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c: Assume <limits.h> exists.
+ * msgexec.c: Likewise.
+ * msggrep.c: Likewise.
+ * write-po.c: Likewise.
+
+2001-08-02 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c (usage): Change bug report address to
+ <bug-gnu-gettext@gnu.org>.
+ * ngettext.c (usage): Likewise.
+ * msgcat.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * msgcomm.c (usage): Likewise.
+ * msgconv.c (usage): Likewise.
+ * msgen.c (usage): Likewise.
+ * msgexec.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * msggrep.c (usage): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * msguniq.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+
+2001-07-22 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (language_to_scanner): Add language "ObjectiveC".
+ (extension_to_language): Map extension "m" to "ObjectiveC".
+
+2001-07-22 Bruno Haible <haible@clisp.cons.org>
+
+ Make msgcomm work with multidomain input and different encodings.
+ * msgl-cat.h (msgcomm_mode, omit_header): New declarations.
+ * msgl-cat.c (msgcomm_mode, omit_header): New variables.
+ (is_message_selected): Different behaviour if omit_header is true.
+ (is_message_needed): Different behaviour if msgcomm_mode is true.
+ (catenate_msgdomain_list): Likewise.
+ * msgcomm.c: Don't include <sys/types.h>, <unistd.h>, po.h. Include
+ read-po.h, msgl-cat.h instead.
+ (line_comment, omit_header, more_than, less_than): Remove variables.
+ (to_code): New variable.
+ (long_options): New option --to-code.
+ (main): New option -t/--to-code. Don't take the address of omit_header.
+ Call catenate_msgdomain_list instead of read_po_file and
+ is_message_selected.
+ (is_message_selected, extract_constructor, extract_directive_domain,
+ extract_directive_message, extract_parse_brief, extract_comment,
+ extract_comment_dot, extract_comment_filepos, extract_comment_special,
+ read_po_file): Remove functions.
+ * Makefile.am (msgcomm_SOURCES): Add read-po.c, msgl-iconv.c,
+ msgl-cat.c.
+
+2001-07-22 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-ascii.h: New file.
+ * msgl-ascii.c: New file.
+ * po-charset.h (po_charset_ascii, po_charset_ascii_compatible): New
+ declarations.
+ * po-charset.c (po_charset_ascii): New variable.
+ (po_charset_ascii_compatible): New function.
+ * write-po.c (has_nonascii): Remove function.
+ (message_print, message_print_obsolete): Use is_ascii_string instead.
+ * msgl-iconv.c (iconv_message_list): Don't complain about missing
+ header entry with charset if all messages are ASCII.
+ * msgl-cat.c (catenate_msgdomain_list): Don't complain about missing
+ header entry with charset if all messages are ASCII. Better choice of
+ canon_to_code: when combining ASCII and an ASCII compatible encoding,
+ choose the latter, not UTF-8. Avoid performing trivial conversions.
+ * Makefile.am (noinst_HEADERS): Add msgl-ascii.h.
+ (msgmerge_SOURCES): Add msgl-ascii.c.
+ (msgunfmt_SOURCES): Likewise.
+ (msgcat_SOURCES): Likewise.
+ (msgcomm_SOURCES): Likewise.
+ (msgconv_SOURCES): Likewise.
+ (msgen_SOURCES): Likewise.
+ (msgexec_SOURCES): Likewise.
+ (msggrep_SOURCES): Likewise.
+ (msguniq_SOURCES): Likewise.
+
+2001-07-22 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-cat.h: Include <stdbool.h>.
+ * po.h: Likewise.
+ * read-po.h: Likewise.
+ * write-po.h: Likewise.
+ * xget-lex.h: Likewise.
+
+2001-07-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msgl-cat.h: New file.
+ * msgl-cat.c: New file, extracted from msgcat.c.
+ * msgcat.c (more_than, less_than): Move to msgl-cat.c.
+ (use_first): Likewise. Change type to bool.
+ (long_options): Don't take the address of use_first.
+ (main): Initialize more_than, less_than, use_first explicitly.
+ Add --use-first handling.
+ (is_message_selected, is_message_needed, is_message_first_needed,
+ catenate_msgdomain_list): Move to msgl-cat.c.
+ * read-po.h (allow_duplicates): New declaration.
+ * read-po.c (allow_duplicates): New variable.
+ (readall_directive_message): If allow_duplicates is true, don't search
+ for the message ID, just append the message.
+ * msguniq.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add msguniq.
+ (noinst_HEADERS): Add msgl-cat.h.
+ (msgcat_SOURCES): Add msgl-cat.c.
+ (msguniq_SOURCES, msguniq_LDADD): New variables.
+
+2001-07-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcat.c (usage): The default value for more-than is 0 here.
+ (is_message_selected): Always keep the header entry. Needed when
+ option --unique is used.
+
+2001-07-21 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (struct message_ty): Add fields 'alternative_count',
+ 'alternative'.
+ * msgcat.c (catenate_msgdomain_list): is_c_format defaults to
+ undecided, not to no. If all alternative translations of a message
+ are the same, keep the first translation instead of mentioning them
+ all, and don't make the message fuzzy.
+
+2001-07-21 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c (extract_directive_message): Don't make the message fuzzy
+ if the first occurrence wasn't fuzzy.
+
+2001-07-21 Bruno Haible <haible@clisp.cons.org>
+
+ * message.c (message_comment_filepos): Don't keep the filepos[] array
+ sorted.
+ * write-po.c (cmp_by_msgid): Renamed from msgid_cmp.
+ (cmp_filepos, msgdomain_list_sort_filepos): New functions.
+ (cmp_by_filepos): Renamed from filepos_cmp.
+ (msgdomain_list_sort_by_filepos): Call msgdomain_list_sort_filepos.
+
+2001-07-12 Bruno Haible <haible@clisp.cons.org>
+
+ * msgexec.c: New file.
+ * msgsed.c: Remove file.
+ * Makefile.am (bin_PROGRAMS): Replace msgsed with msgexec.
+ (msgexec_SOURCES, msgexec_LDADD): New variables.
+ (msgsed_SOURCES, msgsed_LDADD): Remove variables.
+
+2001-07-11 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (test_whether_c_format): When encountering an
+ unterminated % format specifier, return 'impossible'.
+ Reported by Gaute B Strokkenes <gs234@cam.ac.uk>.
+
+2001-07-09 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (has_nonascii): New function.
+ (message_print): Warn if the msgid is not fully ASCII.
+ (message_print_obsolete): Likewise.
+
+2001-07-05 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c (is_message_selected): Keep the header entry.
+
+2001-07-01 Bruno Haible <haible@clisp.cons.org>
+
+ * po-charset.h (po_lex_charset): New declaration.
+ * po-charset.c (po_lex_charset): Export variable.
+ * po-lex.h: Include xerror.h.
+ (gram_pos_column): New declaration.
+ (po_gram_error): Also output current column number.
+ (po_gram_error_at_line): Work around ## problem in gcc.
+ * po-lex.c: Include linebreak.h and utf8-ucs4.h.
+ (gram_pos_column): New variable.
+ (po_gram_error): Also output current column number.
+ (MBCHAR_BUF_SIZE): New macro.
+ (struct mbchar, mbchar_t): New types.
+ (memcpy_small, mb_iseof, mb_ptr, mb_len, mb_iseq, mb_isnul, mb_cmp,
+ mb_equal, mb_isascii): New functions.
+ (MB_UNPRINTABLE_WIDTH): New macro.
+ (mb_width, mb_putc, mb_setascii, mb_copy): New functions.
+ (NPUSHBACK): New macro.
+ (struct mbfile, mbfile_t): New types.
+ (signal_eilseq): New variable.
+ (mbfile_init, mbfile_getc, mbfile_ungetc): New functions.
+ (mbf): New variable.
+ (fp): Remove variable.
+ (lex_open): Initialize mbf, gram_pos_column, signal_eilseq.
+ (lex_close): Reset mbf, gram_pos_column, signal_eilseq.
+ (lex_getc): Return a multibyte character. Update gram_pos_column.
+ (lex_ungetc): Take a multibyte character. Update gram_pos_column.
+ (keyword_p): Use po_gram_error_at_line instead of po_gram_error.
+ No column number needed here.
+ (control_sequence): Read multibyte characters instead of bytes.
+ (po_gram_lex): Likewise.
+ * xgettext.c (exclude_directive_domain): Use po_gram_error_at_line
+ instead of po_gram_error. No column number needed here.
+ (extract_directive_domain): Likewise.
+ * msgcomm.c (extract_directive_domain): Likewise.
+
+2001-06-30 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h: Include stdbool.h.
+ (message_ty): Change type of fields 'is_fuzzy', 'obsolete' to bool.
+ (message_predicate_ty): Change return type to bool.
+ * message.c (message_alloc): Update.
+ * msgcat.c (is_message_selected): Change return type to bool.
+ (is_message_needed): Likewise.
+ (is_message_first_needed): Likewise.
+ (main): Use bool.
+ (catenate_msgdomain_list): Update. Use yes/no for is_c_format and
+ do_wrap.
+ * msgcmp.c (multi_domain_mode): Change type to bool.
+ (compare_directive_message): Change 'obsolete' argument type to bool.
+ (main): Use bool.
+ * msgcomm.c (is_message_selected): Change return type to bool.
+ (extract_directive_message): Change 'obsolete' argument type to bool.
+ (main): Use bool.
+ (struct extract_class_ty): Change type of field 'is_fuzzy' to bool.
+ Change type of field 'filepos_count' to size_t.
+ (extract_constructor): Update.
+ (extract_parse_brief): Update.
+ (extract_comment_special): Update.
+ (read_po_file): Update.
+ * msgconv.c (main): Use bool.
+ * msgen.c (main): Use bool.
+ * msgfmt.c (struct msgfmt_class_ty): Change type of fields 'is_fuzzy',
+ 'has_header_entry' to bool.
+ (include_all): Change type to bool.
+ (do_check): Likewise.
+ (long_options): Update.
+ (format_directive_message): Change 'obsolete' argument type to bool.
+ (main): Use bool.
+ (format_constructor): Update.
+ (format_debrief): Update.
+ (format_comment_special): Update.
+ (check_pair): Update.
+ * msggrep.c (is_string_selected): Change return type to bool.
+ (is_message_selected): Likewise.
+ (main): Use bool.
+ * msgl-charset.c (compare_po_locale_charsets): Use bool.
+ * msgl-iconv.c (iconv_message_list): Update.
+ * msgmerge.c (quiet): Change type to bool.
+ (multi_domain_mode): Likewise.
+ (main): Use bool.
+ (match_domain): Update.
+ (merge): Update.
+ * msgsed.c (main): Use bool.
+ * msgunfmt.c (main): Use bool.
+ * po-gram-gen.y (string, stringlist, number, pos, rhs): Change type of
+ 'obsolete' field to bool.
+ * po-lex.h: Include stdbool.h.
+ (pass_obsolete_entries): Change type to bool.
+ (po_lex_pass_comments): Change argument type to bool.
+ (po_lex_pass_obsolete_entries): Likewise.
+ (po_gram_error): Update.
+ (po_gram_error_at_line): Update.
+ * po-lex.c (po_lex_obsolete): Change type to bool.
+ (pass_comments): Likewise.
+ (pass_obsolete_entries): Likewise.
+ (lex_open): Update.
+ (lex_close): Update.
+ (po_gram_error): Update.
+ (po_gram_error_at_line): Update.
+ (po_gram_lex): Update.
+ (po_lex_pass_comments): Change argument type to bool.
+ (po_lex_pass_obsolete_entries): Likewise.
+ * po.h (struct po_method_ty): Change 'obsolete' argument type of
+ directive_message to bool.
+ (po_callback_message): Change 'obsolete' argument type to bool.
+ * po.c (po_directive_message): Change 'obsolete' argument type to bool.
+ (po_callback_message): Likewise.
+ * read-po.c (struct readall_class_ty): Change type of field 'is_fuzzy'
+ to bool.
+ (readall_directive_message): Change 'obsolete' argument type to bool.
+ (readall_constructor): Update.
+ (readall_parse_brief): Update.
+ (readall_comment_special): Update.
+ (read_po_file): Update.
+ * str-list.h: Include stdbool.h.
+ (string_list_member): Change return type to bool.
+ * str-list.c (string_list_member): Change return type to bool.
+ * write-po.h (message_print_style_escape, msgdomain_list_print): Change
+ argument type to bool.
+ * write-po.c (make_c_format_description_string) Change argument type
+ to bool.
+ (message_print): Likewise.
+ (message_print_obsolete): Likewise.
+ (indent): Change type to bool.
+ (uniforum): Likewise.
+ (escape): Likewise.
+ (message_print_style_indent): Update.
+ (message_print_style_uniforum): Update.
+ (message_print_style_escape): Change argument type to bool.
+ (wrap): Update.
+ (msgdomain_list_print): Change argument type to bool.
+ * xget-lex.h (xgettext_any_keywords): Change return type to bool.
+ * xget-lex.c (trigraphs): Change type to bool.
+ (default_keywords): Likewise.
+ (phase5_get): Use NULL not 0. Update.
+ (phaseX_get): Use bool.
+ (xgettext_lex): Update.
+ (xgettext_lex_keyword): Update.
+ (xgettext_any_keywords): Change return type to bool.
+ (xgettext_lex_trigraphs): Update.
+ * xgettext.c (add_all_comments): Change type to bool.
+ (extract_all): Likewise.
+ (long_options): Update.
+ (exclude_directive_message): Change 'obsolete' argument type to bool.
+ (extract_directive_message): Likewise.
+ (main): Use bool.
+ (scan_c_file): Update.
+ (struct extract_class_ty): Change type of field 'is_fuzzy' to bool.
+ Change type of field 'filepos_count' to size_t.
+ (extract_constructor): Update.
+ (extract_parse_brief): Update.
+ (extract_comment_special): Update.
+ (construct_header): Update.
+
+2001-06-25 Bruno Haible <haible@clisp.cons.org>
+
+ * msggrep.c (usage): Explain the pattern syntax.
+
+2001-06-18 Karl Eichwalder <ke@suse.de>
+
+ * msgcomm.c (long_options): Add '--unique' option as documented
+ within --help output.
+
+2001-06-12 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (extension_to_language): Recognize "*.hh" as being C++.
+ Reported by Felix Natter.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcmp.c (main): Set gram_max_allowed_errors to UINT_MAX, as in
+ msgmerge.c.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * message.c (message_alloc): Cast xmalloc return value.
+ (message_list_alloc): Likewise.
+ (message_list_list_alloc): Likewise.
+ (msgdomain_alloc): Likewise.
+ (msgdomain_list_alloc): Likewise.
+ * msgunfmt.c (string32): Likewise.
+ * po.c (po_alloc): Likewise.
+ * str-list.c (string_list_concat): Likewise.
+ (string_list_join): Likewise.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (message_ty): New field 'tmp'.
+ (message_predicate_ty): New type.
+ (message_list_remove_if_not): New declaration.
+ * message.c (message_list_delete_nth): Comment out.
+ (message_list_remove_if_not): New function.
+ * msgl-charset.h: New file.
+ * msgl-charset.c: New file.
+ * msgl-iconv.h: New file.
+ * msgl-iconv.c: New file.
+ * msgcat.c: New file.
+ * msgconv.c: New file.
+ * msgen.c: New file.
+ * msggrep.c: New file.
+ * msgsed.c: New file.
+ * msgcomm.c (default_domain, output_dir): Remove variables.
+ (long_options): Remove --default-domain and --output-dir. Add
+ --output-file.
+ (main): Don't accept -a, -C, -d, -k, -l, -L, -m, -M, -p, -T, -x.
+ Use output_file instead of complexly computed file_name.
+ Use O(n) loop instead of O(n^2) loop for removing messages.
+ (usage): Don't document --default-domain and --output-dir.
+ (is_message_selected): New function.
+ * Makefile.am (bin_PROGRAMS): Add msgcat, msgconv, msgen, msggrep,
+ msgsed.
+ (noinst_HEADERS): Add msgl-charset.h, msgl-iconv.h.
+ (msgcat_SOURCES, msgconv_SOURCES, msgen_SOURCES, msggrep_SOURCES,
+ msgsed_SOURCES): New variables.
+ (msgcat_LDADD, msgconv_LDADD, msgen_LDADD, msggrep_LDADD,
+ msgsed_LDADD): New variables.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (struct message_variant_ty): Remove type.
+ (struct message_ty): Remove variant_count, variant fields. Add
+ msgstr, msgstr_len, pos fields.
+ (message_alloc): Add msgstr, msgstr_len, pp arguments.
+ (message_variant_search, message_variant_append): Remove declarations.
+ (msgdomain_ty): New type.
+ (msgdomain_alloc, msgdomain_free): New declarations.
+ (msgdomain_list_ty): New type.
+ (msgdomain_list_alloc, msgdomain_list_free, msgdomain_list_append,
+ msgdomain_list_append_list, msgdomain_list_sublist,
+ msgdomain_list_search, msgdomain_list_search_fuzzy): New declarations.
+ * message.c (message_alloc): Add msgstr, msgstr_len, pp arguments.
+ (message_free): Update.
+ (message_variant_search): Remove function.
+ (message_variant_append): Remove function.
+ (message_copy): Update.
+ (message_merge): Update.
+ (message_list_alloc): Use NULL, not 0.
+ (message_list_search): Likewise.
+ (message_list_search_fuzzy_inner): Update.
+ (message_list_list_alloc): Use NULL, not 0.
+ (message_list_list_search): Likewise.
+ (message_list_list_free): Comment out.
+ (msgdomain_alloc, msgdomain_free, msgdomain_list_alloc,
+ msgdomain_list_append, msgdomain_list_sublist): New functions.
+ * read-po.h (read_po_file): Change return type to
+ 'msgdomain_list_ty *'.
+ * read-po.c (readall_class_ty): Remove field domain_list, add field
+ mdlp.
+ (readall_constructor): Update.
+ (readall_destructor): Likewise.
+ (readall_directive_message): Likewise.
+ (readall_parse_debrief): Remove function.
+ (readall_methods): Use NULL instead of readall_parse_debrief.
+ (read_po_file): Change return type to 'msgdomain_list_ty *'.
+ * write-po.h (message_list_print, message_list_sort_by_msgid,
+ message_list_sort_by_filepos): Remove declarations.
+ (msgdomain_list_print, msgdomain_list_sort_by_msgid,
+ msgdomain_list_sort_by_filepos): New declarations.
+ * write-po.c (message_print): Remove domain argument. Update. Assume
+ 'first' is true.
+ (message_print_obsolete): Likewise.
+ (msgdomain_list_print): Renamed from message_list_print. Change type
+ of first argument.
+ (msgdomain_list_sort_by_msgid): Renamed from
+ message_list_sort_by_msgid. Change type of argument.
+ (msgdomain_list_sort_by_filepos): Renamed from
+ message_list_sort_by_filepos. Change type of argument.
+ * msgcmp.c (compare_class_ty): Remove field domain_list, add field
+ mdlp.
+ (multi_domain_mode): New variable.
+ (long_options): Add --multi-domain.
+ (main): Accept -m/--multi-domain.
+ (usage): Explain --multi-domain.
+ (match_domain): New function, extracted from 'compare'.
+ (compare): Update. Handle multi_domain_mode. Change argument types to
+ 'const char *'.
+ (compare_constructor): Update.
+ (compare_destructor): Likewise.
+ (compare_directive_message): Likewise.
+ (compare_parse_debrief): Remove function.
+ (compare_methods): Use NULL instead of compare_parse_debrief.
+ (grammar): Change argument type to 'const char *' and return type to
+ 'msgdomain_list_ty *'.
+ * msgcomm.c (main): Update.
+ (extract_directive_message): Update.
+ * msgmerge.c (multi_domain_mode): New variable.
+ (long_options): Add --multi-domain.
+ (struct statistics): New type.
+ (main): Accept -m/--multi-domain. Update.
+ (usage): Explain --multi-domain.
+ (compendium): Update.
+ (match_domain): New function, extracted from 'merge'.
+ (merge): Update. Handle multi_domain_mode.
+ * msgunfmt.c (main): Update.
+ (read_mo_file): Update.
+ * xgettext.c (main): Update.
+ (exclude_directive_message): Update.
+ (remember_a_message): Likewise.
+ (remember_a_message_plural): Likewise.
+ (scan_c_file): Change argument type to 'msgdomain_list_ty *'.
+ (extract_directive_message): Update.
+ (read_po_file): Change argument type to 'msgdomain_list_ty *'.
+ (construct_header): Update.
+
+ * dir-list.h: Don't use reserved identifiers in prototypes.
+ * gettext.c: Likewise.
+ * message.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * ngettext.c: Likewise.
+ * open-po.h: Likewise.
+ * po-hash.h: Likewise.
+ * po-lex.h: Likewise.
+ * po-lex.c: Likewise.
+ * po.h: Likewise.
+ * po.c: Likewise.
+ * str-list.h: Likewise.
+ * write-po.c: Likewise.
+ * xget-lex.h: Likewise.
+ * xget-lex.c: Likewise.
+ * xgettext.c: Likewise.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (message_list_prepend): New declaration.
+ * message.c (message_list_prepend): New function.
+ * msgmerge.c (merge): Add a header entry to the ref list if it has
+ none.
+
+2001-06-10 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c: Change the --strict option to not apply to domain
+ directives, only to the output filename argument.
+ (struct msg_domain): Add file_name field.
+ (domain_list): Renamed from domain.
+ (new_domain): Add file_name argument.
+ (main): Pass file name to new_domain(). Use file_name passed to
+ new_domain() in fopen call; don't free it.
+ (new_domain): New argument 'file_name'.
+ (format_directive_domain): Always ensure ".mo" suffix, independently
+ of --strict.
+ (format_directive_message): Likewise.
+ (add_mo_suffix): Don't call xstrdup.
+
+2001-06-01 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (datadir): Remove definition.
+ Reported by Albert Chin-A-Young <china@thewrittenword.com>.
+
+2001-05-15 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcmp.c (main): Call set_program_name instead of assigning
+ program_name directly.
+ * msgcomm.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+ * gettext.c (main): Remove "lt-" prefix from program_name.
+ * ngettext.c (main): Likewise.
+
+2001-05-04 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c (usage): Add description of what the program does.
+ Separate blocks with blank lines.
+ * ngettext.c (usage): Likewise.
+
+ * msgcmp.c (usage): Add a blank line after the first usage line.
+ Hardwire the blank line output.
+ * msgcomm.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * msgmerge.c (usage): Likewise.
+ * msgunfmt.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+
+2001-05-05 Karl Eichwalder <ke@suse.de>
+
+ * xgettext.c (read_name_from_file): Remove trailing space from line.
+ * msgcomm.c (read_name_from_file): Likewise.
+
+2001-05-02 Bruno Haible <haible@clisp.cons.org>
+
+ * po-charset.c: Don't include stdio.h, stdlib.h, mbswidth.h. Include
+ xerror.h instead.
+ (multiline_warning): Move to xerror.c.
+ (po_lex_charset_set): Use xasprintf instead of asprintf.
+ * msgfmt.c: Include xerror.h.
+ (format_debrief): Use multiline_error for the error message.
+
+2001-05-01 Bruno Haible <haible@clisp.cons.org>
+
+ Prefix most error messages with the program name, except those
+ starting with "filename:lineno:".
+ * po-lex.h: Include progname.h.
+ (po_gram_error): Set error_with_progname = 0 during error_at_line()
+ call.
+ (po_gram_error_at_line): Likewise.
+ * po-lex.c (po_gram_error): Likewise.
+ (po_gram_error_at_line): Likewise.
+ * msgcmp.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ * msgcomm.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ * msgfmt.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ (format_directive_message): Set error_with_progname = 0 during
+ error_at_line() call.
+ (check_pair): Likewise.
+ * msgmerge.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ * msgunfmt.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ * xget-lex.c: Include progname.h.
+ (phase5_get): Set error_with_progname = 0 during error() call.
+ * xgettext.c: Include progname.h.
+ (program_name): Remove variable.
+ (error_print): Remove function.
+ (main): Initialize error_print_progname with maybe_print_progname.
+ (scan_c_file): Set error_with_progname = 0 during error() call.
+
+2001-04-30 Bruno Haible <haible@clisp.cons.org>
+
+ Save the messages' obsoleteness status in the 'message_ty'.
+ * po.h (struct po_method_ty): Add 'obsolete' argument to
+ directive_message method.
+ (po_callback_message): Add 'obsolete' argument.
+ * po.c (po_directive_message): Add 'obsolete' argument. Pass it to
+ method->directive_message.
+ (po_callback_message): Add 'obsolete' argument.
+ * po-gram-gen.y (message): Pass 'obsolete' argument to
+ po_callback_message.
+ * msgcmp.c (compare_directive_message): Add 'obsolete' argument.
+ * msgcomm.c (extract_directive_message): Likewise.
+ * msgfmt.c (format_directive_message): Likewise.
+ * xgettext.c (exclude_directive_message, extract_directive_message):
+ Likewise.
+ * read-po.c (readall_directive_message): Likewise. Set mp->obsolete.
+
+2001-05-01 Bruno Haible <haible@clisp.cons.org>
+
+ Reduce running time for very long msgstrs from O(n^2) to O(n) where
+ n is the number of string pieces.
+ * str-list.h (string_list_init, string_list_destroy,
+ string_list_concat, string_list_concat_destroy): New declarations.
+ * str-list.c (string_list_init, string_list_destroy,
+ string_list_concat, string_list_concat_destroy): New functions.
+ * po-gram-gen.y Include str-list.h.
+ (%union): Add new alternative of type string_list_ty.
+ (string_list): Change type to stringlist.
+ (message): Use string_list_concat_destroy to convert a stringlist to
+ a single string. Use string_list_destroy instead of free.
+ (msgid_pluralform): Likewise.
+ (pluralform): Likewise.
+ (string_list): Return a stringlist. Don't concatenate the strings one
+ by one.
+ * po-lex.c: Include str-list.h.
+
+2001-04-30 Bruno Haible <haible@clisp.cons.org>
+
+ * message.h (message_alloc): Add const to prototype.
+ * message.c (message_alloc): Add const to argument type.
+ * msgcmp.c (compare_class_ty): Add const to 'domain' field.
+ * msgcomm.c (default_domain): Add const.
+ (main): Add const to 'file_name' variable.
+ * xgettext.c (default_domain): Add const.
+ (msgstr_prefix): Likewise.
+ (msgstr_suffix): Likewise.
+ (main): Add const to 'file_name' variable.
+
+2001-04-29 Bruno Haible <haible@clisp.cons.org>
+
+ * read-po.h: New file.
+ * read-po.c: New file, extracted from msgmerge.c.
+ * msgmerge.c: Don't include string.h. Include read-po.h instead.
+ (merge_class_ty, line_comment, merge_constructor, merge_destructor,
+ merge_directive_domain, merge_directive_message, merge_parse_brief,
+ merge_parse_debrief, merge_comment, merge_comment_dot,
+ merge_comment_special, merge_comment_filepos, grammar): Move out to
+ read-po.c.
+ (compendium, merge): Call read_po_file() instead of grammar().
+ * Makefile.am (noinst_HEADERS): Add read-po.h.
+ (msgmerge_SOURCES): Add read-po.c.
+ * FILES: Update.
+
+2001-04-28 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcmp.c (usage): Restructure usage message. Talk about ref.pot, not
+ ref.po.
+ * msgcomm.c (long_options): Remove --add-comments, --join-existing
+ options.
+ (main): Rename local variables sort_output, sort_by_file to
+ sort_by_msgid, sort_by_filepos. Remove -c and -j options.
+ (usage): Restructure usage message. INPUTFILE is not mandatory. Remove
+ --trigraphs option.
+ * msgfmt.c (usage): Restructure usage message.
+ * msgmerge.c (main): Signal error if both --sort-output and
+ --sort-by-file were given.
+ (usage): Restructure usage message. Talk about ref.pot, not ref.po.
+ Document --sort-output, --sort-by-file, --quiet.
+ * msgunfmt.c (long_options): Add --sort-output.
+ (main): Recognize -s/--sort-output option, and sort the message list
+ when it is given.
+ (usage): Restructure usage message.
+ * xgettext.c (main): Rename local variables sort_output, sort_by_file
+ to sort_by_msgid, sort_by_filepos.
+ (usage): Restructure usage message.
+
+2001-04-28 Bruno Haible <haible@clisp.cons.org>
+
+ * po-charset.h: New file.
+ * po-charset.c: New file, extracted from po.c. Don't warn about wrong
+ or missing charset if it's a POT file.
+ * po-lex.h (iconv.h, po_lex_iconv): Move to po-charset.h.
+ (po_lex_charset): Remove declaration.
+ * po-lex.c: Include po-charset.h.
+ (po_lex_charset, po_lex_iconv): Move to po-charset.c.
+ (lex_open): Call po_lex_charset_init.
+ (lex_close): Call po_lex_charset_close.
+ * po.c: Don't include ctype.h, stdio.h, mbswidth.h, libgettext.h.
+ Include po-charset.h instead.
+ (program_name, _, SIZEOF, multiline_warning): Move to po-charset.c.
+ (po_callback_message): Move charset handling to po-charset.c.
+ * Makefile.am (noinst_HEADERS): Add po-charset.h.
+ (msgcmp_SOURCES, msgfmt_SOURCES, msgmerge_SOURCES, xgettext_SOURCES,
+ msgcomm_SOURCES): Add po-charset.c.
+ * FILES: Update.
+
+ * msgfmt.c: Don't include ctype.h.
+ * msgunfmt.c: Likewise.
+
+2001-04-28 Bruno Haible <haible@clisp.cons.org>
+
+ * po.h (PO_BASE_TY): Remove next_is_fuzzy field.
+ * po.c (po_alloc): Don't reset next_is_fuzzy.
+ (po_callback_message): Ignore fuzziness of the header entry. Don't
+ reset next_is_fuzzy.
+ (po_callback_comment): Don't set next_is_fuzzy.
+ * msgfmt.c (format_directive_message): Ignore fuzziness of the header
+ entry. Count fuzzy untranslated entries as untranslated.
+
+2001-09-13 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext-0.10.40 released.
+
+2001-07-24 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext-0.10.39 released.
+
+2001-05-23 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext-0.10.38 released.
+
+2001-05-23 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (wrap): Avoid broken EUC-KR conversion in glibc-2.1.
+
+2001-05-21 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (construct_header): Replace 8-bit with 8bit.
+
+2001-04-19 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext-0.10.37 released.
+
+2001-04-18 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c (po_callback_message): Don't call iconv_open when the
+ OLD_PO_FILE_INPUT environment variable is set.
+ * write-po.c (wrap): Don't call iconv_open when the OLD_PO_FILE_OUTPUT
+ environment variable is set.
+
+2001-04-09 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c: Include mbswidth.h.
+ (program_name): New declaration.
+ (multiline_warning): New function.
+ (po_callback_message): Change messages again. Call multiline_warning
+ instead of error.
+
+2001-04-04 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c (program_name): Change type to 'const char *'.
+ * ngettext.c (program_name): Likewise.
+
+2001-04-04 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.c (wrap): Prohibit line breaks inside backslash escape
+ sequences.
+
+2001-03-29 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext-0.10.36 released.
+
+2001-03-27 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c (po_callback_message): Change message again.
+
+2001-03-26 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (test_whether_c_format): Recognize '%%' as valid.
+ * msgfmt.c (check_pair): In error message, count format
+ specifications starting from 1, not 0.
+
+2001-03-25 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (warn_id_len): Remove unused variable.
+ (main): Don't set it. 'l' option becomes a nop.
+ (usage): Remove mention of --string-limits/-l.
+
+2001-03-23 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c (main): Don't give an error message if there are "too few"
+ messages in the input files. Do sanity checking before reading the
+ input files. Make --add-location the default, even if --omit-header
+ is specified.
+
+2001-03-22 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (construct_header): Replace ENCODING with 8-bit.
+
+2001-03-20 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c (po_callback_message): Change message, refer to GNU libiconv.
+
+2001-03-18 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c [TESTS]: When testing, set HAVE_SETLOCALE.
+ (main): Expand escape sequences also if no domain is given. Don't need
+ to call bindtextdomain if there are no string arguments.
+ * ngettext.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add ngettext.
+ (ngettext_SOURCES): New variable.
+
+2001-03-19 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (extension_to_language): Recognize "*.cpp" and "*.hpp" as
+ being C++.
+ Patch by Dennis Bjorklund <db@zigo.dhs.org>.
+
+2001-03-11 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c: Force inclusion of libgnuintl.h. On Solaris, <locale.h>
+ has already included libintl.h.
+
+2001-03-11 Bruno Haible <haible@clisp.cons.org>
+ Karl Eichwalder <ke@suse.de>
+
+ * po.c (po_callback_message): Add comments to support translators.
+
+2001-03-11 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.c (lex_close): Use ngettext and plural-form message.
+ * msgcmp.c (compare): Likewise.
+ * msgfmt.c (main): Likewise.
+
+2001-03-10 Bruno Haible <haible@clisp.cons.org>
+
+ * po.c (po_callback_message): Cast %*s argument from size_t to int.
+ * msgfmt.c (format_debrief): Likewise.
+ (check_pair): Cast format string argument from size_t to long.
+ * msgmerge.c (merge): Likewise.
+
+ * gettext.c (main): Update copyright year.
+ * msgcmp.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgunfmt.c (main): Likewise.
+ * xgettext.c (main): Likewise.
+
+ * po.c (po_callback_message): Also warn if no iconv is present and
+ the charset is a CJK encoding requiring iconv. Also avoid broken
+ EUC-KR conversion in glibc-2.1.
+
+2001-03-09 Bruno Haible <haible@clisp.cons.org>
+
+ Fix output of strings in CJK encodings.
+ * write-po.c: Include iconv.h.
+ (wrap): While preparing a line, use iconv to avoid treating the second
+ byte of a multi-byte character as an ASCII character.
+
+2001-03-04 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c (main): Use IS_ABSOLUTE_PATH and concatenated_pathname.
+ Fixes an incorrectly computed memory allocation.
+ * xgettext.c (main): Likewise.
+ * open-po.c (open_po_file): Use IS_ABSOLUTE_PATH and
+ concatenated_pathname.
+ * xget-lex.c (xgettext_lex_open): Likewise.
+ * msgfmt.c (main): Use SET_BINARY instead of setmode.
+ * msgunfmt.c (read_mo_file): Likewise.
+
+2001-03-04 Bruno Haible <haible@clisp.cons.org>
+
+ Check syntax of obsolete entries of PO files, not only in msgmerge.
+ * po-lex.h (pass_obsolete_entries): New declaration.
+ (msgstr_def): Remove pos field.
+ * po-lex.c (po_lex_obsolete): New variable.
+ (pass_obsolete_entries): Make non-static.
+ (lex_open): Initialize po_lex_obsolete.
+ (lex_close): Reset po_lex_obsolete.
+ (po_gram_lex): Don't look at pass_obsolete_entries. Instead, set
+ po_lex_obsolete to 1 when "#~" is seen. Reset po_lex_obsolete when
+ a newline is seen. Before returning, fill the 'pos' and 'obsolete'
+ fields of any token.
+ * po-gram.gen.y (check_obsolete): New macro.
+ (union): Add a 'pos' and 'obsolete' field for any symbol type.
+ (NAME): Assign to type <string>.
+ (DOMAIN, MSGID, MSGID_PLURAL, MSGSTR, '[', ']'): Assign to type <pos>.
+ (msgid, msgstr): Remove.
+ (message, msgid_pluralform, pluralform_list, pluralform, string_list):
+ Signal an error if the 'obsolete' field is not the same across the
+ entire token sequence of the rule.
+ (message): Deal with pass_obsolete_entries here.
+ (msgid_pluralform, pluralform_list, pluralform, string_list): Fill the
+ 'pos' and 'obsolete' fields of $$.
+
+2001-03-03 Bruno Haible <haible@clisp.cons.org>
+
+ Fix parsing of strings in CJK encodings.
+ * po.h (PO_BASE_TY): New field next_is_fuzzy.
+ * po-lex.h: Include iconv.h.
+ (po_lex_charset, po_lex_iconv): New declarations.
+ * po.c (SIZEOF): New macro.
+ (po_alloc): Initialize next_is_fuzzy.
+ (po_callback_message): Add check of charset in header entry.
+ Set po_lex_charset and po_lex_iconv.
+ After calling po_directive_message, reset next_is_fuzzy.
+ (po_callback_comment): Set next_is_fuzzy.
+ * msgfmt.c (format_directive_message): Remove check of charset in
+ header entry, now done in po.c.
+ * po-lex.c (po_lex_charset, po_lex_iconv): New variables.
+ (lex_open): Initialize them.
+ (lex_close): Reset them.
+ (po_gram_lex): While parsing a string, use 'po_lex_iconv' to avoid
+ treating the second byte of a multi-byte character as an ASCII
+ character.
+ * Makefile.am (msgcmp_LDADD, msgfmt_LDADD): New variables.
+
+2001-03-03 Bruno Haible <haible@clisp.cons.org>
+
+ * write-po.h: New file, pieces of message.h.
+ * message.h (message_page_width_set, message_print_style_indent,
+ message_print_style_uniforum, message_print_style_escape,
+ message_list_print, message_list_sort_by_msgid,
+ message_list_sort_by_filepos): Move to write-po.h.
+ * write-po.c: New file, pieces of message.c.
+ (wrap): Add charset argument. Rewritten to use mbs_width_linebreaks().
+ Use c_isprint() instead of isprint().
+ (message_print): Add charset argument. Insert a space after '#' even
+ if the comment line's first character is a tab.
+ (message_print_obsolete): Likewise.
+ (message_list_print): Don't change locales. Extract the charset name
+ from the header entry.
+ (msgid_cmp, filepos_cmp): Always use strcmp, never use strcoll.
+ (message_list_sort_by_msgid, message_list_sort_by_filepos): Don't
+ change locales.
+ * message.c (indent, uniforum, escape, page_width,
+ make_c_format_description_string, significant_c_format_p,
+ make_c_width_description_string, message_print_style_indent,
+ message_print_style_uniforum, message_print_style_escape, wrap,
+ print_blank_line, message_print, message_print_obsolete,
+ message_list_print, msgid_cmp, message_list_sort_by_msgid,
+ filepos_cmp, message_list_sort_by_filepos, message_page_width_set):
+ Move to write-po.c.
+ * msgmerge.c: Include write-po.h.
+ * msgunfmt.c: Likewise.
+ * xgettext.c: Likewise.
+ * msgcomm.c: Likewise.
+ * Makefile.am (noinst_HEADERS): Add write-po.h.
+ (msgmerge_SOURCES): Add write-po.c.
+ (msgunfmt_SOURCES): Likewise.
+ (xgettext_SOURCES): Likewise.
+ (msgcomm_SOURCES): Likewise.
+ (msgmerge_LDADD, msgunfmt_LDADD, xgettext_LDADD, msgcomm_LDADD): New
+ variables.
+
+2001-03-03 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcmp.c (compare): Internationalize error message.
+
+2001-02-10 Bruno Haible <haible@clisp.cons.org>
+
+ * msgunfmt.c (main): Accept -e and -E options.
+
+2001-02-04 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (main): Open the output file in binary mode.
+ * msgunfmt.c (read_mo_file): Open the input file in binary mode.
+
+2001-01-21 Bruno Haible <haible@clisp.cons.org>
+
+ Use libtool.
+ * Makefile.am (LDADD): Remove ../intl/libintl.$la, not needed any more.
+ (l): Remove macro.
+
+2001-01-20 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcomm.c (main): Options '-<' and '->' want an argument.
+
+2001-01-07 Bruno Haible <haible@clisp.cons.org>
+
+ * gettext.c: Renamed from gettextp.c. Use included libintl if TESTS
+ is defined.
+ * setlocale.c: Move to ../tests.
+ * Makefile.am (EXTRA_PROGRAMS, all-local, CLEANFILES): Remove.
+ (tstgettext_SOURCES): Remove.
+ (gettext_SOURCES): Update.
+
+2001-01-07 Bruno Haible <haible@clisp.cons.org>
+
+ * msgcmp.c: Include libgettext.h instead of libintl.h, so that the
+ configure argument --disable-nls gets respected.
+ * msgfmt.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * open-po.c: Likewise.
+ * po-gram-gen.y: Likewise.
+ * po-lex.c: Likewise.
+ * xgettext.c: Likewise. Don't include gettext.h.
+
+2001-01-07 Bruno Haible <haible@clisp.cons.org>
+
+ Assume <stddef.h>, <stdlib.h>, <string.h>, <locale.h> exist.
+ * dir-list.c: Likewise.
+ * gettextp.c: Likewise.
+ * message.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgfmt.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * open-po.c: Likewise.
+ * po.c: Likewise.
+ * setlocale.c: Likewise.
+ * str-list.h: Likewise.
+ * xget-lex.c: Likewise.
+ * xgettext.c: Likewise.
+
+2001-01-06 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (datadir): Assume DATADIRNAME = share.
+
+2001-01-06 Bruno Haible <haible@clisp.cons.org>
+
+ * setlocale.c: New file.
+ * Makefile.am (EXTRA_PROGRAMS): New variable.
+ (all-local): New target.
+ (CLEANFILES): New variable.
+ (tstgettext_SOURCES): New variable.
+
+2001-01-05 Bruno Haible <haible@clisp.cons.org>
+
+ * Makefile.am (po-gram-gen.h): Don't use $^, not supported by the
+ SUSV2 "make" specification.
+ (LDADD): Add @INTLLIBS@. Needed on Solaris without
+ --with-included-gettext.
+ * po-lex.c (gram_max_allowed_errors): Change type to
+ 'unsigned int'.
+
+2001-01-01 Bruno Haible <haible@clisp.cons.org>
+
+ Implement plural form handling.
+ * message.h (struct message_variant_ty): Add msgstr_len field.
+ (struct message_ty): Add msgid_plural field.
+ (message_alloc): Take additional msgid_plural argument.
+ (message_variant_append): Take additional msgstr_len argument.
+ * message.c (message_alloc): Take additional msgid_plural argument.
+ (message_free): Free msgid_plural field.
+ (message_variant_append): Take additional msgstr_len argument.
+ (message_copy): Copy msgid_plural as well. Pass msgstr_len.
+ (message_merge): Likewise.
+ (message_print): Print plural entries using a different format.
+ (message_print_obsolete): Likewise.
+ * msgunfmt.c (string32): Return the string's size as well. Verify
+ the string is NUL terminated.
+ (read_mo_file): Split the original string into msgid and msgid_plural.
+ Pass msgstr_len.
+ * po-lex.h (msgstr_def): New definition, taken from msgfmt.c.
+ * po-lex.c (keyword_p): Recognize the msgid_plural keyword.
+ (po_gram_lex): Accept brackets as single-character tokens.
+ * po.h (struct po_method_ty): Method 'directive_message' takes
+ additional arguments 'msgid_plural', 'msgstr_len'.
+ (po_callback_message): Additional arguments 'msgid_plural',
+ 'msgstr_len'.
+ * po-hash-gen.y (yyerror): Effectively rename to po_hash_error.
+ * po-gram-gen.y (yyerror): Effectively rename to po_gram_error,
+ thus enabling reporting of syntax errors.
+ (plural_counter): New variable.
+ (%token): Add MSGID_PLURAL, '[', ']' as new tokens.
+ (%union): Add new alternative of type 'struct msgstr_def'.
+ (msgid_pluralform, pluralform, pluralform_list): New productions.
+ (message): Add plural rules.
+ * po.c (po_directive_message): Additional arguments 'msgid_plural',
+ 'msgstr_len'.
+ (po_callback_message): Likewise.
+ * msgfmt.c (SIZEOF): New macro.
+ (struct id_str_pair): Add id_len, id_plural, id_plural_len, str_len
+ fields.
+ (struct hashtable_entry): Renamed from struct msgstr_def. Add
+ 'msgid_plural', 'msgstr_len' fields.
+ (format_directive_message): Additional arguments 'msgid_plural',
+ 'msgstr_len'. Verify the validity of the charset field in the header.
+ Compare msgstr using memcmp, not strcmp.
+ (check_pair): Additional arguments 'msgid_plural', 'msgstr_len'.
+ Apply the tests to msgid_plural and each msgstr[i] string.
+ (format_debrief): Change error message.
+ (write_table): Store msgid_plural and msgstr_len in msg_arr[], then
+ output the strings including embedded NULs.
+ * msgcmp.c (compare_directive_message): Additional arguments
+ 'msgid_plural', 'msgstr_len'.
+ * msgcomm.c (extract_directive_message): Additional arguments
+ 'msgid_plural', 'msgstr_len'.
+ * msgmerge.c (merge_directive_message): Additional arguments
+ 'msgid_plural', 'msgstr_len'.
+ * xget-lex.h (struct xgettext_token_ty): Replace argnum field with
+ argnum1, argnum2.
+ * xget-lex.c (xgettext_lex): Add to default keywords: "ngettext:1,2",
+ "dngettext:2,3", "dcngettext:2,3".
+ (xgettext_lex_keyword): Accept new syntax "id:argnum1,argnum2".
+ * xgettext.c (exclude_directive_message): Additional arguments
+ 'msgid_plural', 'msgstr_len'.
+ (remember_a_message): Return the new message.
+ (remember_a_message_plural): New function.
+ (scan_c_file): Extend state machine to allow remembering msgid1 and
+ msgid2 later.
+ (extract_directive_message): Additional arguments 'msgid_plural',
+ 'msgstr_len'. Compare msgstr using memcmp, not strcmp.
+ (construct_header): Update.
+
+2000-12-31 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (format_directive_message): Pass to insert_entry and
+ find_entry the length including the terminating NUL.
+ * xget-lex.c (xgettext_lex, xgettext_lex_keyword): Likewise.
+
+2000-12-31 Bruno Haible <haible@clisp.cons.org>
+
+ * msgunfmt.c (read_mo_file): Recognize "/dev/stdin", not "/dev/stdout".
+
+2000-12-31 Bruno Haible <haible@clisp.cons.org>
+
+ * str-list.h: Fix typo. Add comments.
+ * str-list.c: Add comments.
+
+ * dir-list.h: Add comments.
+ * dir-list.c: Likewise.
+
+ * domain.h: Remove file.
+ * msgfmt.c: Incorporate it here. Don't include "domain.h".
+ * msgunfmt.c: Don't include "domain.h".
+ * xgettext.c: Likewise.
+ * Makefile.am (noinst_HEADERS): Remove domain.h.
+
+ * open-po.h: New file.
+ * open-po.c: Include it.
+ * po-lex.c: Likewise.
+ * Makefile.am (noinst_HEADERS): Add open-po.h.
+
+ * open-po.c (open_po_file): Merge duplicated code.
+
+ * po-lex.h: Add comments.
+ (gram_max_allowed_errors): Change type to 'unsigned int'.
+ (po_gram_error_at_line): Renamed from gram_error_at_line.
+ (po_gram_error, po_gram_error_at_line): Use ISO C 99 macro vararg
+ syntax if possible.
+ * po-lex.c: Add comments. Don't include "po-gram.h".
+ (po_gram_error_at_line): Renamed from gram_error_at_line.
+ * msgmerge.c (main): Initialize gram_max_allowed_errors to UINT_MAX.
+
+ * message.h: Add comments.
+ (enum is_wrap): New type.
+ (parse_c_width_description_string): Change return type.
+ (message_ty): Change type of do_wrap field.
+ * message.c (wrap): Change type of do_wrap argument.
+ * msgfmt.c (msgfmt_class_ty): Change type of do_wrap field.
+ * msgmerge.c (merge_class_ty): Change type of do_wrap field.
+ * msgcomm.c (extract_class_ty): Change types of is_c_format, do_wrap
+ fields.
+ * xgettext.c (extract_class_ty): Likewise.
+ (remember_a_message): Change type of do_wrap.
+
+ * pos.h: New file.
+ * po-lex.h: Include it.
+ (struct lex_pos_ty): Remove type, now in pos.h.
+ * message,h: Include pos.h instead of po-lex.h.
+ * Makefile.am (noinst_HEADERS): Add pos.h.
+
+ * po.h: Add comments.
+
+ * gettextp.c (main): Use bindtextdomain instead of bindtextdomain__,
+ and dgettext instead of dgettext__. There is no need to use the
+ included libintl when GNU gettext or an X/Open gettext or a catgets
+ has been found in the system's C library.
+
+ * ylwrap: Update from automake-1.4.
+
+ * FILES: New file.
+ * Makefile.am (EXTRA_DIST): New variable.
+
+2000-12-30 Bruno Haible <haible@clisp.cons.org>
+
+ * xgettext.c (scanner_c, scanner_cxx): Remove declarations.
+
+2000-12-30 Bruno Haible <haible@clisp.cons.org>
+
+ * msgfmt.c (write_table): Pass additional argument &id_len to
+ iterate_table.
+
+2000-09-13 Bruno Haible <haible@clisp.cons.org>
+
+ Look into #define bodies.
+ * xget-lex.c (phaseX_get): Simplify.
+ (phase6_get): Stop the loop when recognizing a #define directive.
+ Based on a patch by Martin v. Löwis.
+
+ Accept ISO C 99 comment syntax.
+ * xget-lex.c (cplusplus_comments: Remove variable.
+ (phase4_getc): Always recognize // comments.
+ (xgettext_lex_cplusplus): Remove function.
+ * xget-lex.h (xgettext_lex_cplusplus): Remove declaration.
+ * xgettext.c (scan_c_file): Remove is_cpp_file argument.
+ (scanner_c, scanner_cxx): Remove functions.
+ (language_to_scanner): Call scan_c_file directly.
+
+2000-08-23 Bruno Haible <haible@clisp.cons.org>
+
+ * po-lex.c (ALERT_CHAR): New constant macro.
+ (control_sequence): Accept \a. Don't accept \X.
+
+2000-08-23 Ulrich Drepper <drepper@redhat.com>
+
+ * po-lex.c (control_sequence): Unget character which ended \x..
+ sequence.
+
+2000-07-28 Bruno Haible <haible@clisp.cons.org>
+
+ * xget-lex.h (enum xgettext_token_type_ty): Rename
+ xgettext_token_type_lp/rp to xgettext_token_type_lparen/rparen.
+ * xget-lex.c (enum token_type_ty): Rename token_type_lp/rp to
+ token_type_lparen/rparen.
+ (xgettext_any_keywords): `keywords' is now a hash table.
+ * xgettext.c (construct_header): Change two printf directives %02d
+ to %02ld.
+
+1998-07-17 Paul Eggert <eggert@twinsun.com>
+
+ Add support for user-specified argument numbers for keywords.
+ Extract all strings from a keyword arg, not just the first one.
+ Handle parenthesized commas inside keyword args correctly.
+ Warn about nested keywords.
+
+ * xgettext.c (scan_c_file):
+ Warn about nested keywords, e.g. _(_("xxx")).
+ Warn also about not-yet-implemented but allowed nesting, e.g.
+ dcgettext(..._("xxx")..., "yyy").
+ Get all strings in a keyword arg, not just the first one.
+ Handle parenthesized commas inside keyword args correctly.
+
+ * xget-lex.h (enum xgettext_token_type_ty):
+ Replace xgettext_token_type_keyword1 and
+ xgettext_token_type_keyword2 with just plain
+ xgettext_token_type_keyword; it now has argnum value.
+ Add xgettext_token_type_rp.
+ (struct xgettext_token_ty): Add argnum member.
+ line_number and file_name are now also set for
+ xgettext_token_type_keyword.
+ (xgettext_lex_keyword): Arg is const char *.
+
+ * xget-lex.c: Include "hash.h".
+ (enum token_type_ty): Add token_type_rp.
+ (keywords): Now a hash table.
+ (phase5_get): Return token_type_rp for ')'.
+ (xgettext_lex, xgettext_lex_keyword): Add support for keyword argnums.
+ (xgettext_lex): Return xgettext_token_type_rp for ')'.
+ Report keyword argnum, line number, and file name back to caller.
+
+2000-05-06 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am (EXTRA_DIST): Replace po-gram.gen.h and po-hash.gen.h
+ with po-gram-gen.h and po-hash-gen.h.
+ (YACC): Define as @YACC@ -d.
+ (YFLAGS): Removed.
+ (msgcmp_SOURCES): Replace dependencies on po-gram.gen.c and
+ po-hash.gen.c with po-gram-gen.y and po-hash-gen.y.
+ (msgmerge_SOURCES): Likewise.
+ (xgettext_SOURCES): Likewise.
+ (msgcomm_SOURCES): Likewise.
+ Add rule to built po-gram-gen2.h and make po-lex.o depend on it.
+ Remove rules to generate po-gram.gen.c and po-hash.gen.c.
+ Patches by Jim Meyering.
+
+ * po-lex.c: Include "po-gram-gen2.h" instead of "po-gram.gen.h".
+
+ * message.c (message_list_search_fuzzy_inner): Define static. Take
+ extra parameter with best weight so far.
+ (message_list_search_fuzzy): New function.
+ (message_list_list_alloc): New function.
+ (message_list_list_append): New function.
+ (message_list_list_append_list): New function.
+ (message_list_list_search): New function.
+ (message_list_list_search_fuzzy): New function.
+ (message_list_list_free): New function.
+ * message.h (message_list_list_ty): Define type.
+ (message_list_list_alloc): Add prototype.
+ (message_list_list_free): Likewise.
+ (message_list_list_append): Likewise.
+ (message_list_list_append_list): Likewise.
+ (message_list_list_search): Likewise.
+ (message_list_list_search_fuzzy): Likewise.
+ * msgmerge.c: Implement --compendium/-C option.
+ * xget-lex.c (xgettext_any_keywords): New function.
+ * xget-lex.h (xgettext_any_keywords): Add prototype.
+ * xgettext.c: Use xgettext_any_keywords to see whether keywords are
+ defined.
+ Patches by Peter Miller.
+
+1998-05-15 Ulrich Drepper <drepper@cygnus.com>
+
+ * po-lex.c (control_sequence): Replace illegal with invalid.
+
+1998-05-01 08:47 Ulrich Drepper <drepper@cygnus.com>
+
+ * gettext-0.10.35 released.
+
+1998-04-30 Ulrich Drepper <drepper@cygnus.com>
+
+ * dir-list.c: Update Peter Miller's mail address.
+ * dir-list.h: Likewise.
+ * message.c: Likewise.
+ * message.h: Likewise.
+ * msgcmp.c: Likewise.
+ * msgcomm.c: Likewise.
+ * msgmerge.c: Likewise.
+ * po-gram.h: Likewise.
+ * po-gram.y: Likewise.
+ * po-hash.h: Likewise.
+ * po-hash.y: Likewise.
+ * po-lex.c: Likewise.
+ * po-lex.h: Likewise.
+ * po.c: Likewise.
+ * po.h: Likewise.
+ * str-list.c: Likewise.
+ * str-list.h: Likewise.
+ * xget-lex.c: Likewise.
+ * xget-lex.h: Likewise.
+
+1998-04-30 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgfmt.c: Fix typo in --help text.
+ Reported by Jan.Djarv@mbox200.swipnet.se.
+
+1998-04-03 01:18 1998 Philippe De Muyter <phdm@macqel.be>
+
+ * str-list.h (stddef.h): Include that file only if STDC_HEADERS.
+ Otherwise include sys/types.h and stdio.h.
+ * msgmerge.c (string.h): Include that file if HAVE_STRING_H, not
+ if STDC_HEADERS.
+
+1998-04-29 Ulrich Drepper <drepper@cygnus.com>
+
+ * message.c: Use unsigned char for various local variables.
+ * xgettext.c (comment_tag): Define as unsigned char *.
+ For losing Solaris systems. Patches by Jim Meyering.
+
+ * msgfmt.c: Use extra braces in if to shut up gcc.
+ * po-lex.h: Don't declare function po_gram_error and
+ gram_error_at_line if macros with the same names are defined.
+ * Makefile.am (MAINTAINERCLEANFILES): New variable.
+ Patches by Jim Meyering.
+
+1998-04-27 Ulrich Drepper <drepper@cygnus.com>
+
+ * xgettext.c: Update year and bug report address. Add little
+ explanation in --help messages.
+ * msgfmt.c: Likewise.
+
+ * msgmerge.c: Update year and bug report address.
+ * msgcomm.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgunfmt.c: Likewise.
+
+1997-08-31 22:20 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgfmt.c (format_directive_message): Count fuzzy messages as
+ fuzzy, even if they are not written to the output file.
+
+1997-08-18 13:47 Philippe De Muyter <phdm@info.ucl.ac.be>
+
+ * msgcomm.c (sys/types.h): File included.
+
+1997-08-15 12:38 Ulrich Drepper <drepper@cygnus.com>
+
+ * xgettext.c: Include <sys/param.h> to define MIN/MAX for HP/UX.
+ Patch by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>.
+
+ * msgfmt.c: Change DEFAULT_ALIGNMENT to DEFAULT_OUTPUT_ALIGNMENT
+ to avoid clash with macro with same name in obstack.c.
+ Reported by Akim Demaille <demaille@inf.enst.fr>.
+
+1997-08-01 15:48 Ulrich Drepper <drepper@cygnus.com>
+
+ * Makefile.am (AUTOMAKE_OPTIONS): Require version 1.2.
+
+1997-05-07 04:21 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgcomm.c (main): Print author in --version message.
+ * msgunfmt.c: Likewise.
+ * msgcmp.c: Likewise.
+ * msgmerge.c: Likewise.
+ * msgfmt.c: Likewise.
+ * xgettext.c: Likewise.
+ * gettextp.c: Likewise.
+
+1997-05-01 02:33 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgcmp.c (main): Update copyright.
+ * msgunfmt.c (main): Likewise.
+ * msgcomm.c (main): Likewise.
+ * msgmerge.c (main): Likewise.
+ * msgfmt.c (main): Likewise.
+ * gettextp.c (main): Likewise.
+
+ * msgcomm.c: Fix comment about default output (Jan Djarv). Split
+ help message.
+
+1997-03-31 16:09 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgcomm.c (usage): Use program_name as argument for print to
+ print message, not stdout.
+ Patch by Jan Djarv <jan.djarv@mbox200.swipnet.se>.
+
+Mon Mar 10 06:18:58 1997 Ulrich Drepper <drepper@cygnus.com>
+
+ * xgettext.c: Implement generic language scanner handling.
+
+ * xget-lex.c (phase7_getc): Better comments.
+ (phase5_get): Print warnings about unterminated strings and
+ character constants.
+
+ * po-lex.c (po_gram_error): Don't count continuation lines in
+ messages as errors.
+ (gram_error_at_line): Likewise.
+ * po-lex.h: Likewise for macro versions.
+
+ * po-hash.y: Correct typo.
+
+ * msgunfmt.c: Implement --force-po option.
+
+ * msgmerge.c: Implement --force-po, --no-location, and
+ --add-location options.
+
+ * msgfmt.c (format_directive_message): If messages are duplicated
+ and translations are different this is a fatal error.
+
+ * msgcmp.c (compare_directive_message): Use correct format for
+ continuation line in message.
+
+ * message.h: Add prototype for message_list_delete_nth.
+
+ * message.c: Add message_list_delete_nth function.
+
+ * Makefile.am (bin_PROGRAMS): Add msgcomm. (l): New variable.
+ Set to `l' is using libtool. (LDADD): Change for the needs of
+ libtool. (msgcomm_SOURCES): New variable.
+
+ * msgcomm.c: New file.
+
+Wed Dec 4 01:58:10 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * Makefile.am (LDADD): Change for use of libtool.
+
+Tue Dec 3 18:08:46 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * xget-lex.c (phase7_getc): Return \n when newline is seen, not
+ P7_NEWLINE.
+
+ * xgettext.c (main): Implement --foreign-user flag.
+
+ * msgcmp.c (main): Change --version output to what is required by
+ GNU standards. (usage): Correct bug report address.
+ * msgfmt.c: Likewise.
+ * msgunfmt.c: Likewise.
+ * msgmerge.c: Likewise.
+ * xgettext.c: Likewise.
+ * gettextp.c: Likewise.
+
+Sat Sep 14 04:28:09 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgunfmt.c (usage): Put bug report address in separate string.
+ * msgmerge.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * gettextp.c (usage): Likewise.
+
+Thu Sep 12 21:40:48 1996 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+
+ * msgfmt.c (check_pair): Fix error messages.
+
+Sat Aug 31 14:05:29 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgunfmt.c (usage): Add hint about where to report bugs to.
+ * msgmerge.c (usage): Likewise.
+ * msgcmp.c (usage): Likewise.
+ * xgettext.c (usage): Likewise.
+ * msgfmt.c (usage): Likewise.
+ * gettextp.c (usage): Likewise.
+
+Sat Aug 31 04:49:38 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * gettextp.c: Don't include <libintl.h> since this can generate
+ conflicts.
+
+Mon Jul 15 02:21:25 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgfmt.c: Major change: msgfmt now does not write fuzzy messages
+ out by default. The option -f/--use-fuzzy must be used to
+ explicitely tell to do it.
+
+Sat Jul 13 20:23:34 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * xget-lex.c (phase6_get): Reset selected comments on every
+ preprocessor directive.
+
+Fri Jul 12 12:38:49 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * xgettext.c (main): Remove `v' from short option list.
+
+Sat Jul 6 11:22:47 1996 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
+
+ * message.c (message_merge): Add some casts to (char *) in alloca
+ calls. Make KNOWN_FIELDS array static.
+
+Sat Jul 6 11:15:43 1996 Jim Meyering <meyering@na-net.ornl.gov>
+
+ * msgfmt.c (check_pair): Correct English in Message.
+
+Fri Jul 5 17:27:11 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * message.c (message_merge): Terminate string for UNKNOWN fields
+ in header entry.
+
+ * message.c (message_merge): Don't print POT-Revision-Date twice.
+
+ * msgfmt.c: Implement --statistics.
+ Suggested by Santiago Vila Doncel.
+
+ * msgfmt.c: and change message checking so that tests for leading
+ and trailing \n are always performed.
+ Suggested by François Pinard.
+
+Wed Jun 19 02:42:52 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * message.c (message_merge): Implement sorting of header entry
+ lines.
+
+Sat Jun 15 19:46:50 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgmerge.c (usage): Correct -w option in help string (was -W).
+
+Tue Jun 11 15:28:44 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * xget-lex.c, xget-lex.h (xgettext_lex_comment): Change parameter
+ type to size_t.
+
+ * po-lex.h, po-lex.c (gram_max_allowed_errors): Change type to
+ size_t.
+
+ * message.c, message.h (message_page_width_set): Change parameter
+ type to size_t.
+
+ * Makefile.am (AUTOMAKE_OPTIONS): Add variable. Must be defined
+ in all subdirs.
+
+Mon Jun 10 02:53:52 1996 Marcus Daniels <marcus@sysc.pdx.edu>
+
+ * dir-list.c: Include system.h in order to get size_t and NULL.
+
+Thu Jun 6 01:59:31 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * str-list.h: Include <stddef.h> to get size_t defined.
+ Reported by Philippe Defert.
+
+ * Makefile.am (LDADD): Remove `@INTLLIBS@ ../lib/libnlsut.a
+ @LIBS@'. This is not necessary anymore.
+
+Wed Jun 5 00:00:08 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * xgettext.c, msgunfmt.c (usage): Correct --page-width to --width
+ in usage string.
+
+ * open-po.c (open_po_file): Implement search path for PO files.
+
+ * xget-lex.c (xgettext_lex_open): Implement search path for PO
+ files. Patch by Peter Miller.
+
+ * message.h (struct message_ty): Add do_wrap member.
+ Add prototypes for parse_c_width_description_string and
+ message_page_width_set.
+
+ * xgettext.c: Implement --width option to specify width for which
+ line wrapping is done. Change --directory option implement search
+ path instead of single directory. Patch by Peter Miller.
+
+ * message.c (wrap): Implement no-wrap special comment. If given
+ no wrapping is performed.
+
+ * gettextp.c, msgcmp.c, msgfmt.c, msgmerge.c, msgunfmt.c,
+ xgettext.c: Add `no-wrap' comments at usage message strings.
+
+ * msgcmp.c: Implement --directory to specify search path for .po
+ files. Patch by Peter Miller.
+
+Tue Jun 4 23:57:59 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * msgfmt.c: Implement --width option to specify width for which
+ line wrapping is done. Patch by Peter Miller.
+
+ * msgmerge.c: Implement --width option to specify width for which
+ line wrapping is done. Implement --directory to specify search
+ path for .po files. Patch by Peter Miller.
+
+ * msgunfmt.c: Implement --width option to specify width for which
+ line wrapping is done. Patch by Peter Miller.
+
+Tue Jun 4 00:12:25 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * po-hash.y: Add new clause where in GNU style format the name is
+ `file'.
+
+ * Makefile.am (noinst_HEADERS): Add dir-list.h.
+ (msgcmp_SOURCES, msgfmt_SOURCES, msgmerge_SOURCES, xgettext_SOURCES):
+ Add dir-list.c
+
+Mon Jun 3 00:43:07 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * message.h: Add new parameter for message_list_print.
+
+ * msgmerge.c, msgunfmt.c (main): Add new parameter to
+ message_list_print. Set to 0 because we don't need to know about
+ the reasoning.
+
+ * xgettext.c: Implement new option --debug which determines
+ whether a difference is made between c-format and
+ possible-c-format. Default is to print c-format for both cases.
+
+ * message.c (make_c_format_description_string): Take additional
+ parameter DEBUG. If nonzero, distinguish between c-format and
+ possible-c-format.
+
+ * message.c (message_print_obsolete): Copy precious translator
+ comment to output file. Reported by Santiago Vila Doncel.
+
+ * dir-list.c: Include file now is called dir-list.h, not
+ dir_list.h.
+
+ * message.c: Include <limits.h>.
+
+ * Makefile.am (EXTRA_DIST): Add variable to distribute po-gram.y
+ and po-hash.y.
+
+ * Makefile.am (msgfmt_SOURCES): Fix typo: msgfmt.o -> msgfmt.c.
+
+Sat Jun 1 04:33:48 1996 Ulrich Drepper <drepper@cygnus.com>
+
+ * Makefile.in: Remove support for tupdate. msgmerge is stable
+ now.
+
+Wed Apr 10 01:20:49 1996 Ulrich Drepper <drepper@myware>
+
+ * message.c (message_print_obsolete): Don't print
+ c-format/no-c-format flags for obsolete entries.
+
+ * xgettext.c (construct_header): Change DIST to ZONE in header
+ entry template.
+
+ * message.c (message_merge): Insert POT-Creation-Date field before
+ PO-Revision-Date.
+
+Tue Apr 9 17:13:34 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (construct_header): Move POT-Creation-Date line
+ before PO-Revision-Date line.
+
+Fri Apr 5 12:07:19 1996 Ulrich Drepper <drepper@myware>
+
+ * msgmerge.c: Implement --quiet option to prevent dots printed as
+ progress report.
+ (merge): Don't print dots if `quiet'.
+
+ * msgmerge.c (merge): Nicer statistics message.
+
+ * message.c (message_merge): Update POT-Creation-Date field in
+ header entry from contents in reference file.
+
+ * msgfmt.c (format_directive_message): Better test for unchanged
+ fields in header entry.
+
+ * xgettext.c (difftm): Is back.
+ (construct_header): Print distance to GMT in POT file time stamp.
+
+ * xgettext.c (construct_header): Print leading comment and fuzzy
+ flag.
+
+ * message.c (message_print): Allow translator comment to fill more
+ than one line.
+
+ * xgettext.c (construct_header): Introduce POT-Creation-Date
+ field.
+
+Fri Apr 5 03:05:07 1996 Ulrich Drepper <drepper@myware>
+
+ * msgmerge.c (merge): Rename empty in missing. There might be
+ more empty messages which are not missed.
+
+ * msgmerge.c (merge): Terminate `.' line if no verbose output is
+ selected.
+
+ * msgmerge.c (main): Implement -v option to increase verbosity
+ level.
+ (merge): Print `.' to signal ongoing work.
+ Unless verbosity level > 1 don't print information about fuzzy and
+ missing matches. Instead print statistics at the end.
+ Suggested by François Pinard and Santiago Vila Doncel.
+
+Thu Apr 4 11:59:20 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (difftm): Remove unused function.
+ (construct_header): Don't fill Last-Translator field with information
+ about curent user but instead constant text mentioning xgettext.
+
+ * message.c (message_list_search_fuzzy): Initialize mp before
+ using it.
+
+ * message.c (message_print): Normalize printed messages even more.
+ Don't print fuzzy flag is msgstr is empty.
+
+ * message.c (message_list_search_fuzzy): Don't try to match
+ against msgid if none if the msgstr of this message in non-empty.
+
+Thu Apr 4 01:57:37 1996 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (format_directive_message): When testing header entry
+ also check whether they still contain the initial values.
+ Suggested by François Pinard.
+
+Tue Apr 2 16:19:42 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (main): Add "warning" to message about unknown input
+ file type.
+
+ * Makefile.in (all-gettext): New goal. Same as all.
+
+ * xgettext.c (usage): Rearange help strings. One of them was too
+ long for some dumb catgets programs. Reported by Marcus Daniels.
+
+ * msgfmt.c (format_directive_message): Check for standard header
+ entry fields.
+
+ * xgettext.c (construct_header): MIME-VERSION should be written
+ MIME-Version.
+
+ * msgmerge.c (main): Don't recognize -f option anymore. This is
+ the default now.
+
+ * msgfmt.c (format_debrief): New function. Warn if no header
+ entry is found.
+ (format_directive_message): Remember if header entry is found.
+
+Tue Apr 2 11:12:15 1996 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (format_constructor): New function. Initialize
+ is_c_format field.
+ (format_directive_message): Clear is_c_format field for next message
+ at the end.
+
+ * xgettext.c, po-lex.h, po-lex.c, po-gram.y, msgmerge.c, msgfmt.c,
+ msgcmp.c: Use gram_error_at_line instead of gram_error_with_loc
+ and error_at_line instead of error_with_loc. Roland does not like
+ my English.
+
+Tue Apr 2 03:27:34 1996 Ulrich Drepper <drepper@myware>
+
+ * msgunfmt.c (main): Removed -S option. People should think twice
+ before using this and so are forced to use the long version.
+
+Tue Apr 2 03:25:56 1996 François Pinard <pinard@iro.umontreal.ca>
+
+ * msgunfmt.c (usage): Remove Tab character from message.
+
+Tue Apr 2 03:15:16 1996 Marcus Daniels <marcus@sysc.pdx.edu>
+
+ * message.c (significant_c_format_p): If is_c_format is `no' this
+ is significant.
+
+Tue Apr 2 03:12:24 1996 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
+
+ * po.c (po_comment_filepos): Make definition static as declaration
+ was before.
+
+ * msgunfmt.c (usage): Add missing \n to help string.
+
+Mon Apr 1 02:37:45 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (main): When recognizing file type, default to C
+ instead of regarding it as an error. Suggested by Marcus Daniels.
+
+ * po.c (po_callback_comment): For now recognize #! also as special
+ comment.
+
+ * msgmerge.c (merge): Remove --force option. We now always write
+ the result. It makes no sense to reject the output because some
+ messages are not matching.
+
+ * po-lex.c (po-gram_error, gram_error_with_loc): Use
+ error_message_count instead of gram_nerrors.
+ (gram_nerrors): Remove definition.
+
+ * po-lex.h (po-gram_error, gram_error_with_loc): Use
+ error_message_count instead of gram_nerrors.
+
+ * xgettext.c: Remove verbose option and variable.
+ (test_whether_c_format): Don't return `possible' is string
+ contains no format specifier.
+
+Sun Mar 31 23:23:40 1996 Ulrich Drepper <drepper@myware>
+
+ * xget-lex.c (xgettext_lex): Fix typo. Reported by François
+ Pinard.
+
+Thu Mar 28 19:01:22 1996 Ulrich Drepper <drepper@myware>
+
+ * po.c (po_callback_comment): Correct handling of special
+ comments. Give whole comment to callback function instead of
+ tokenized form.
+
+Thu Mar 28 18:37:49 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (remember_a_message): Always look through comments
+ because we have to look for c-format comments.
+
+ * message.h, msgmerge.c, xgettext.c, message.c, msgfmt.c:
+ Implement more detailed C format string handling. Basically coded
+ by Marcus Daniels.
+
+Thu Mar 28 16:52:56 1996 Marcus Daniels <marcus@sysc.pdx.edu>
+
+ * Makefile.in (MSGFMT_OBJ): Add message.o.
+
+Wed Mar 27 03:16:01 1996 Ulrich Drepper <drepper@myware>
+
+ * xget-lex.c (xgettext_lex): Correct implementation of comments
+ grouped with messages.
+
+Tue Mar 26 21:23:54 1996 Ulrich Drepper <drepper@myware>
+
+ * po.c (po_callback_comment): Remove unused variable string.
+ Include <ctype.h> because isspace is used.
+
+ * message.h (message_list_print): Undo change of Mon Mar 25
+ 03:34:44 1996. Don't print trailing comment. Remove additional
+ argument.
+
+ * message.c (message_list_print): Undo change of Mon Mar 25
+ 03:34:44 1996. Don't print trailing comment. We now have a
+ correct implementation of obsolete entry handling.
+ * msgmerge.c: Ditto.
+
+ * po-lex.h: Add prototype for po_lex_pass_obsolete_entries.
+
+ * po-lex.c (po_gram_lex): Implement handling of comments for
+ obsolete entries (#~).
+
+ * msgunfmt.c, xgettext.c (main): Undo change of Mon Mar 25
+ 03:34:44 1996. Remove added argument to message_list_print again.
+
+ * po-lex.c (po_gram_lex): Small optimization in string collection.
+
+ * message.c (message_print_obsolete): Obsolete messages are now
+ preceded by #~ on each line.
+
+ * xgettext.c (remember_a_message): Fix bug with dereferencing
+ dangling pointer. This caused xgettext test 1 to fail.
+
+ * xgettext.c (construct_header): Update format for file header.
+
+ * xgettext.c (extract_directive_message): Don't report error when
+ message variant exists but the value is the same. Reported by
+ Roland McGrath.
+
+ * message.c (wrap): Only write characters in escape notation if
+ explicitely wanted or if it is one of the well known escapes like
+ \n.
+
+ * msgunfmt.c (main): Adopt interface to GNU coding standard. All
+ given files on command line are input files. Output by default is
+ written to standard output and can be redirected using -o.
+
+Mon Mar 25 04:25:42 1996 Ulrich Drepper <drepper@myware>
+
+ * message.c, message.h (message_list_print): Parameter filename is
+ const.
+
+ * message.c (wrap): Change line break behaviour a bit. While it
+ is reasonable to break long line containing #: comments
+ immediately when reaching the the limit, this could lead to unnice
+ results for the strings in msgid and msgstr. The programmer
+ usually knows why the lines are that long.
+
+ So we break for now after reaching 2 * PAGE_WIDTH instead of
+ PAGE_WIDTH.
+
+ * message.c (message_list_print): Print blank line before trailing
+ comments.
+
+ * message.c (message_print_obsolete): Don't print anything for
+ obsolete entries with empty msgstr.
+
+Mon Mar 25 03:34:44 1996 Ulrich Drepper <drepper@myware>
+
+ * msgunfmt.c (main): Make program by default read from stdin and
+ by default write to stdout in the appropriate argument is not
+ given. Suggested by François Pinard.
+
+ * msgfmt.c (format_directive_domain): Only check for correct
+ format string elements is special comment contains c-format.
+
+ * msgfmt.c: Use sizeof instead of strlen to determine length of
+ constant string.
+
+ * xgettext.c: Implement generation of c-format special comments.
+
+ * msgunfmt.c (main): Call message_list_print with additional
+ argument set to NULL.
+
+ * xgettext.c (main): Call message_list_print with additional
+ argument set to NULL;
+ (extract_class_ty): We don't have field comment_special anymore, but
+ instead flags fuzzy and c_format.
+ (extract_constructor): Reset fields fuzzy and c_format.
+ (extract_directive_message): Set flags according to special comments,
+ not string list.
+ (extract_comment_special): Set flags according to special comment.
+
+ * message.h (message_ty): We don't have field comment_special
+ anymore. Instead flags fuzzy and c_format.
+ (message_comment_special_append): Remove prototype.
+ (message_list_print): Add new argument to prototype.
+
+ * message.c (message_alloc): We don't have the comment_special,
+ but fuzzy and c_format.
+ (message_comment_special_append): Remove function. We now have flags.
+ (message_copy, message_merge): Copy fuzzy and c_format flag
+ appropriately.
+ (message_print, message_print_obsolete): Print special comment using
+ flags, not string list.
+ (message_list_print): Additional argument with trailing comments.
+ Printed at the end if not NULL.
+
+ * msgmerge.c (merge_class_ty): New fields fuzzy and c_format for
+ flags;
+ (trailing_comments): New global variable for list of trailing
+ comments in definition file.
+ (grammar): Takes an additional argument which if NULL gets the list of
+ trailing comments assigned to.
+ (main): Call message_list_print with additional argument of trailing
+ comments.
+ (merge_comment_special): Recognize fuzzy and c-format special comment.
+
+Sun Mar 24 17:35:26 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (usage): Option --output-suffix does not exist
+ anymore.
+
+ * msgmerge.c (usage): --strict does not have short form -S.
+
+ * message.h (message_ty): New field `obsolete'.
+
+ * msgmerge.c (merge): Change behaviour not not matched entries.
+ Instead of giving a message write them out at the end of the
+ regular output and precede each line with `# '.
+
+ * message.c (message_list_print): Handle obsolete entries
+ separately by printing them at the end and preceded by `# '.
+ (message_print_obsolete): New function to print obsolete entries.
+
+ * Makefile.in ($(PROGRAMS)): Add generated libraries as
+ dependencies for programs.
+
+ * Makefile.in (PROGRAMS): Remove msgjoin.
+
+Sun Mar 24 01:03:32 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (extract_comment_filepos): Only add line comment if
+ requested by -n option.
+
+ * po.c (po_callback_comment): Short by one bug in special comment
+ entry copying.
+
+ * po.c (po_callback_comment): Handle special comments. Separate
+ them into a list of comma separate entries.
+
+ * message.c (message_print): Format of lines containing fuzzy
+ comments et.al. is now `#, xxx'.
+
+ * Makefile.in: msgjoin program is not anymore generated.
+
+ * xgettext.c: First step to implementing general input file
+ handling. The program now recognizes the file type by the file
+ name extension and uses the appropriate function. For now two
+ file types are recognized: C/C++ and PO. Especially handling PO
+ files make the msgjoin program obsolete.
+
+Sat Mar 23 01:50:00 1996 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (verbose): Rename to verbose_level.
+ (main): Increment verbose_level each time -v option is given.
+ (format_directive_domain): Print some of the diagnostic messages only
+ if verbosity level is > 1. Suggested by François Pinard for a
+ better interface to PO mode.
+
+ * xgettext.c (scan_c_file): Extract all string if `extract-all'
+ option is given. Reported by Roland McGrath.
+
+Thu Mar 14 14:55:20 1996 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (format_comment_special): Be prepared that special
+ comment contains more than one entry, separated by commas.
+
+ * message.c (message_print): Special comments are now written in a
+ line, separated by commas.
+
+ * msgmerge.c (merge): Replace `INEXACT' with `fuzzy'.
+
+Thu Mar 14 11:50:48 1996 Marcus Daniels <marcus@sysc.pdx.edu>
+
+ * po-hash.h (po_hash): Change __P to PARAMS in prototype.
+
+Fri Mar 1 13:35:01 1996 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (main): Set error_one_per_line to 1 to prevent more
+ than one error message per line. Suggested by François Pinard.
+
+ * po-lex.h (po_gram_error): Don't write source file name in fatal
+ message.
+ (po_gram_error_with_loc): Ditto.
+
+ * po-lex.c (lex_close): Don't write source file name in fatal
+ message.
+ (po_gram_error): Ditto.
+ (po_gram_error_with_loc): Ditto.
+
+Fri Mar 1 00:30:49 1996 Ulrich Drepper <drepper@myware>
+
+ * po-lex.h: Use PARAMS instead of __P in header declarations.
+
+ * msgcmp.c (compare_methods): Set new field comment_special to
+ NULL.
+
+ * msgjoin.c (join_methods): Set new field comment_special to NULL.
+
+ * xgettext.c (exclude_methods): Set new field comment_special to
+ NULL.
+
+ * po.h (struct po_method_ty): New field comment_special.
+ (po_directive_domain, po_directive_message, po_parse_brief,
+ po_parse_debrief, po_comment, po_comment_dot, po_comment_filepos):
+ Remove prototypes. Now are local functions.
+
+ * po.c (po_parse_brief, po_parse_debrief, po_directive_domain,
+ po_directive_message, po_comment, po_comment_dot,
+ po_comment_filepos): Declare functions as local and add
+ prototypes.
+ (po_comment_special): New function.
+ (po_callback_comment): For special comments call po_comment_special.
+
+ * msgmerge.c (usage): Add --force in help message.
+ (merge_methods): Set special comment callback to NULL.
+
+ * po-lex.c (lex_open): Don't set pass_comments to 0. This has to
+ be done in upper layer functions.
+ (po_gram_lex): Also pass #! comments up.
+
+ * msgfmt.c (main): Make lexer pass comments up.
+ (format_comment_special): New function. Warns about `#! INEXACT'
+ comments.
+ (format_methods): Add callback for special comment.
+
+Wed Feb 14 01:56:14 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (main): Remove option --output-suffix. When default
+ domain name is "-" write to stdout.
+
+Mon Feb 12 02:20:09 1996 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (main): Implement --output-suffix parameter to
+ determine alternative form of suffix for output file.
+
+ * xgettext.c, msgjoin.c, msgmerge.c, msgunfmt.c (main): Add
+ additional argument to message_list_print call: control output in
+ case of empty PO file.
+ * message.c (message_list_print): Implement FORCE parameter.
+ * message.h: Change prototype.
+
+Sun Jan 21 17:24:56 1996 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (install-exec): Include empty else case for case
+ when Perl is not available. Reported by John David Anglin.
+
+Sat Dec 23 12:41:43 1995 Jun Young <bangjy@nownuri.nowcom.co.kr>
+
+ * gettextp.c (usage): Short option for version info is -V.
+
+Tue Dec 19 22:10:12 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (Makefile, tupdate): Explicitly use $(SHELL) for
+ running shell scripts.
+
+Sat Dec 9 17:06:11 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c, xget-lex.h, xget-lex.c, str-list.h, po.h, po.c,
+ po-lex.c, po-hash.y, po-gram.h, open-po.c, msgunfmt.c, msgmerge.c,
+ msgjoin.c, msgfmt.c, msgcmp.c, message.h, message.c, gettextp.c:
+ Use PARAMS instead of __P. Suggested by Roland McGrath.
+
+Fri Dec 8 01:38:40 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (PROGRAMS): Add definitions for new msgjoin program.
+
+ * msgjoin.c: Initial revision.
+
+Wed Dec 6 18:43:14 1995 Ulrich Drepper <drepper@myware>
+
+ * open-po.c (open_po_file): Recognize /dev/stdin as name for
+ stdin. Recognize .pot as valid extension.
+
+Wed Dec 6 18:05:11 1995 Ulrich Drepper <drepper@myware>
+
+ * msgmerge.c (main): Sort options in getopt loop.
+
+Mon Dec 4 15:37:22 1995 Ulrich Drepper <drepper@myware>
+
+ * msgmerge.c: (main): Remove unused variable `exit_status'.
+
+Sun Dec 3 02:51:31 1995 Ulrich Drepper <drepper@myware>
+
+ * xget-lex.h: [xgettext_token_type_ty]: We now have two keyword
+ types and also the comma is important.
+
+ * xgettext.c (remember_a_message):
+ Correct handling of -c option. This is not a
+ string to prepend to output. Instead it selects single strings to
+ include (instead of all). Reported by Marcus Daniels.
+ (scan_c_file): Extend state machine. We have to retrieve the second
+ argument for the keywords `dgettext' and `dcgetetxt`.
+
+ * xget-lex.c (phase5_get): Recognize ','.
+ (xgettext_lex): Pass ',' on caller.
+ Return different value for keywords `dgetetxt' and `dcgettext`.
+
+ * xget-lex.c (xgettext_lex): `gettext_noop' is an default keyword.
+
+ * msgunfmt.c (usage):
+ Message should not contain TABs. Reported by François Pinard.
+
+ * msgunfmt.c (usage):
+ Correct typo: Uniforun -> Uniforum. Reported by François Pinard.
+
+Mon Nov 20 21:12:52 1995 Ulrich Drepper <drepper@myware>
+
+ * po-lex.c, message.c: Some more pretty printing.
+
+ * message.c (wrap): Don't support '\a' and '\v'.
+
+ * xget-lex.c (phase7_getc): Don't support '\v'.
+
+ * po-lex.c (control_sequence): Don't support '\v'.
+
+ * gettextp.c (expand_escape): Don't support \a and \v.
+
+ * msgcmp.c (compare): Really define static.
+
+Thu Nov 16 22:42:33 1995 Ulrich Drepper <drepper@myware>
+
+ * msgmerge.c (merge): Remove additional parameter in in prototype.
+
+ * xgettext.c: Reomved unused type definition. Patch by Peter Miller.
+
+ * xget-lex.c: Correct some comments and better implementation of
+ -k option. Patch by Peter Miller.
+
+ * po.h: Fix typos. By Peter Miller.
+
+ * po-lex.c (po_gram_lex): Prevent accumulation of #! comments.
+
+ * po-gram.y (comments): Remove unused rule.
+
+ * msgmerge.c: Implement new options sort-by-file and sort-output.
+ Patches by Peter Miller.
+
+ * msgcmp.c (domain): Remove unused global variable.
+ (domain_directive): Remove unused function.
+
+ * message.h: Fix comment for MESSAGE_DOMAIN_DEFAULT definition.
+
+ * message.c (message_print): Correct typo.
+ Clarify comments about ANSI escape sequences.
+ Patches by Peter Miller.
+
+ * Makefile.in (DISTFILES): Remove $(COMSRCS).
+ (MSGFMT_OBJ): Correct indentation. Patches by Peter Miller.
+
+Sun Nov 12 12:52:29 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (line, string, comment): Remove unused global variables.
+ (read_name_from_file): Remove unused variable `cp'.
+
+ * msgmerge.c (merge): Add missing return statement.
+
+ * msgfmt.c (check_pair): Correctly pair comparisons.
+
+ * msgcmp.c (domain_list, mlp): Remove unused global variables.
+
+Sat Nov 11 21:39:17 1995 Ulrich Drepper <drepper@myware>
+
+ * message.c (message_list_print):
+ Prevent output if we have no (real) entry.
+
+ * xgettext.c (remember_a_message): Implement exclude file handling.
+
+Sat Nov 11 17:38:05 1995 Ulrich Drepper <drepper@myware>
+
+ * msgunfmt.c: Fix message.
+
+ * xgettext.c: Use string handling from str-list and .po file
+ handling use xget-lex et.al.
+
+ * xget-lex.h, xget-lex.c, str-list.h, str-list.c, po.h:
+ Initial revision.
+
+ * po-lex.h: Allow variable upper limit of errors.
+ New prototypes.
+
+ * po-lex.c: Allow variable upper limit of errors.
+ Make comment's text available to the caller.
+
+ * po.c, po-hash.y, po-hash.h: Initial revision.
+
+ * po-gram.y: Add handling of comments.
+
+ * po-gram.h: Remove all but one declaration.
+
+ * msgunfmt.c, msgmerge.c: Initial revision.
+
+ * msgfmt.c: Adopt for new interface to parser.
+
+ * msgcmp.c: Move lot's of general code to other files.
+
+ * message.h, message.c:
+ Extended functionality for Peter Miller's pseudo-OO programming.
+
+ * Makefile.in: Rewrite after adding rules for new programs.
+
+Fri Nov 10 10:01:37 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (PROGRAMS): Add new programs msgmerge and msgunfmt.
+
+Thu Nov 9 01:29:46 1995 Ulrich Drepper <drepper@myware>
+
+ * gettextp.c (usage): Split message in two parts.
+
+ * xgettext.c (usage): Split message in three parts.
+
+ * xgettext.c (main):
+ Print warning if --files-from option and file names on command
+ line are given.
+
+ * xgettext.c (long_options):
+ Mixed up `default-domain' and `directory' values.
+
+Wed Nov 8 23:31:34 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c: Implement -D and -f option.
+
+Tue Nov 7 13:44:44 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (LIBS): One @LIBS@ must be @INTLLIBS@.
+
+ * Makefile.in (LIBS): Correct definition. We must be prepared to
+ use two different libintl.a libraries.
+ (po-gram.gen.c): Don't use $< in non-implicit rule.
+
+ * Makefile.in (install-exec): Use `test -n' instead of
+ `test XXX != ""'. Proposed by François Pinard.
+
+Sun Nov 5 23:59:03 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (INSTALL_PROGRAM): Do not specify mode.
+
+Sun Nov 5 21:13:57 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c, msgfmt.c:
+ Comments describing what has to be done should start with FIXME.
+
+Sun Nov 5 19:39:56 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (dist-gettext): Make synonym for dist.
+
+Sun Nov 5 18:11:15 1995 Ulrich Drepper <drepper@myware>
+
+ * po-lex.h (gram_error, gram_error_with_loc):
+ Don't define macros when !__STDC__
+ even when using gcc.
+
+ * po-lex.c (gram_error, gram_error_with_loc):
+ Compile if !__STDC__ even if using gcc.
+
+ * Makefile.in (po-gram.gen.c po-gram.gen.h):
+ Remove file prior of generation.
+
+Sun Nov 5 11:39:21 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (dist): Suppress error message when ln failed.
+ Get files from $(srcdir) explicitly.
+
+ * xgettext.c (process_c_source):
+ Make gettext_noop the forth builtin marker.
+
+Fri Nov 3 00:57:52 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (main): Don't free fname when no suffix was added.
+
+Thu Nov 2 22:55:44 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (dist): Also remove msgcmp.
+
+Tue Oct 31 22:27:52 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c: New option --strict: Only if this is given the .mo
+ file ending is forced.
+
+ * msgfmt.c (message_directive):
+ Call error_with_loc with correct parameters.
+
+ * msgfmt.c (message_directive):
+ Ignores empty message does not count as fatal error.
+
+ * Makefile.in (po-gram.gen.c):
+ Add g suffix to make multiple substitution in one
+ line possible.
+
+Mon Oct 30 22:35:41 1995 Ulrich Drepper <drepper@myware>
+
+ * po-gram.h:
+ Don't give defines for translation of yy* symbols. This is not enough
+ to be able to have more than one parser. See src/Makefile for the way
+ we chose.
+
+ * Makefile.in (po-gram.gen.c):
+ Rewrite generated source while copying. This is the
+ only portable way to get more than one parser in the same program.
+ Patch by Peter Miller.
+
+Sun Oct 29 10:49:59 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (INSTALL_SCRIPT): New variable.
+ (install-exec): Install tupdate using INSTALL_SCRIPT to prevent error
+ when using strip flag.
+
+Sat Oct 28 14:39:33 1995 Ulrich Drepper <drepper@myware>
+
+ * po-gram.h: Include <sys/types.h>.
+
+ * xgettext.c (main):
+ Honour -n option even if --omit-header is given. By Peter Miller.
+
+ * msgcmp.c (check_domain_coverage):
+ No double space in message. By Peter Miller.
+
+ * msgcmp.c (grammar):
+ Close input file after coverage check. Patch by Peter Miller.
+
+Wed Sep 27 20:27:26 1995 Ulrich Drepper <drepper@myware>
+
+ * msgcmp.c:
+ Don't try to include <string.h>. This is done in "system.h".
+
+ * po-lex.c (gram_error, gram_error_with_loc):
+ Add argument definition for K&R style.
+ (gram_error, gram_error_with_loc): We increase gram_nerrors,
+ not nerrors. Reported by Francesco Potorti`.
+
+Tue Sep 26 10:03:29 1995 Ulrich Drepper <drepper@myware>
+
+ * po-gram.h (yyparse): Add redefinition to gram_parse.
+
+ * Makefile.in (YFLAGS):
+ Don't use -p option. Stupid old yaccs do not know it.
+
+ * po-lex.c: Include po-gram.h to get redefinitions of yy* symbols.
+
+ * po-gram.h (yylex, yylval, yyerror):
+ Redefine these symbols to gram_* because the
+ yacc is now called without -p option.
+
+ * Makefile.in (LIBS):
+ Undid last change. On some systems libintl.a is not
+ completely self-contained. alloca() is miisong e.g. on HP-UX.
+
+Mon Sep 25 22:35:55 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (add_mo_suffix): Fix typo.
+
+ * po-lex.h: Include <sys/types.h>.
+ (lex_open): Argument is now `const char *'.
+
+ * msgfmt.c (add_mo_suffix):
+ Allow .gmo suffix. Great idea by Marcus Daniels.
+
+Sat Sep 23 08:20:54 1995 Ulrich Drepper <drepper@myware>
+
+ * po-gram.y, po-lex.c: Include error.h.
+
+ * open-po.c (open_po_file):
+ Remove unused variables `path_dir' and `open_po_file'.
+
+Thu Sep 21 15:30:36 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (LIBS):
+ using libnlsut.a twice is not necessary anymore. libintl.a is
+ selfcontained.
+
+ * gettextp.c (main):
+ Use dcgettext__ and bindtextdomain__ instead of __dcgettext
+ and __bindtextdomain.
+
+ * msgfmt.c, xgettext.c (exit_status):
+ New variables. Contains exit status for the case the program
+ ends normally. Changed when non-fatal error messages are given.
+
+Wed Sep 20 09:16:57 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (xgettext): No also link po-gram.gen.o and po-lex.o.
+
+ * po-lex.c: Pretty print comments.
+ Do some CSE in computation of hex value.
+
+ * xgettext.c (read_po_file): Now use the generated scanner.
+ (domain_directive, message_directive): New functions needed for
+ scanner.
+ (add_id_str): Correct test for exclude files. The messages in the
+ exclude table are in raw format, not C format.
+ (write_out_domain): Check for zero messages and don't write anything in
+ this case.
+
+ * po-lex.c (lex_open): Argument NAME is now const.
+ (gram_error): Move VARARGS1 comment to right place.
+ (control_sequence): Pretty print some comments.
+
+Mon Sep 18 21:23:55 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (add_mo_suffix): Really check for not .mo suffix.
+
+ * xgettext.c (write_out_domain):
+ Write file names in #: lines to file, not stdout.
+
+ * po-gram.y (grammar):
+ Remove function. This allows sharing this file in different
+ programs.
+
+ * domain.h (msg_domain): Member DOMAIN_NAME is now const.
+
+ * Makefile.in (PROGRAMS): Add msgcmp.
+ (HEADERS): Add message.h.
+ (SOURCES): Add message.c and msgcmp.c.
+ (OBJECTS): Add message.o and msgcmp.o.
+ (msgcmp): Rule to construct program.
+
+ * message.h, message.c: Initial revision
+
+ * msgfmt.c (grammar): Close comment so that function is seen.
+ (message_directive): MSGID and MSGSTR are not const.
+
+ * msgcmp.c: Initial revision
+
+ * po-gram.h (message_directive):
+ MSGID and MSGSTR argument are not const.
+
+ * po-gram.h (grammar): Remove prototype. Is now locally defined.
+
+ * po-lex.h (gram_error, gram_error_with_loc):
+ Protect the instructions by do while (0).
+
+ * msgfmt.c (grammar):
+ Define function here. This allows sharing the grammar file
+ with the msgcmp program.
+
+ * msgfmt.c (domain_directive): Free memory of NAME if not needed.
+ (new_domain): Do not duplicate filename, use it as it is.
+
+ * msgfmt.c (message_directive):
+ Free parameter string memory here if necessary.
+ Was done in po-gram.y before.
+
+ * po-gram.h: Remove comment after closing #endif.
+
+ * po-gram.h (grammar): Name parameter in prototype.
+
+Sun Sep 17 23:29:30 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (read_po_file): We don't have a search path anymore,
+ so third argument to open_po_file is not needed anymore. Reduce
+ argument list by this parameter, too.
+
+ * po-lex.c (lex_open):
+ We don't have a search path anymore, so third argument to
+ ope_po_file is not needed anymore.
+
+ * open-po.c (open_po_file): Remove unused `use_path' parameter.
+
+ * Makefile.in (HEADERS): Add po-gram.h and po-lex.h.
+
+ * po-gram.h, po-lex.h: Initial revision
+
+ * Makefile.in (YACC, YFLAGS): New program used for .po file grammar.
+ (SOURCES): Add po-gram.y and po-lex.c.
+ (GENHEADERS, GENSOURCES): New variables for generated headers
+ and sources.
+ (OBJECTS): Add po-gram.gen.o and po-lex.o.
+ Add rules for new files and add to dependency list for msgfmt.
+
+ * po-lex.c, po-gram.y: Initial revision
+
+ * xgettext.c: Remove input path handling.
+ Adapt for new hashing functions return values.
+
+ * msgfmt.c:
+ Rewrite .po file handling. Use Peter Millers .po file Yacc grammar.
+
+ * open-po.c: Remove handling of input path.
+
+ * xgettext.c (write_out_domain):
+ Split #: lines each 80 columns. Based on a patch by
+ Peter Miller.
+
+ * Makefile.in: hash.[ch] moved from src/ to lib/ subdirectory.
+
+Wed Aug 23 21:13:11 1995 Ulrich Drepper <drepper@myware>
+
+ * tupdate.in: Don't print comment in front of obsolete entries.
+
+Tue Aug 22 22:16:31 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (AR, RANLIB): Remove definition. Not needed here.
+ Reported by François Pinard.
+
+Sat Aug 19 17:38:22 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (install-src):
+ Make behave like install. I.e. really install the catalogs.
+
+Sat Aug 19 00:57:07 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (SCRIPTS):
+ New variable. Contains names of scipts to be generated and
+ installed. For now it is tupdate.
+ (PROGRAMS): Remove tupdate.
+ (all): Also depend on $(SCRIPTS).
+
+Fri Aug 18 13:02:04 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (PROGRAMS): Add tupdate.
+ (tupdate): New rule. Rebuild tupdate if tupdate.in or
+ ../config.status changed.
+
+ * tupdate.in: Correct case where message is new: no really print msgid.
+ Better help message by François Pinard.
+ Recognize #\t as comment.
+ Print comment for now obsolete entries.
+ Handle real comments (translator comments and tupdate generate obsolete
+ entries).
+
+ * gettextp.c (usage): Better help message.
+ (usage): Add -s description to help screen.
+
+Mon Aug 14 23:50:48 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (install-src): New no-op goal.
+
+Thu Aug 10 11:26:45 1995 Ulrich Drepper <drepper@myware>
+
+ * tupdate.in: Don't print two " in front of commented out msgstrs.
+
+Wed Aug 9 09:10:30 1995 Ulrich Drepper <drepper@myware>
+
+ * hash.c:
+ Better implementation. Rehashing is now much faster because the
+ hashing value stored in the `used' field is reused.
+ (insert_entry): Split into two function. `insert_entry_2' now does the
+ the work while in `insert_entry' the checks are done.
+ (lookup_2): New function. Expects the search key to be NUL
+ terminated. This is the case when the key is already in the
+ hash table when rehashing.
+
+ * msgfmt.c (write_table):
+ Third argument to `iterate_table' is now a `const' pointer.
+
+ * hash.h (iterate_table): Third arguemtn is `const' pointer.
+
+ * xgettext.c (struct id_str): Make fields `const' pointers.
+
+Fri Aug 4 22:45:39 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (main): Fix typo: me -> we.
+
+ * msgfmt.c (output_file_open): Remove this unused variable.
+ Reported by Jim Meyering.
+
+ * Makefile.in (dist): Remove `copying instead' message.
+
+ * gettextp.c: Start to implement non-Uniforum behaviour.
+ Implemented new mode where gettext behaves like `echo',
+ while translating the messages available in the specified test.
+ New option: -d, -e, -E, also available in normal mode:
+ -E: ignored.
+ -e: enable expansion of some escape sequences.
+ -d: specify text domain to use.
+ New option: -s: enable `echo' mode.
+
+Thu Aug 3 18:25:37 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (usage):
+ Fix typo: anormalies -> anomalies.
+ Reported by Karl Anders O/ygard.
+
+Wed Aug 2 18:51:08 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (ID, TAGS): Do not use $^.
+
+ * xgettext.c (write-header): Add `Content-Type' field.
+
+Tue Aug 1 20:07:58 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (distclean): Remove ID file.
+
+ * Makefile.in (TAGS, ID): Use $^ as command argument.
+ (TAGS): Give etags -o option t write to current directory,
+ not $(srcdir).
+ (ID): Use $(srcdir) instead os $(top_srcdir)/src.
+
+Mon Jul 31 20:57:48 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (process_po_file):
+ Quote msgstr in message "empty `msgstr' entry ignored".
+ Report by Karl Anders O/ygard.
+
+Sun Jul 30 12:14:29 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (LIBS): Always use ../intl/libintl.a.
+ (all): Always depend on ../intl/libintl.a.
+
+Tue Jul 25 00:15:01 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (process_po_file): Correct problem with empty lines.
+
+Sun Jul 23 22:47:56 1995 Ulrich Drepper <drepper@myware>
+
+ * msgfmt.c (process_po_file):
+ Give a message when a sole msgid is found at the end of file.
+
+Wed Jul 19 01:52:13 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (PROGRAMS): Always compile all three programs.
+
+ * gettextp.c: Include libgettext.h explicitly, in addition to
+ libintl.h. On system having libintl.h provided by the C library
+ this assures to have the prototypes for the function defined in
+ GNU gettext library.
+ Use __bindtextdomain and __dgettext instead of bindtextdomain and
+ dgettext resp.
+ Swap arguments in bindtextdomain call.
+
+Tue Jul 18 23:57:16 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (add_id_str):
+ `free(msgstr)' can fail on system not allowing free(0).
+ Reported by Francesco Potorti`.
+
+Tue Jul 18 19:43:41 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (getpwuid):
+ Define prototype if !defined _POSIX_VERSION.
+
+ * hash.c: Don't include malloc.h and string.h because it will be
+ done in system.h.
+
+ * msgfmt.c: Don't include malloc.h because it will be done in
+ system.h.
+
+Sat Jul 15 00:45:31 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (main):
+ Disable line_comment option when omit_header is selected.
+ (write_out_domain): Don't write empty line if !line_comment.
+
+ * Makefile.in (DISTFILES):
+ Due shorted file names now distribute tupdate.in.
+ (install, clean): Handle tupdate, not tupdate.perl.
+
+ * tupdate.in: Rename tupdate.perl.in to tupdate.in to fit in 14
+ character file systems.
+
+Thu Jul 13 22:21:22 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (install): Test whether Perl was found before
+ installing.
+
+ * tupdate.perl.in: Make die message more GNU-like.
+
+ * gettextp.c (usage):
+ Protect prototypes with __P and use K&R form for parameters.
+
+ * xgettext.c (main):
+ Don't use 100u; poor K&R compilers need (unsigned) 100.
+
+ * open-po.c (xstrdup): Protect prototype with __P.
+
+ * msgfmt.c (usage, new_domain, process_po_file, compare_id,
+ write_table, check_pair): Protect prototypes with __P.
+
+ * hash.c (xmalloc): Protect prototype with __P.
+
+Thu Jul 13 01:39:47 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in (check): New no-op goal.
+
+Wed Jul 12 10:40:54 1995 Ulrich Drepper <drepper@myware>
+
+ * tupdate.perl.in: Implement --help and --version options
+
+ * xgettext.c: Add --string-limit option to specify limit on string
+ length.
+ Only warned when verbose mode is selected.
+ Add --verbose option.
+ Help message now correctly says --version == -V.
+
+Tue Jul 11 22:57:54 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c (usage): Split help string because it breaks 1024
+ byte limit.
+
+ * Makefile.in (install-exec): Install tupdate.
+
+ * xgettext.c (stdlib.h): Protect inclusion by STDC_HEADERS.
+ (assert): No assertions anymore.
+ (HAVE_STRTOUL): strtoul is now substituted when not available.
+ Add warning about too long strings (some systems have limits
+ for strings in their compiler and/or tools). E.g. Sinix's
+ gencat program.
+
+ * msgfmt.c (printf.h): Include always
+ (assert): No assertions anymore (all bugs are gone :-).
+ (HAVE_STRTOUL): Not needed anymore because we substitute
+ strtoul if not present.
+
+ * Makefile.in (DISTFILES,distclean): tupdate.perl is now found here.
+
+Tue Jul 11 01:31:03 1995 Ulrich Drepper <drepper@myware>
+
+ * Makefile.in: msgfmt.o depends on ../lib/printf.h.
+
+ * msgfmt.c: protect inclusion of malloc.h and stdlib.h.
+ Don't use GCC's `case b ... e:' feature (NeXT's gcc is
+ gcc-2.xx but does not understand this. Grrr!).
+
+ * open-po.c: Protect inclusion of string.h and stdlib.h.
+
+ * hash.c: Pretty print #define.
+ (init_hash, insert_entry): Cast result of calloc.
+ (insert_entry): Remove non-ANSI `(type *) var = ...' by
+ `*(type **) &var = ...'.
+ (compute_hashval): Cast constant to unsigned long (default: int).
+ Has effects on 64-bit machines.
+
+Tue Jul 4 00:39:58 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c: Don't write "Version:" field for msgid "".
+
+Mon Jul 3 23:02:04 1995 Ulrich Drepper <drepper@myware>
+
+ * xgettext.c, msgfmt.c: Better comment.
+
+ * Makefile.in: Pretty print with François.
+ Fix typo in dependencies.
+ Even more dependency corrections.
+ Correct dependencies of open-po.c.
+ Remove unneeded $(srcdir) from Makefile.in dependency.
+ (LIBS): Correct for building in different directory.
+ (INCLUDES): Correct for building in different directory.
+ (DEFS): Rename DEF_MSG_DOM_DIR to LOCALEDIR.
+ (INCLUDE): Don't use -I paths when not needed.
+
+ * hash.c: Include malloc.c and protect string.h inclusion.
+
+ * gettextp.c: Protect include of stdlib.h declare prototype for
+ getenv if not __STDC__.
+ Include system.h for EXIT_FAILURE.
+ (main, usage): Replace DEF_MSG_DOM_DIR by LOCALEDIR.
+
+ * open-po.c: Include system.h for EXIT_FAILURE.
+
+ * msgfmt.c: Fix typo in !__STDC__ path.
+ (process_po_file): Change for new .po file format.
+
+ * xgettext.c (main): Rename DEF_MSG_DOM_DIR to LOCALEDIR.
+ Update to new .po file format because Solaris' msgfmt can only
+ handle ANSI C style multi-line strings.
+
+Sun Jul 2 21:31:00 1995 Ulrich Drepper <drepper@myware>
+
+ * gettextp.c: gettextp.c (usage): Fix typo in help message.
+ Reported by François Pinard.
+
+Sun Jul 2 02:12:41 1995 Ulrich Drepper <drepper@myware>
+
+ * First official release. This directory contains the
+ source code for the programs specified in the Uniforum proposal
+ for internationalization.
diff --git a/gettext-tools/src/FILES b/gettext-tools/src/FILES
new file mode 100644
index 0000000..5fb474f
--- /dev/null
+++ b/gettext-tools/src/FILES
@@ -0,0 +1,363 @@
+ Short description of the source files
+ =====================================
+
+The msg* and xgettext programs.
+
+Bottom-up structure:
+
+str-list.h
+str-list.c
+ A list-of-immutable-strings type.
+
+dir-list.h
+dir-list.c
+ Management of the list of directories where PO files are
+ searched.
+
+file-list.h
+file-list.c
+ Reading a file list from a file. Used by those programs which
+ accept multiple file arguments and have a --files-from option.
+
+lang-table.h
+lang-table.c
+ Language names according to ISO 639.
+
+pos.h
+ Source file positions.
+
+message.h
+message.c
+ The message type, with many utility routines.
+ A list-of-messages type.
+ A list-of-lists-of-messages type.
+
+msgl-ascii.h
+msgl-ascii.c
+ Message list test for ASCII character set.
+
+po-error.h
+po-error.c
+po-xerror.h
+po-xerror.c
+ Error handling during writing and reading of PO files.
+
++-------------- Writing PO files
+| color.h
+| color.c
+| Color and style handling.
+| write-catalog.h
+| write-catalog.c
+| Output of a list-of-messages.
+| write-po.h
+| write-po.c
+| Output of a list-of-messages to a PO file.
+| write-properties.h
+| write-properties.c
+| Output of a list-of-messages to a Java .properties file.
+| write-stringtable.h
+| write-stringtable.c
+| Output of a list-of-messages to a NeXTstep/GNUstep .strings
+| file.
+| write-desktop.h
+| write-desktop.c
+| Output of a list-of-messages to a .desktop file.
++-------------- Writing PO files
+
++-------------- Reading PO files
+| open-catalog.h
+| open-catalog.c
+| Opening PO files for reading.
+|
+| po-charset.h
+| po-charset.c
+| Charset handling while reading PO files.
+|
+| po-lex.h
+| po-lex.c
+| Lexical analysis of PO files.
+|
+| read-catalog-abstract.h
+| po-gram.h
+| po-gram-gen.y
+| read-po.h
+| read-po.c
+| read-properties.h
+| read-properties.c
+| read-stringtable.h
+| read-stringtable.c
+| read-catalog-abstract.c
+| Parsing of PO files and Java .properties and NeXTstep/GNUstep
+| .strings files.
+| read-catalog-abstract.h
+| General parser structure.
+| po-gram.h
+| po-gram-gen.y
+| Parsing of PO files, based on po-lex.{h,c}.
+| read-po.h
+| read-po.c
+| Parsing of PO files.
+| read-properties.h
+| read-properties.c
+| Parsing of Java .properties files.
+| read-stringtable.h
+| read-stringtable.c
+| Parsing of NeXTstep/GNUstep .strings files.
+| read-catalog-abstract.c
+| Top-level parser functions and callbacks.
+|
+| read-catalog.h
+| read-catalog.c
+| Reading of a PO file, returning a list-of-messages.
+|
+| read-desktop.h
+| read-desktop.c
+| Reading of a .desktop file, returning a list-of-messages.
++-------------- Reading PO files
+
+msgl-iconv.h
+msgl-iconv.c
+ Convert a list-of-messages to another character encoding.
+
+msgl-cat.h
+msgl-cat.c
+ Concatenate message lists from several files, with handling
+ of duplicate msgids.
+
+msgcmp.c Main source for the 'msgcmp' program.
+
++-------------- The 'msgmerge' program
+| msgl-equal.h
+| msgl-equal.c
+| Comparing two lists-of-messages.
+| plural-count.h
+| plural-count.c
+| Extracting the plural count of a header of a lists-of-messages.
+| msgmerge.c
+| Main source for the 'msgmerge' program.
+|
++-------------- The 'msgmerge' program
+
+msgl-header.h
+msgl-header.c
+ Message list header manipulation.
+
+msgcomm.c Main source for the 'msgcomm' program.
+msgattrib.c Main source for the 'msgattrib' program.
+msgcat.c Main source for the 'msgcat' program.
+msgconv.c Main source for the 'msgconv' program.
+msguniq.c Main source for the 'msguniq' program.
+
+msgl-charset.h
+msgl-charset.c
+ Compare the encoding of a list-of-messages with the locale
+ encoding.
+
+msgexec.c Main source for the 'msgexec' program.
+msgfilter.c Main source for the 'msgfilter' program.
+msggrep.c Main source for the 'msggrep' program.
+
++-------------- The 'msgen' program
+| msgl-english.h
+| msgl-english.c
+| English message initialization.
+| msgen.c
+| Main source for the 'msgen' program.
+|
++-------------- The 'msgen' program
+
+po-time.h
+po-time.c
+ Create time stamps for use in PO/POT files.
+
+plural-table.h
+plural-table.c
+ Table of plural form formulas.
+
++-------------- The 'msginit' program
+| hostname.c
+| The 'hostname' program.
+| user-email.sh.in
+| Determine the user's email address.
+| urlget.c
+| The 'urlget' program.
+| project-id
+| Determine the package's name.
+| msginit.c
+| Main source for the 'msginit' program.
+|
++-------------- The 'msginit' program
+
++-------------- The 'msgunfmt' program
+| msgunfmt.h
+| Declarations.
+| read-mo.h
+| read-mo.c
+| Reading GNU .mo files.
+| read-java.h
+| read-java.c
+| Reading Java ResourceBundle files.
+| read-csharp.h
+| read-csharp.c
+| msgunfmt.cs
+| Reading C# satellite assemblies.
+| read-resources.h
+| read-resources.c
+| msgunfmt.cs
+| Reading C# .resources files.
+| read-tcl.h
+| read-tcl.c
+| msgunfmt.tcl
+| Reading Tcl .msg files.
+| msgunfmt.c
+| Main source for the 'msgunfmt' program.
+|
++-------------- The 'msgunfmt' program
+
+format.h Declarations of the language dependent format string handlers.
+format-invalid.h Declarations of some error messages for invalid strings.
+format-c.c Format string handling for C.
+format-c-parse.h Format string handling for C, parsing routine.
+format-sh.c Format string handling for Shell.
+format-python.c Format string handling for Python.
+format-python-brace.c Format string handling for Python, braced syntax.
+format-lisp.c Format string handling for Common Lisp.
+format-elisp.c Format string handling for Emacs Lisp.
+format-librep.c Format string handling for librep.
+format-scheme.c Format string handling for Scheme.
+format-java.c Format string handling for Java.
+format-csharp.c Format string handling for C#.
+format-awk.c Format string handling for awk.
+format-pascal.c Format string handling for Object Pascal.
+format-ycp.c Format string handling for YCP.
+format-tcl.c Format string handling for Tcl.
+format-perl.c Format string handling for Perl.
+format-perl-brace.c Format string handling for Perl, braced syntax.
+format-php.c Format string handling for PHP.
+format-gcc-internal.c Format string handling GCC internal.
+format-gfc-internal.c Format string handling GFC internal.
+format-qt.c Format string handling for Qt.
+format-qt-plural.c Format string handling for Qt plural forms.
+format-kde.c Format string handling for KDE.
+format-boost.c Format string handling for Boost.
+format-lua.c Format string handling for Lua.
+format-javascript.c Format string handling for JavaScript.
+format.c Table of the language dependent format string handlers.
+
+plural-exp.c
+ Parsing plural expressions.
+plural-eval.h
+plural-eval.c
+ Evaluating plural expressions.
+plural-distrib.h
+ Value distribution of plural expressions.
+msgl-check.h
+msgl-check.c
+ Checking of messages.
+
++-------------- The 'msgfmt' program
+| msgfmt.h
+| Declarations.
+| write-mo.h
+| write-mo.c
+| Generating GNU .mo files.
+| write-java.h
+| write-java.c
+| Generating Java ResourceBundle files.
+| write-csharp.h
+| write-csharp.c
+| Generating C# satellite assemblies.
+| write-resources.h
+| write-resources.c
+| msgfmt.cs
+| Generating C# .resources files.
+| write-tcl.h
+| write-tcl.c
+| Generating Tcl .msg files.
+| write-qt.h
+| write-qt.c
+| Generating Qt .qm files.
+| msgfmt.c
+| Main source for the 'msgfmt' program.
+|
++-------------- The 'msgfmt' program
+
++-------------- The 'xgettext' program
+| xgettext.h
+| Declarations used by the backends.
+| x-c.h
+| x-c.c
+| String extractor for C.
+| x-po.h
+| x-properties.h
+| x-stringtable.h
+| x-po.c
+| String extractor from PO files and Java .properties and
+| NeXTstep/GNUstep .strings files.
+| x-sh.h
+| x-sh.c
+| String extractor for Shell.
+| x-python.h
+| x-python.c
+| String extractor for Python.
+| x-lisp.h
+| x-lisp.c
+| String extractor for Common Lisp.
+| x-elisp.h
+| x-elisp.c
+| String extractor for Emacs Lisp.
+| x-librep.h
+| x-librep.c
+| String extractor for librep.
+| x-scheme.h
+| x-scheme.c
+| String extractor for Scheme.
+| x-smalltalk.h
+| x-smalltalk.c
+| String extractor for Smalltalk.
+| x-java.h
+| x-java.c
+| String extractor for Java.
+| x-csharp.h
+| x-csharp.c
+| String extractor for C#.
+| x-awk.h
+| x-awk.c
+| String extractor for awk.
+| x-ycp.h
+| x-ycp.c
+| String extractor for YCP.
+| x-tcl.h
+| x-tcl.c
+| String extractor for Tcl.
+| x-perl.h
+| x-perl.c
+| String extractor for Perl.
+| x-php.h
+| x-php.c
+| String extractor for PHP.
+| x-rst.h
+| x-rst.c
+| String extractor from .rst files, for Object Pascal.
+| x-glade.h
+| x-glade.c
+| String extractor from .glade files, GNOME GUI descriptions.
+| x-lua.h
+| x-lua.c
+| String extractor for Lua.
+| x-javascript.h
+| x-javascript.c
+| String extractor for JavaScript.
+| x-vala.h
+| x-vala.c
+| String extractor for Vala.
+| x-gsettings.h
+| x-gsettings.c
+| String extractor for GSettings schema file.
+| x-desktop.h
+| x-desktop.c
+| String extractor from Desktop Entry file.
+| xgettext.c
+| Main source for the 'xgettext' program.
+|
++-------------- The 'xgettext' program
diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am
new file mode 100644
index 0000000..9f2325f
--- /dev/null
+++ b/gettext-tools/src/Makefile.am
@@ -0,0 +1,621 @@
+## Makefile for the gettext-tools/src subdirectory of GNU gettext
+## Copyright (C) 1995-1998, 2000-2011 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 <http://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in.
+
+AUTOMAKE_OPTIONS = 1.5 gnits no-dependencies subdir-objects
+EXTRA_DIST =
+MOSTLYCLEANFILES = core *.stackdump
+CLEANFILES =
+DISTCLEANFILES =
+
+RM = rm -f
+
+bin_PROGRAMS = \
+msgcmp msgfmt msgmerge msgunfmt xgettext \
+msgattrib msgcat msgcomm msgconv msgen msgexec msgfilter msggrep msginit msguniq \
+recode-sr-latin
+
+noinst_PROGRAMS = hostname urlget
+
+lib_LTLIBRARIES = libgettextsrc.la
+
+noinst_HEADERS = pos.h message.h po-error.h po-xerror.h po-gram.h po-charset.h \
+po-lex.h open-catalog.h read-catalog-abstract.h read-catalog.h \
+read-po.h read-properties.h read-stringtable.h \
+str-list.h \
+color.h write-catalog.h write-po.h write-properties.h write-stringtable.h \
+dir-list.h file-list.h po-gram-gen.h po-gram-gen2.h \
+msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-header.h \
+msgl-english.h msgl-check.h msgl-fsearch.h msgfmt.h msgunfmt.h \
+plural-count.h plural-eval.h plural-distrib.h \
+read-mo.h write-mo.h \
+read-java.h write-java.h \
+read-csharp.h write-csharp.h \
+read-resources.h write-resources.h \
+read-tcl.h write-tcl.h \
+write-qt.h \
+read-desktop.h write-desktop.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 \
+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 libexpat-compat.h
+
+EXTRA_DIST += FILES project-id ChangeLog.0
+
+aliaspath = $(localedir)
+jardir = $(datadir)/gettext
+pkgdatadir = $(datadir)/gettext
+projectsdir = $(pkgdatadir)/projects
+pkglibdir = $(libdir)/gettext
+
+AM_CPPFLAGS = \
+ -I. -I$(srcdir) \
+ -I.. -I$(top_srcdir) \
+ -I$(top_srcdir)/libgrep \
+ -I../gnulib-lib -I$(top_srcdir)/gnulib-lib \
+ -I../intl -I$(top_srcdir)/../gettext-runtime/intl
+DEFS = \
+ -DLOCALEDIR=\"$(localedir)\" -DBISON_LOCALEDIR=\"$(BISON_LOCALEDIR)\" \
+ -DLOCALE_ALIAS_PATH=\"$(aliaspath)\" \
+ -DUSEJAVA=$(USEJAVA) \
+ -DUSEJEXE=$(USEJEXE) \
+ -DGETTEXTJEXEDIR=\"$(pkglibdir)\" \
+ -DGETTEXTJAR=\"$(jardir)/gettext.jar\" \
+ -DLIBDIR=\"$(libdir)\" \
+ -DGETTEXTDATADIR=\"$(pkgdatadir)\" \
+ -DPROJECTSDIR=\"$(projectsdir)\" @DEFS@
+# Ensure that <stdint.h> defines SIZE_MAX in C++ mode, like it does in C mode.
+AM_CXXFLAGS = -D__STDC_LIMIT_MACROS
+
+LDADD = ../gnulib-lib/libgettextlib.la $(LTLIBUNISTRING) @LTLIBINTL@ $(WOE32_LDADD)
+OTHERPROGDEPENDENCIES = ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+
+SED = sed
+YACC = @YACC@ -d
+GCJ = @GCJ@
+GCJFLAGS = @GCJFLAGS@
+JAR = @JAR@
+JAVACOMP = $(SHELL) ../javacomp.sh
+CSHARPCOMP = $(SHELL) ../csharpcomp.sh
+CSHARPCOMPFLAGS = @CSHARPCOMPFLAGS@
+
+
+# All programs deal with message lists.
+# All programs must read PO files. (msgunfmt also, for read-java.c,
+# read-csharp.c and read-resources.c.)
+# message.c -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> open-catalog.c -> dir-list.c -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> po-charset.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> message.c -> str-list.c.
+COMMON_SOURCE = message.c po-error.c po-xerror.c \
+read-catalog-abstract.c po-lex.c po-gram-gen.y po-charset.c \
+read-po.c read-properties.c read-stringtable.c open-catalog.c \
+dir-list.c str-list.c
+
+# xgettext and msgfmt deal with format strings.
+if !WOE32DLL
+FORMAT_SOURCE = format.c
+else
+FORMAT_SOURCE = ../woe32dll/c++format.cc
+endif
+FORMAT_SOURCE += \
+ format-invalid.h \
+ format-c.c format-c-parse.h \
+ format-sh.c \
+ format-python.c \
+ format-python-brace.c \
+ format-lisp.c \
+ format-elisp.c \
+ format-librep.c \
+ format-scheme.c \
+ format-java.c \
+ format-csharp.c \
+ format-awk.c \
+ format-pascal.c \
+ format-ycp.c \
+ format-tcl.c \
+ format-perl.c \
+ format-perl-brace.c \
+ format-php.c \
+ format-gcc-internal.c \
+ format-gfc-internal.c \
+ format-qt.c \
+ format-qt-plural.c \
+ format-kde.c \
+ format-boost.c \
+ format-lua.c \
+ format-javascript.c
+
+# libgettextsrc contains all code that is needed by at least two programs.
+libgettextsrc_la_SOURCES = \
+$(COMMON_SOURCE) read-catalog.c \
+color.c write-catalog.c write-properties.c write-stringtable.c write-po.c \
+msgl-ascii.c msgl-iconv.c msgl-equal.c msgl-cat.c msgl-header.c msgl-english.c \
+msgl-check.c file-list.c msgl-charset.c po-time.c plural-exp.c plural-eval.c \
+plural-table.c \
+$(FORMAT_SOURCE) \
+read-desktop.c
+
+# msggrep needs pattern matching.
+LIBGREP = ../libgrep/libgrep.a
+
+# Source dependencies.
+msgcmp_SOURCES = msgcmp.c
+msgcmp_SOURCES += msgl-fsearch.c
+msgfmt_SOURCES = msgfmt.c
+msgfmt_SOURCES += \
+ write-mo.c write-java.c write-csharp.c write-resources.c write-tcl.c \
+ write-qt.c write-desktop.c ../../gettext-runtime/intl/hash-string.c
+if !WOE32DLL
+msgmerge_SOURCES = msgmerge.c
+else
+msgmerge_SOURCES = ../woe32dll/c++msgmerge.cc
+endif
+msgmerge_SOURCES += msgl-fsearch.c lang-table.c plural-count.c
+msgunfmt_SOURCES = msgunfmt.c
+msgunfmt_SOURCES += \
+ read-mo.c read-java.c read-csharp.c read-resources.c read-tcl.c
+if !WOE32DLL
+xgettext_SOURCES = xgettext.c
+else
+xgettext_SOURCES = ../woe32dll/c++xgettext.cc
+endif
+xgettext_SOURCES += \
+ 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-glade.c x-lua.c x-javascript.c x-vala.c x-gsettings.c \
+ libexpat-compat.c \
+ x-desktop.c
+if !WOE32DLL
+msgattrib_SOURCES = msgattrib.c
+else
+msgattrib_SOURCES = ../woe32dll/c++msgattrib.cc
+endif
+if !WOE32DLL
+msgcat_SOURCES = msgcat.c
+else
+msgcat_SOURCES = ../woe32dll/c++msgcat.cc
+endif
+if !WOE32DLL
+msgcomm_SOURCES = msgcomm.c
+else
+msgcomm_SOURCES = ../woe32dll/c++msgcomm.cc
+endif
+if !WOE32DLL
+msgconv_SOURCES = msgconv.c
+else
+msgconv_SOURCES = ../woe32dll/c++msgconv.cc
+endif
+if !WOE32DLL
+msgen_SOURCES = msgen.c
+else
+msgen_SOURCES = ../woe32dll/c++msgen.cc
+endif
+msgexec_SOURCES = msgexec.c
+if !WOE32DLL
+msgfilter_SOURCES = msgfilter.c
+else
+msgfilter_SOURCES = ../woe32dll/c++msgfilter.cc
+endif
+msgfilter_SOURCES += filter-sr-latin.c
+msgfilter_SOURCES += filter-quote.c
+if !WOE32DLL
+msggrep_SOURCES = msggrep.c
+else
+msggrep_SOURCES = ../woe32dll/c++msggrep.cc
+endif
+msginit_SOURCES = msginit.c
+msginit_SOURCES += lang-table.c plural-count.c
+msginit_SOURCES += ../../gettext-runtime/intl/localealias.c
+# This is needed because on Solaris, localealias.c requires the symbol
+# libintl_thread_in_use which is defined in lock.c. The copy of lock.c inside
+# libintl.so is not sufficient, because libintl.so doesn't export the symbol
+# libintl_thread_in_use.
+msginit_SOURCES += ../../gettext-runtime/intl/lock.c
+if !WOE32DLL
+msguniq_SOURCES = msguniq.c
+else
+msguniq_SOURCES = ../woe32dll/c++msguniq.cc
+endif
+recode_sr_latin_SOURCES = recode-sr-latin.c filter-sr-latin.c
+hostname_SOURCES = hostname.c
+urlget_SOURCES = urlget.c
+
+# How to build libgettextsrc.la.
+# Need ../gnulib-lib/libgettextlib.la.
+# Need $(LTLIBUNISTRING) because ulc_width_linebreaks, uc_width, etc. may be
+# taken from libunistring, when the configure option --with-libunistring-prefix
+# was given.
+# Need @LTLIBINTL@ because many source files use gettext().
+# Need @LTLIBICONV@ because po-charset.c, po-lex.c, msgl-iconv.c, write-po.c
+# use iconv().
+libgettextsrc_la_LDFLAGS = \
+ -release @VERSION@ \
+ ../gnulib-lib/libgettextlib.la $(LTLIBUNISTRING) @LTLIBINTL@ @LTLIBICONV@ -lc -no-undefined
+
+libgettextsrc_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+# Tell the mingw or Cygwin linker which symbols to export.
+if WOE32DLL
+libgettextsrc_la_SOURCES += ../woe32dll/gettextsrc-exports.c
+libgettextsrc_la_LDFLAGS += -Wl,--export-all-symbols
+libgettextsrc_la_CPPFLAGS += $(GETTEXTLIB_EXPORTS_FLAGS)
+endif
+
+# No need to install libgettextsrc.a, except on AIX.
+install-exec-hook: install-exec-clean
+install-exec-clean:
+ case "@host_os@" in \
+ aix*) ;; \
+ *) $(RM) $(DESTDIR)$(libdir)/libgettextsrc.a ;; \
+ esac
+
+# Compile-time flags for particular source files.
+msgmerge_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS)
+msgmerge_CXXFLAGS = $(AM_CXXFLAGS) $(OPENMP_CFLAGS)
+
+# Link dependencies.
+# INTL_MACOSX_LIBS is needed because the programs depend on libintl.la
+# but libtool doesn't put -Wl,-framework options into .la files.
+# For msginit, it is also needed because of localename.c.
+msgcmp_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@ $(WOE32_LDADD)
+msgfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgmerge_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@ $(WOE32_LDADD) $(OPENMP_CFLAGS)
+msgunfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+xgettext_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @LTLIBEXPAT@ @LTLIBICONV@ $(WOE32_LDADD)
+msgattrib_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgcat_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgcomm_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgconv_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgen_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgexec_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgfilter_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msggrep_LDADD = $(LIBGREP) libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msginit_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msguniq_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+
+# Specify when to relink the programs.
+msgcmp_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgfmt_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgmerge_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgunfmt_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+xgettext_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgattrib_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgcat_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgcomm_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgconv_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgen_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgexec_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgfilter_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msggrep_DEPENDENCIES = $(LIBGREP) libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msginit_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msguniq_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+recode_sr_latin_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+hostname_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+urlget_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+
+# Specify installation directory, for --enable-relocatable.
+msgcmp_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgfmt_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgmerge_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgunfmt_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+xgettext_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgattrib_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgcat_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgcomm_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgconv_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgen_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgexec_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgfilter_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msggrep_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msginit_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msguniq_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+recode_sr_latin_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+hostname_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(pkglibdir)\"
+urlget_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(pkglibdir)\"
+if RELOCATABLE_VIA_LD
+msgcmp_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgfmt_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgmerge_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgunfmt_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+xgettext_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgattrib_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgcat_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgcomm_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgconv_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgen_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgexec_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msgfilter_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msggrep_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msginit_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+msguniq_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+recode_sr_latin_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+hostname_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(pkglibdir)`
+urlget_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(pkglibdir)`
+endif
+
+# Linking with C++ libraries is needed _only_ on mingw and Cygwin.
+if !WOE32DLL
+libgettextsrc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(libgettextsrc_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgattrib_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgattrib_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgcat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgcat_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgcomm_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgcomm_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgconv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgconv_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgen_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgfilter_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgfilter_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msggrep_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msggrep_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgmerge_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(msgmerge_CFLAGS) $(CFLAGS) $(msgmerge_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msguniq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msguniq_LDFLAGS) $(LDFLAGS) \
+ -o $@
+xgettext_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(xgettext_LDFLAGS) $(LDFLAGS) \
+ -o $@
+else
+libgettextsrc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(libgettextsrc_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgattrib_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgattrib_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgcat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgcat_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgcomm_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgcomm_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgconv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgconv_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgen_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgfilter_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgfilter_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msggrep_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msggrep_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msgmerge_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(msgmerge_CXXFLAGS) $(CXXFLAGS) $(msgmerge_LDFLAGS) $(LDFLAGS) \
+ -o $@
+msguniq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msguniq_LDFLAGS) $(LDFLAGS) \
+ -o $@
+xgettext_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(xgettext_LDFLAGS) $(LDFLAGS) \
+ -o $@
+endif
+
+# Special rules for bison and flex generated files.
+
+BUILT_SOURCES = \
+ po-gram-gen.c po-gram-gen.h po-gram-gen2.h
+
+po-lex.o po-lex.lo: po-gram-gen2.h
+po-gram-gen2.h: po-gram-gen.h
+ srcdir=''; \
+ test -f ./po-gram-gen.h || srcdir=$(srcdir)/; \
+ $(SED) -e 's/yy/po_gram_/g' -e 's/extern /extern DLL_VARIABLE /' \
+ $${srcdir}po-gram-gen.h > $@-tmp && \
+ mv $@-tmp $@
+
+
+# Special rules for installation of auxiliary programs.
+
+install-exec-local:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) hostname$(EXEEXT) $(DESTDIR)$(pkglibdir)/hostname$(EXEEXT)
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) urlget$(EXEEXT) $(DESTDIR)$(pkglibdir)/urlget$(EXEEXT)
+ $(INSTALL_SCRIPT) user-email $(DESTDIR)$(pkglibdir)/user-email
+ $(INSTALL_SCRIPT) $(srcdir)/project-id $(DESTDIR)$(pkglibdir)/project-id
+
+installdirs-local:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+uninstall-local:
+ $(RM) $(DESTDIR)$(pkglibdir)/hostname$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/urlget$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/user-email
+ $(RM) $(DESTDIR)$(pkglibdir)/project-id
+
+DISTCLEANFILES += user-email
+
+
+# Special rules for Java compilation.
+
+USEJAVA = $(USEJAVA_@BUILDJAVAEXE@)
+USEJAVA_yes = 1
+USEJAVA_no = $(USEJAVA_no_@BUILDJAVA@)
+USEJAVA_no_yes = 1
+USEJAVA_no_no = 0
+
+USEJEXE = $(USEJEXE_@BUILDJAVAEXE@)
+USEJEXE_yes = 1
+USEJEXE_no = 0
+
+all-local: all-java-@BUILDJAVAEXE@
+all-java-yes: gnu.gettext.DumpResource$(EXEEXT) gnu.gettext.GetURL$(EXEEXT)
+all-java-no: all-java-no-@BUILDJAVA@
+all-java-no-yes: gettext.jar
+all-java-no-no:
+
+gnu.gettext.DumpResource$(EXEEXT): $(srcdir)/gnu/gettext/DumpResource.java
+ $(GCJ) $(GCJFLAGS) $(srcdir)/gnu/gettext/DumpResource.java --main=gnu.gettext.DumpResource -o $@
+
+gnu.gettext.GetURL$(EXEEXT): $(srcdir)/gnu/gettext/GetURL.java
+ $(GCJ) $(GCJFLAGS) $(srcdir)/gnu/gettext/GetURL.java --main=gnu.gettext.GetURL -o $@
+
+gnu/gettext/DumpResource.class: $(srcdir)/gnu/gettext/DumpResource.java
+ $(JAVACOMP) -d . $(srcdir)/gnu/gettext/DumpResource.java
+
+gnu/gettext/GetURL.class: $(srcdir)/gnu/gettext/GetURL.java
+ $(JAVACOMP) -d . $(srcdir)/gnu/gettext/GetURL.java
+
+gettext.jar: gnu/gettext/DumpResource.class gnu/gettext/GetURL.class
+ $(JAR) cf $@ gnu/gettext/DumpResource*.class gnu/gettext/GetURL*.class
+
+EXTRA_DIST += gnu/gettext/DumpResource.java gnu/gettext/GetURL.java
+
+CLEANFILES += gnu.gettext.DumpResource$(EXEEXT) gnu.gettext.GetURL$(EXEEXT) \
+ gettext.jar gnu/gettext/*.class
+
+install-exec-local: install-exec-java-@BUILDJAVAEXE@
+install-exec-java-yes: all-java-yes
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) gnu.gettext.DumpResource$(EXEEXT) $(DESTDIR)$(pkglibdir)/gnu.gettext.DumpResource$(EXEEXT)
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) gnu.gettext.GetURL$(EXEEXT) $(DESTDIR)$(pkglibdir)/gnu.gettext.GetURL$(EXEEXT)
+install-exec-java-no:
+
+install-data-local: install-data-java-@BUILDJAVAEXE@
+install-data-java-yes:
+install-data-java-no: install-data-java-no-@BUILDJAVA@
+install-data-java-no-yes: all-java-no-yes
+ $(INSTALL_DATA) gettext.jar $(DESTDIR)$(jardir)/gettext.jar
+install-data-java-no-no:
+
+installdirs-local: installdirs-java-@BUILDJAVAEXE@
+installdirs-java-yes:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+installdirs-java-no: installdirs-java-no-@BUILDJAVA@
+installdirs-java-no-yes:
+ $(MKDIR_P) $(DESTDIR)$(jardir)
+installdirs-java-no-no:
+
+uninstall-local: uninstall-java-@BUILDJAVAEXE@
+uninstall-java-yes:
+ $(RM) $(DESTDIR)$(pkglibdir)/gnu.gettext.DumpResource$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/gnu.gettext.GetURL$(EXEEXT)
+uninstall-java-no: uninstall-java-no-@BUILDJAVA@
+uninstall-java-no-yes:
+ $(RM) $(DESTDIR)$(jardir)/gettext.jar
+uninstall-java-no-no:
+
+
+# Special rules for C# auxiliary programs.
+
+EXTRA_DIST += msgfmt.cs msgunfmt.cs
+
+CLEANFILES += \
+ msgfmt.net.exe msgfmt.net.exe.mdb \
+ msgunfmt.net.exe msgunfmt.net.exe.mdb
+
+all-local: all-csharp-@BUILDCSHARP@
+all-csharp-yes: msgfmt.net.exe msgunfmt.net.exe
+all-csharp-no:
+
+msgfmt.net.exe: msgfmt.cs
+ $(CSHARPCOMP) $(CSHARPCOMPFLAGS) -o $@ $(srcdir)/msgfmt.cs
+
+msgunfmt.net.exe: msgunfmt.cs
+ $(CSHARPCOMP) $(CSHARPCOMPFLAGS) -o $@ -L ../../gettext-runtime/intl-csharp -l GNU.Gettext $(srcdir)/msgunfmt.cs
+
+install-exec-local: install-exec-csharp-@BUILDCSHARP@
+install-exec-csharp-yes: all-csharp-yes
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_DATA) msgfmt.net.exe $(DESTDIR)$(pkglibdir)/msgfmt.net.exe
+ $(INSTALL_DATA) msgunfmt.net.exe $(DESTDIR)$(pkglibdir)/msgunfmt.net.exe
+install-exec-csharp-no:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+installdirs-local: install-csharp
+installdirs-csharp:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+uninstall-local: uninstall-csharp-@BUILDCSHARP@
+uninstall-csharp-yes: all-csharp-yes
+ $(RM) $(DESTDIR)$(pkglibdir)/msgfmt.net.exe
+ $(RM) $(DESTDIR)$(pkglibdir)/msgunfmt.net.exe
+uninstall-csharp-no:
+
+
+# Special rules for Tcl auxiliary program.
+
+EXTRA_DIST += msgunfmt.tcl
+
+install-data-local: install-tcl
+install-tcl:
+ $(MKDIR_P) $(DESTDIR)$(pkgdatadir)
+ $(INSTALL_DATA) $(srcdir)/msgunfmt.tcl $(DESTDIR)$(pkgdatadir)/msgunfmt.tcl
+
+installdirs-local: installdirs-tcl
+installdirs-tcl:
+ $(MKDIR_P) $(DESTDIR)$(pkgdatadir)
+
+uninstall-local: uninstall-tcl
+uninstall-tcl:
+ $(RM) $(DESTDIR)$(pkgdatadir)/msgunfmt.tcl
+
+
+# Support for relocatability.
+RELOCATABLE_LIBRARY_PATH = $(libdir)
+RELOCATABLE_SRC_DIR = $(top_srcdir)/gnulib-lib
+RELOCATABLE_BUILD_DIR = ../gnulib-lib
+RELOCATABLE_CONFIG_H_DIR = ..
+RELOCATABLE_STRIP = :
+
+# Version information according to Woe32 conventions.
+if WOE32
+WOE32_LDADD = gettext.res
+gettext.res : $(top_srcdir)/../windows/gettext.rc
+ $(WINDRES) `$(SHELL) $(top_srcdir)/../windows/windres-options --escape $(VERSION)` -i $(top_srcdir)/../windows/gettext.rc -o gettext.res --output-format=coff
+MOSTLYCLEANFILES += gettext.res
+else
+WOE32_LDADD =
+endif
diff --git a/gettext-tools/src/Makefile.in b/gettext-tools/src/Makefile.in
new file mode 100644
index 0000000..4e29138
--- /dev/null
+++ b/gettext-tools/src/Makefile.in
@@ -0,0 +1,3511 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = msgcmp$(EXEEXT) msgfmt$(EXEEXT) msgmerge$(EXEEXT) \
+ msgunfmt$(EXEEXT) xgettext$(EXEEXT) msgattrib$(EXEEXT) \
+ msgcat$(EXEEXT) msgcomm$(EXEEXT) msgconv$(EXEEXT) \
+ msgen$(EXEEXT) msgexec$(EXEEXT) msgfilter$(EXEEXT) \
+ msggrep$(EXEEXT) msginit$(EXEEXT) msguniq$(EXEEXT) \
+ recode-sr-latin$(EXEEXT)
+noinst_PROGRAMS = hostname$(EXEEXT) urlget$(EXEEXT)
+
+# Tell the mingw or Cygwin linker which symbols to export.
+@WOE32DLL_TRUE@am__append_1 = ../woe32dll/gettextsrc-exports.c
+@WOE32DLL_TRUE@am__append_2 = -Wl,--export-all-symbols
+@WOE32DLL_TRUE@am__append_3 = $(GETTEXTLIB_EXPORTS_FLAGS)
+@WOE32_TRUE@am__append_4 = gettext.res
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/../build-aux/mkinstalldirs \
+ $(srcdir)/user-email.sh.in po-gram-gen.c \
+ $(top_srcdir)/../build-aux/ylwrap $(noinst_HEADERS) ChangeLog
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = \
+ $(top_srcdir)/libgettextpo/gnulib-m4/gnulib-comp.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/gnulib-comp.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/langinfo_h.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/localeconv.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/mbrlen.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/nl_langinfo.m4 \
+ $(top_srcdir)/libgrep/gnulib-m4/regex.m4 \
+ $(top_srcdir)/gnulib-m4/00gnulib.m4 \
+ $(top_srcdir)/gnulib-m4/absolute-header.m4 \
+ $(top_srcdir)/gnulib-m4/acl.m4 \
+ $(top_srcdir)/gnulib-m4/alloca.m4 \
+ $(top_srcdir)/gnulib-m4/ansi-c++.m4 \
+ $(top_srcdir)/gnulib-m4/asm-underscore.m4 \
+ $(top_srcdir)/gnulib-m4/atexit.m4 \
+ $(top_srcdir)/gnulib-m4/backupfile.m4 \
+ $(top_srcdir)/gnulib-m4/bison-i18n.m4 \
+ $(top_srcdir)/gnulib-m4/btowc.m4 \
+ $(top_srcdir)/gnulib-m4/byteswap.m4 \
+ $(top_srcdir)/gnulib-m4/canonicalize.m4 \
+ $(top_srcdir)/gnulib-m4/close.m4 \
+ $(top_srcdir)/gnulib-m4/closedir.m4 \
+ $(top_srcdir)/gnulib-m4/configmake.m4 \
+ $(top_srcdir)/gnulib-m4/copy-file.m4 \
+ $(top_srcdir)/gnulib-m4/csharp.m4 \
+ $(top_srcdir)/gnulib-m4/csharpcomp.m4 \
+ $(top_srcdir)/gnulib-m4/csharpexec.m4 \
+ $(top_srcdir)/gnulib-m4/curses.m4 \
+ $(top_srcdir)/gnulib-m4/dirent_h.m4 \
+ $(top_srcdir)/gnulib-m4/double-slash-root.m4 \
+ $(top_srcdir)/gnulib-m4/dup.m4 $(top_srcdir)/gnulib-m4/dup2.m4 \
+ $(top_srcdir)/gnulib-m4/eaccess.m4 \
+ $(top_srcdir)/gnulib-m4/eealloc.m4 \
+ $(top_srcdir)/gnulib-m4/environ.m4 \
+ $(top_srcdir)/gnulib-m4/errno_h.m4 \
+ $(top_srcdir)/gnulib-m4/error.m4 \
+ $(top_srcdir)/gnulib-m4/execute.m4 \
+ $(top_srcdir)/gnulib-m4/exponentd.m4 \
+ $(top_srcdir)/gnulib-m4/extensions.m4 \
+ $(top_srcdir)/gnulib-m4/fatal-signal.m4 \
+ $(top_srcdir)/gnulib-m4/fcntl.m4 \
+ $(top_srcdir)/gnulib-m4/fcntl_h.m4 \
+ $(top_srcdir)/gnulib-m4/fdopen.m4 \
+ $(top_srcdir)/gnulib-m4/findprog.m4 \
+ $(top_srcdir)/gnulib-m4/float_h.m4 \
+ $(top_srcdir)/gnulib-m4/fnmatch.m4 \
+ $(top_srcdir)/gnulib-m4/fopen.m4 \
+ $(top_srcdir)/gnulib-m4/fpieee.m4 \
+ $(top_srcdir)/gnulib-m4/fseeko.m4 \
+ $(top_srcdir)/gnulib-m4/fstat.m4 \
+ $(top_srcdir)/gnulib-m4/ftell.m4 \
+ $(top_srcdir)/gnulib-m4/ftello.m4 \
+ $(top_srcdir)/gnulib-m4/gcj.m4 \
+ $(top_srcdir)/gnulib-m4/getcwd.m4 \
+ $(top_srcdir)/gnulib-m4/getdelim.m4 \
+ $(top_srcdir)/gnulib-m4/getdtablesize.m4 \
+ $(top_srcdir)/gnulib-m4/getline.m4 \
+ $(top_srcdir)/gnulib-m4/getopt.m4 \
+ $(top_srcdir)/gnulib-m4/getpagesize.m4 \
+ $(top_srcdir)/gnulib-m4/gettimeofday.m4 \
+ $(top_srcdir)/gnulib-m4/gnulib-common.m4 \
+ $(top_srcdir)/gnulib-m4/gnulib-comp.m4 \
+ $(top_srcdir)/gnulib-m4/iconv_h.m4 \
+ $(top_srcdir)/gnulib-m4/iconv_open.m4 \
+ $(top_srcdir)/gnulib-m4/include_next.m4 \
+ $(top_srcdir)/gnulib-m4/inline.m4 \
+ $(top_srcdir)/gnulib-m4/intmax_t.m4 \
+ $(top_srcdir)/gnulib-m4/inttypes.m4 \
+ $(top_srcdir)/gnulib-m4/iswblank.m4 \
+ $(top_srcdir)/gnulib-m4/java.m4 \
+ $(top_srcdir)/gnulib-m4/javacomp.m4 \
+ $(top_srcdir)/gnulib-m4/javaexec.m4 \
+ $(top_srcdir)/gnulib-m4/largefile.m4 \
+ $(top_srcdir)/gnulib-m4/lib-ld.m4 \
+ $(top_srcdir)/gnulib-m4/lib-link.m4 \
+ $(top_srcdir)/gnulib-m4/lib-prefix.m4 \
+ $(top_srcdir)/gnulib-m4/libcroco.m4 \
+ $(top_srcdir)/gnulib-m4/libglib.m4 \
+ $(top_srcdir)/gnulib-m4/libunistring-base.m4 \
+ $(top_srcdir)/gnulib-m4/libunistring-optional.m4 \
+ $(top_srcdir)/gnulib-m4/libunistring.m4 \
+ $(top_srcdir)/gnulib-m4/libxml.m4 \
+ $(top_srcdir)/gnulib-m4/localcharset.m4 \
+ $(top_srcdir)/gnulib-m4/locale-fr.m4 \
+ $(top_srcdir)/gnulib-m4/locale-ja.m4 \
+ $(top_srcdir)/gnulib-m4/locale-tr.m4 \
+ $(top_srcdir)/gnulib-m4/locale-zh.m4 \
+ $(top_srcdir)/gnulib-m4/locale_h.m4 \
+ $(top_srcdir)/gnulib-m4/localename.m4 \
+ $(top_srcdir)/gnulib-m4/lseek.m4 \
+ $(top_srcdir)/gnulib-m4/lstat.m4 \
+ $(top_srcdir)/gnulib-m4/malloc.m4 \
+ $(top_srcdir)/gnulib-m4/malloca.m4 \
+ $(top_srcdir)/gnulib-m4/mbchar.m4 \
+ $(top_srcdir)/gnulib-m4/mbiter.m4 \
+ $(top_srcdir)/gnulib-m4/mbrtowc.m4 \
+ $(top_srcdir)/gnulib-m4/mbsinit.m4 \
+ $(top_srcdir)/gnulib-m4/mbslen.m4 \
+ $(top_srcdir)/gnulib-m4/mbsrtowcs.m4 \
+ $(top_srcdir)/gnulib-m4/mbstate_t.m4 \
+ $(top_srcdir)/gnulib-m4/mbswidth.m4 \
+ $(top_srcdir)/gnulib-m4/mbtowc.m4 \
+ $(top_srcdir)/gnulib-m4/memchr.m4 \
+ $(top_srcdir)/gnulib-m4/memmove.m4 \
+ $(top_srcdir)/gnulib-m4/memset.m4 \
+ $(top_srcdir)/gnulib-m4/minmax.m4 \
+ $(top_srcdir)/gnulib-m4/mkdtemp.m4 \
+ $(top_srcdir)/gnulib-m4/mmap-anon.m4 \
+ $(top_srcdir)/gnulib-m4/mode_t.m4 \
+ $(top_srcdir)/gnulib-m4/moo.m4 \
+ $(top_srcdir)/gnulib-m4/msvc-inval.m4 \
+ $(top_srcdir)/gnulib-m4/msvc-nothrow.m4 \
+ $(top_srcdir)/gnulib-m4/multiarch.m4 \
+ $(top_srcdir)/gnulib-m4/no-c++.m4 \
+ $(top_srcdir)/gnulib-m4/nocrash.m4 \
+ $(top_srcdir)/gnulib-m4/obstack.m4 \
+ $(top_srcdir)/gnulib-m4/off_t.m4 \
+ $(top_srcdir)/gnulib-m4/open.m4 \
+ $(top_srcdir)/gnulib-m4/opendir.m4 \
+ $(top_srcdir)/gnulib-m4/pathmax.m4 \
+ $(top_srcdir)/gnulib-m4/pipe2.m4 \
+ $(top_srcdir)/gnulib-m4/posix_spawn.m4 \
+ $(top_srcdir)/gnulib-m4/printf.m4 \
+ $(top_srcdir)/gnulib-m4/putenv.m4 \
+ $(top_srcdir)/gnulib-m4/quote.m4 \
+ $(top_srcdir)/gnulib-m4/quotearg.m4 \
+ $(top_srcdir)/gnulib-m4/raise.m4 \
+ $(top_srcdir)/gnulib-m4/rawmemchr.m4 \
+ $(top_srcdir)/gnulib-m4/read-file.m4 \
+ $(top_srcdir)/gnulib-m4/read.m4 \
+ $(top_srcdir)/gnulib-m4/readdir.m4 \
+ $(top_srcdir)/gnulib-m4/readlink.m4 \
+ $(top_srcdir)/gnulib-m4/realloc.m4 \
+ $(top_srcdir)/gnulib-m4/relocatable-lib.m4 \
+ $(top_srcdir)/gnulib-m4/relocatable.m4 \
+ $(top_srcdir)/gnulib-m4/rmdir.m4 \
+ $(top_srcdir)/gnulib-m4/safe-read.m4 \
+ $(top_srcdir)/gnulib-m4/safe-write.m4 \
+ $(top_srcdir)/gnulib-m4/sched_h.m4 \
+ $(top_srcdir)/gnulib-m4/secure_getenv.m4 \
+ $(top_srcdir)/gnulib-m4/setenv.m4 \
+ $(top_srcdir)/gnulib-m4/setlocale.m4 \
+ $(top_srcdir)/gnulib-m4/sig_atomic_t.m4 \
+ $(top_srcdir)/gnulib-m4/sigaction.m4 \
+ $(top_srcdir)/gnulib-m4/signal_h.m4 \
+ $(top_srcdir)/gnulib-m4/signalblocking.m4 \
+ $(top_srcdir)/gnulib-m4/sigpipe.m4 \
+ $(top_srcdir)/gnulib-m4/sleep.m4 \
+ $(top_srcdir)/gnulib-m4/snprintf.m4 \
+ $(top_srcdir)/gnulib-m4/spawn-pipe.m4 \
+ $(top_srcdir)/gnulib-m4/spawn_h.m4 \
+ $(top_srcdir)/gnulib-m4/ssize_t.m4 \
+ $(top_srcdir)/gnulib-m4/stat.m4 \
+ $(top_srcdir)/gnulib-m4/stdarg.m4 \
+ $(top_srcdir)/gnulib-m4/stdbool.m4 \
+ $(top_srcdir)/gnulib-m4/stddef_h.m4 \
+ $(top_srcdir)/gnulib-m4/stdint.m4 \
+ $(top_srcdir)/gnulib-m4/stdio_h.m4 \
+ $(top_srcdir)/gnulib-m4/stdlib_h.m4 \
+ $(top_srcdir)/gnulib-m4/stpcpy.m4 \
+ $(top_srcdir)/gnulib-m4/stpncpy.m4 \
+ $(top_srcdir)/gnulib-m4/strchrnul.m4 \
+ $(top_srcdir)/gnulib-m4/strcspn.m4 \
+ $(top_srcdir)/gnulib-m4/strerror.m4 \
+ $(top_srcdir)/gnulib-m4/string_h.m4 \
+ $(top_srcdir)/gnulib-m4/strnlen.m4 \
+ $(top_srcdir)/gnulib-m4/strpbrk.m4 \
+ $(top_srcdir)/gnulib-m4/strstr.m4 \
+ $(top_srcdir)/gnulib-m4/strtol.m4 \
+ $(top_srcdir)/gnulib-m4/strtoul.m4 \
+ $(top_srcdir)/gnulib-m4/symlink.m4 \
+ $(top_srcdir)/gnulib-m4/sys_select_h.m4 \
+ $(top_srcdir)/gnulib-m4/sys_socket_h.m4 \
+ $(top_srcdir)/gnulib-m4/sys_stat_h.m4 \
+ $(top_srcdir)/gnulib-m4/sys_time_h.m4 \
+ $(top_srcdir)/gnulib-m4/sys_types_h.m4 \
+ $(top_srcdir)/gnulib-m4/sys_wait_h.m4 \
+ $(top_srcdir)/gnulib-m4/tempname.m4 \
+ $(top_srcdir)/gnulib-m4/term-ostream.m4 \
+ $(top_srcdir)/gnulib-m4/terminfo.m4 \
+ $(top_srcdir)/gnulib-m4/thread.m4 \
+ $(top_srcdir)/gnulib-m4/time_h.m4 \
+ $(top_srcdir)/gnulib-m4/tls.m4 \
+ $(top_srcdir)/gnulib-m4/tmpdir.m4 \
+ $(top_srcdir)/gnulib-m4/ungetc.m4 \
+ $(top_srcdir)/gnulib-m4/unionwait.m4 \
+ $(top_srcdir)/gnulib-m4/unistd-safer.m4 \
+ $(top_srcdir)/gnulib-m4/unistd_h.m4 \
+ $(top_srcdir)/gnulib-m4/unlocked-io.m4 \
+ $(top_srcdir)/gnulib-m4/vasnprintf.m4 \
+ $(top_srcdir)/gnulib-m4/vasprintf.m4 \
+ $(top_srcdir)/gnulib-m4/vsnprintf.m4 \
+ $(top_srcdir)/gnulib-m4/wait-process.m4 \
+ $(top_srcdir)/gnulib-m4/waitpid.m4 \
+ $(top_srcdir)/gnulib-m4/warn-on-use.m4 \
+ $(top_srcdir)/gnulib-m4/wchar_h.m4 \
+ $(top_srcdir)/gnulib-m4/wcrtomb.m4 \
+ $(top_srcdir)/gnulib-m4/wctob.m4 \
+ $(top_srcdir)/gnulib-m4/wctomb.m4 \
+ $(top_srcdir)/gnulib-m4/wctype_h.m4 \
+ $(top_srcdir)/gnulib-m4/wcwidth.m4 \
+ $(top_srcdir)/gnulib-m4/write.m4 \
+ $(top_srcdir)/gnulib-m4/xvasprintf.m4 \
+ $(top_srcdir)/gnulib-m4/yield.m4 \
+ $(top_srcdir)/../m4/fixautomake.m4 \
+ $(top_srcdir)/../m4/libtool.m4 \
+ $(top_srcdir)/../m4/ltoptions.m4 \
+ $(top_srcdir)/../m4/ltsugar.m4 \
+ $(top_srcdir)/../m4/ltversion.m4 \
+ $(top_srcdir)/../m4/lt~obsolete.m4 \
+ $(top_srcdir)/../m4/woe32-dll.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/codeset.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/extern-inline.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/fcntl-o.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/gettext.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/glibc2.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/glibc21.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/iconv.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/intdiv0.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/intl.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/intlmacosx.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/intmax.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/inttypes-pri.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/inttypes_h.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/lcmessage.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/lock.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/longlong.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/nls.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/po.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/printf-posix.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/progtest.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/size_max.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/stdint_h.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/threadlib.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/uintmax_t.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/visibility.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/wchar_t.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/wint_t.m4 \
+ $(top_srcdir)/../gettext-runtime/m4/xsize.m4 \
+ $(top_srcdir)/m4/exported.m4 $(top_srcdir)/m4/hostname.m4 \
+ $(top_srcdir)/m4/setlocale.m4 $(top_srcdir)/m4/siginfo.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/../build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = user-email
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libgettextsrc_la_LIBADD =
+am__libgettextsrc_la_SOURCES_DIST = message.c po-error.c po-xerror.c \
+ read-catalog-abstract.c po-lex.c po-gram-gen.y po-charset.c \
+ read-po.c read-properties.c read-stringtable.c open-catalog.c \
+ dir-list.c str-list.c read-catalog.c color.c write-catalog.c \
+ write-properties.c write-stringtable.c write-po.c msgl-ascii.c \
+ msgl-iconv.c msgl-equal.c msgl-cat.c msgl-header.c \
+ msgl-english.c msgl-check.c file-list.c msgl-charset.c \
+ po-time.c plural-exp.c plural-eval.c plural-table.c format.c \
+ format-invalid.h format-c.c format-c-parse.h format-sh.c \
+ format-python.c format-python-brace.c format-lisp.c \
+ format-elisp.c format-librep.c format-scheme.c format-java.c \
+ format-csharp.c format-awk.c format-pascal.c format-ycp.c \
+ format-tcl.c format-perl.c format-perl-brace.c format-php.c \
+ format-gcc-internal.c format-gfc-internal.c format-qt.c \
+ format-qt-plural.c format-kde.c format-boost.c format-lua.c \
+ format-javascript.c ../woe32dll/c++format.cc read-desktop.c \
+ ../woe32dll/gettextsrc-exports.c
+am__objects_1 = libgettextsrc_la-message.lo \
+ libgettextsrc_la-po-error.lo libgettextsrc_la-po-xerror.lo \
+ libgettextsrc_la-read-catalog-abstract.lo \
+ libgettextsrc_la-po-lex.lo libgettextsrc_la-po-gram-gen.lo \
+ libgettextsrc_la-po-charset.lo libgettextsrc_la-read-po.lo \
+ libgettextsrc_la-read-properties.lo \
+ libgettextsrc_la-read-stringtable.lo \
+ libgettextsrc_la-open-catalog.lo libgettextsrc_la-dir-list.lo \
+ libgettextsrc_la-str-list.lo
+am__dirstamp = $(am__leading_dot)dirstamp
+@WOE32DLL_FALSE@am__objects_2 = libgettextsrc_la-format.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-c.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-sh.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-python.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-python-brace.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-lisp.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-elisp.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-librep.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-scheme.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-java.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-csharp.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-awk.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-pascal.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-ycp.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-tcl.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-perl.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-perl-brace.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-php.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-gcc-internal.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-gfc-internal.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-qt.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-qt-plural.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-kde.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-boost.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-lua.lo \
+@WOE32DLL_FALSE@ libgettextsrc_la-format-javascript.lo
+@WOE32DLL_TRUE@am__objects_2 = \
+@WOE32DLL_TRUE@ ../woe32dll/libgettextsrc_la-c++format.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-c.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-sh.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-python.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-python-brace.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-lisp.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-elisp.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-librep.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-scheme.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-java.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-csharp.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-awk.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-pascal.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-ycp.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-tcl.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-perl.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-perl-brace.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-php.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-gcc-internal.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-gfc-internal.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-qt.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-qt-plural.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-kde.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-boost.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-lua.lo \
+@WOE32DLL_TRUE@ libgettextsrc_la-format-javascript.lo
+@WOE32DLL_TRUE@am__objects_3 = ../woe32dll/libgettextsrc_la-gettextsrc-exports.lo
+am_libgettextsrc_la_OBJECTS = $(am__objects_1) \
+ libgettextsrc_la-read-catalog.lo libgettextsrc_la-color.lo \
+ libgettextsrc_la-write-catalog.lo \
+ libgettextsrc_la-write-properties.lo \
+ libgettextsrc_la-write-stringtable.lo \
+ libgettextsrc_la-write-po.lo libgettextsrc_la-msgl-ascii.lo \
+ libgettextsrc_la-msgl-iconv.lo libgettextsrc_la-msgl-equal.lo \
+ libgettextsrc_la-msgl-cat.lo libgettextsrc_la-msgl-header.lo \
+ libgettextsrc_la-msgl-english.lo \
+ libgettextsrc_la-msgl-check.lo libgettextsrc_la-file-list.lo \
+ libgettextsrc_la-msgl-charset.lo libgettextsrc_la-po-time.lo \
+ libgettextsrc_la-plural-exp.lo libgettextsrc_la-plural-eval.lo \
+ libgettextsrc_la-plural-table.lo $(am__objects_2) \
+ libgettextsrc_la-read-desktop.lo $(am__objects_3)
+libgettextsrc_la_OBJECTS = $(am_libgettextsrc_la_OBJECTS)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am_hostname_OBJECTS = hostname-hostname.$(OBJEXT)
+hostname_OBJECTS = $(am_hostname_OBJECTS)
+hostname_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+@WOE32_TRUE@am__DEPENDENCIES_2 = gettext.res
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+hostname_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(hostname_LDFLAGS) $(LDFLAGS) -o $@
+am__msgattrib_SOURCES_DIST = msgattrib.c ../woe32dll/c++msgattrib.cc
+@WOE32DLL_FALSE@am_msgattrib_OBJECTS = msgattrib-msgattrib.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgattrib_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgattrib-c++msgattrib.$(OBJEXT)
+msgattrib_OBJECTS = $(am_msgattrib_OBJECTS)
+am__msgcat_SOURCES_DIST = msgcat.c ../woe32dll/c++msgcat.cc
+@WOE32DLL_FALSE@am_msgcat_OBJECTS = msgcat-msgcat.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgcat_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgcat-c++msgcat.$(OBJEXT)
+msgcat_OBJECTS = $(am_msgcat_OBJECTS)
+am_msgcmp_OBJECTS = msgcmp-msgcmp.$(OBJEXT) \
+ msgcmp-msgl-fsearch.$(OBJEXT)
+msgcmp_OBJECTS = $(am_msgcmp_OBJECTS)
+msgcmp_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(msgcmp_LDFLAGS) $(LDFLAGS) -o $@
+am__msgcomm_SOURCES_DIST = msgcomm.c ../woe32dll/c++msgcomm.cc
+@WOE32DLL_FALSE@am_msgcomm_OBJECTS = msgcomm-msgcomm.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgcomm_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgcomm-c++msgcomm.$(OBJEXT)
+msgcomm_OBJECTS = $(am_msgcomm_OBJECTS)
+am__msgconv_SOURCES_DIST = msgconv.c ../woe32dll/c++msgconv.cc
+@WOE32DLL_FALSE@am_msgconv_OBJECTS = msgconv-msgconv.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgconv_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgconv-c++msgconv.$(OBJEXT)
+msgconv_OBJECTS = $(am_msgconv_OBJECTS)
+am__msgen_SOURCES_DIST = msgen.c ../woe32dll/c++msgen.cc
+@WOE32DLL_FALSE@am_msgen_OBJECTS = msgen-msgen.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgen_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgen-c++msgen.$(OBJEXT)
+msgen_OBJECTS = $(am_msgen_OBJECTS)
+am_msgexec_OBJECTS = msgexec-msgexec.$(OBJEXT)
+msgexec_OBJECTS = $(am_msgexec_OBJECTS)
+msgexec_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(msgexec_LDFLAGS) $(LDFLAGS) -o $@
+am__msgfilter_SOURCES_DIST = msgfilter.c filter-sr-latin.c \
+ filter-quote.c ../woe32dll/c++msgfilter.cc
+@WOE32DLL_FALSE@am_msgfilter_OBJECTS = msgfilter-msgfilter.$(OBJEXT) \
+@WOE32DLL_FALSE@ msgfilter-filter-sr-latin.$(OBJEXT) \
+@WOE32DLL_FALSE@ msgfilter-filter-quote.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgfilter_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgfilter-c++msgfilter.$(OBJEXT) \
+@WOE32DLL_TRUE@ msgfilter-filter-sr-latin.$(OBJEXT) \
+@WOE32DLL_TRUE@ msgfilter-filter-quote.$(OBJEXT)
+msgfilter_OBJECTS = $(am_msgfilter_OBJECTS)
+am_msgfmt_OBJECTS = msgfmt-msgfmt.$(OBJEXT) msgfmt-write-mo.$(OBJEXT) \
+ msgfmt-write-java.$(OBJEXT) msgfmt-write-csharp.$(OBJEXT) \
+ msgfmt-write-resources.$(OBJEXT) msgfmt-write-tcl.$(OBJEXT) \
+ msgfmt-write-qt.$(OBJEXT) msgfmt-write-desktop.$(OBJEXT) \
+ ../../gettext-runtime/intl/msgfmt-hash-string.$(OBJEXT)
+msgfmt_OBJECTS = $(am_msgfmt_OBJECTS)
+msgfmt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(msgfmt_LDFLAGS) $(LDFLAGS) -o $@
+am__msggrep_SOURCES_DIST = msggrep.c ../woe32dll/c++msggrep.cc
+@WOE32DLL_FALSE@am_msggrep_OBJECTS = msggrep-msggrep.$(OBJEXT)
+@WOE32DLL_TRUE@am_msggrep_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msggrep-c++msggrep.$(OBJEXT)
+msggrep_OBJECTS = $(am_msggrep_OBJECTS)
+am_msginit_OBJECTS = msginit-msginit.$(OBJEXT) \
+ msginit-lang-table.$(OBJEXT) msginit-plural-count.$(OBJEXT) \
+ ../../gettext-runtime/intl/msginit-localealias.$(OBJEXT) \
+ ../../gettext-runtime/intl/msginit-lock.$(OBJEXT)
+msginit_OBJECTS = $(am_msginit_OBJECTS)
+msginit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(msginit_LDFLAGS) $(LDFLAGS) -o $@
+am__msgmerge_SOURCES_DIST = msgmerge.c msgl-fsearch.c lang-table.c \
+ plural-count.c ../woe32dll/c++msgmerge.cc
+@WOE32DLL_FALSE@am_msgmerge_OBJECTS = msgmerge-msgmerge.$(OBJEXT) \
+@WOE32DLL_FALSE@ msgmerge-msgl-fsearch.$(OBJEXT) \
+@WOE32DLL_FALSE@ msgmerge-lang-table.$(OBJEXT) \
+@WOE32DLL_FALSE@ msgmerge-plural-count.$(OBJEXT)
+@WOE32DLL_TRUE@am_msgmerge_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msgmerge-c++msgmerge.$(OBJEXT) \
+@WOE32DLL_TRUE@ msgmerge-msgl-fsearch.$(OBJEXT) \
+@WOE32DLL_TRUE@ msgmerge-lang-table.$(OBJEXT) \
+@WOE32DLL_TRUE@ msgmerge-plural-count.$(OBJEXT)
+msgmerge_OBJECTS = $(am_msgmerge_OBJECTS)
+am_msgunfmt_OBJECTS = msgunfmt-msgunfmt.$(OBJEXT) \
+ msgunfmt-read-mo.$(OBJEXT) msgunfmt-read-java.$(OBJEXT) \
+ msgunfmt-read-csharp.$(OBJEXT) \
+ msgunfmt-read-resources.$(OBJEXT) msgunfmt-read-tcl.$(OBJEXT)
+msgunfmt_OBJECTS = $(am_msgunfmt_OBJECTS)
+msgunfmt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(msgunfmt_LDFLAGS) $(LDFLAGS) -o $@
+am__msguniq_SOURCES_DIST = msguniq.c ../woe32dll/c++msguniq.cc
+@WOE32DLL_FALSE@am_msguniq_OBJECTS = msguniq-msguniq.$(OBJEXT)
+@WOE32DLL_TRUE@am_msguniq_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/msguniq-c++msguniq.$(OBJEXT)
+msguniq_OBJECTS = $(am_msguniq_OBJECTS)
+am_recode_sr_latin_OBJECTS = \
+ recode_sr_latin-recode-sr-latin.$(OBJEXT) \
+ recode_sr_latin-filter-sr-latin.$(OBJEXT)
+recode_sr_latin_OBJECTS = $(am_recode_sr_latin_OBJECTS)
+recode_sr_latin_LDADD = $(LDADD)
+recode_sr_latin_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(recode_sr_latin_LDFLAGS) $(LDFLAGS) \
+ -o $@
+am_urlget_OBJECTS = urlget-urlget.$(OBJEXT)
+urlget_OBJECTS = $(am_urlget_OBJECTS)
+urlget_LDADD = $(LDADD)
+urlget_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(urlget_LDFLAGS) $(LDFLAGS) -o $@
+am__xgettext_SOURCES_DIST = xgettext.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-glade.c x-lua.c x-javascript.c x-vala.c \
+ x-gsettings.c libexpat-compat.c x-desktop.c \
+ ../woe32dll/c++xgettext.cc
+@WOE32DLL_FALSE@am_xgettext_OBJECTS = xgettext-xgettext.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-c.$(OBJEXT) xgettext-x-po.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-sh.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-python.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-lisp.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-elisp.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-librep.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-scheme.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-smalltalk.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-java.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-csharp.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-awk.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-ycp.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-tcl.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-perl.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-php.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-rst.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-glade.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-lua.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-javascript.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-vala.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-gsettings.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-libexpat-compat.$(OBJEXT) \
+@WOE32DLL_FALSE@ xgettext-x-desktop.$(OBJEXT)
+@WOE32DLL_TRUE@am_xgettext_OBJECTS = \
+@WOE32DLL_TRUE@ ../woe32dll/xgettext-c++xgettext.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-c.$(OBJEXT) xgettext-x-po.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-sh.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-python.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-lisp.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-elisp.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-librep.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-scheme.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-smalltalk.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-java.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-csharp.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-awk.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-ycp.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-tcl.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-perl.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-php.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-rst.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-glade.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-lua.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-javascript.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-vala.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-gsettings.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-libexpat-compat.$(OBJEXT) \
+@WOE32DLL_TRUE@ xgettext-x-desktop.$(OBJEXT)
+xgettext_OBJECTS = $(am_xgettext_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp =
+am__depfiles_maybe =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
+YLWRAP = $(top_srcdir)/../build-aux/ylwrap
+SOURCES = $(libgettextsrc_la_SOURCES) $(hostname_SOURCES) \
+ $(msgattrib_SOURCES) $(msgcat_SOURCES) $(msgcmp_SOURCES) \
+ $(msgcomm_SOURCES) $(msgconv_SOURCES) $(msgen_SOURCES) \
+ $(msgexec_SOURCES) $(msgfilter_SOURCES) $(msgfmt_SOURCES) \
+ $(msggrep_SOURCES) $(msginit_SOURCES) $(msgmerge_SOURCES) \
+ $(msgunfmt_SOURCES) $(msguniq_SOURCES) \
+ $(recode_sr_latin_SOURCES) $(urlget_SOURCES) \
+ $(xgettext_SOURCES)
+DIST_SOURCES = $(am__libgettextsrc_la_SOURCES_DIST) \
+ $(hostname_SOURCES) $(am__msgattrib_SOURCES_DIST) \
+ $(am__msgcat_SOURCES_DIST) $(msgcmp_SOURCES) \
+ $(am__msgcomm_SOURCES_DIST) $(am__msgconv_SOURCES_DIST) \
+ $(am__msgen_SOURCES_DIST) $(msgexec_SOURCES) \
+ $(am__msgfilter_SOURCES_DIST) $(msgfmt_SOURCES) \
+ $(am__msggrep_SOURCES_DIST) $(msginit_SOURCES) \
+ $(am__msgmerge_SOURCES_DIST) $(msgunfmt_SOURCES) \
+ $(am__msguniq_SOURCES_DIST) $(recode_sr_latin_SOURCES) \
+ $(urlget_SOURCES) $(am__xgettext_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+pkgdatadir = $(datadir)/gettext
+pkglibdir = $(libdir)/gettext
+pkglibexecdir = @pkglibexecdir@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
+AR = @AR@
+ARCHIVE_FORMAT = @ARCHIVE_FORMAT@
+ARFLAGS = @ARFLAGS@
+AS = @AS@
+ASM_SYMBOL_PREFIX = @ASM_SYMBOL_PREFIX@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BISON_LOCALEDIR = @BISON_LOCALEDIR@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+BUILDCSHARP = @BUILDCSHARP@
+BUILDJAVA = @BUILDJAVA@
+BUILDJAVAEXE = @BUILDJAVAEXE@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+BYTESWAP_H = @BYTESWAP_H@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CLASSPATH = @CLASSPATH@
+CLASSPATH_SEPARATOR = @CLASSPATH_SEPARATOR@
+CLIX_PATH = @CLIX_PATH@
+CLIX_PATH_VAR = @CLIX_PATH_VAR@
+CONF_JAVA = @CONF_JAVA@
+CONF_JAVAC = @CONF_JAVAC@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CROSS_COMPILING = @CROSS_COMPILING@
+CSHARPCOMPFLAGS = @CSHARPCOMPFLAGS@
+CSHARP_CHOICE = @CSHARP_CHOICE@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXX_CHOICE = @CXX_CHOICE@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = \
+ -DLOCALEDIR=\"$(localedir)\" -DBISON_LOCALEDIR=\"$(BISON_LOCALEDIR)\" \
+ -DLOCALE_ALIAS_PATH=\"$(aliaspath)\" \
+ -DUSEJAVA=$(USEJAVA) \
+ -DUSEJEXE=$(USEJEXE) \
+ -DGETTEXTJEXEDIR=\"$(pkglibdir)\" \
+ -DGETTEXTJAR=\"$(jardir)/gettext.jar\" \
+ -DLIBDIR=\"$(libdir)\" \
+ -DGETTEXTDATADIR=\"$(pkgdatadir)\" \
+ -DPROJECTSDIR=\"$(projectsdir)\" @DEFS@
+
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DVIPS = @DVIPS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EMACS = @EMACS@
+EMACSLOADPATH = @EMACSLOADPATH@
+EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@
+EMULTIHOP_VALUE = @EMULTIHOP_VALUE@
+ENOLINK_HIDDEN = @ENOLINK_HIDDEN@
+ENOLINK_VALUE = @ENOLINK_VALUE@
+EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@
+EOVERFLOW_VALUE = @EOVERFLOW_VALUE@
+ERRNO_H = @ERRNO_H@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FLOAT_H = @FLOAT_H@
+FNMATCH_H = @FNMATCH_H@
+GCJ = @GCJ@
+GCJFLAGS = @GCJFLAGS@
+GENCAT = @GENCAT@
+GETOPT_H = @GETOPT_H@
+GETTEXTLIB_EXPORTS_FLAGS = @GETTEXTLIB_EXPORTS_FLAGS@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GLIBC2 = @GLIBC2@
+GLIBC21 = @GLIBC21@
+GLOBAL_SYMBOL_PIPE = @GLOBAL_SYMBOL_PIPE@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNULIB_ALPHASORT = @GNULIB_ALPHASORT@
+GNULIB_ATOLL = @GNULIB_ATOLL@
+GNULIB_BTOWC = @GNULIB_BTOWC@
+GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@
+GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@
+GNULIB_CHDIR = @GNULIB_CHDIR@
+GNULIB_CHOWN = @GNULIB_CHOWN@
+GNULIB_CLOSE = @GNULIB_CLOSE@
+GNULIB_CLOSEDIR = @GNULIB_CLOSEDIR@
+GNULIB_DIRFD = @GNULIB_DIRFD@
+GNULIB_DPRINTF = @GNULIB_DPRINTF@
+GNULIB_DUP = @GNULIB_DUP@
+GNULIB_DUP2 = @GNULIB_DUP2@
+GNULIB_DUP3 = @GNULIB_DUP3@
+GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@
+GNULIB_ENVIRON = @GNULIB_ENVIRON@
+GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@
+GNULIB_FACCESSAT = @GNULIB_FACCESSAT@
+GNULIB_FCHDIR = @GNULIB_FCHDIR@
+GNULIB_FCHMODAT = @GNULIB_FCHMODAT@
+GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@
+GNULIB_FCLOSE = @GNULIB_FCLOSE@
+GNULIB_FCNTL = @GNULIB_FCNTL@
+GNULIB_FDATASYNC = @GNULIB_FDATASYNC@
+GNULIB_FDOPEN = @GNULIB_FDOPEN@
+GNULIB_FDOPENDIR = @GNULIB_FDOPENDIR@
+GNULIB_FFLUSH = @GNULIB_FFLUSH@
+GNULIB_FFSL = @GNULIB_FFSL@
+GNULIB_FFSLL = @GNULIB_FFSLL@
+GNULIB_FGETC = @GNULIB_FGETC@
+GNULIB_FGETS = @GNULIB_FGETS@
+GNULIB_FOPEN = @GNULIB_FOPEN@
+GNULIB_FPRINTF = @GNULIB_FPRINTF@
+GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@
+GNULIB_FPURGE = @GNULIB_FPURGE@
+GNULIB_FPUTC = @GNULIB_FPUTC@
+GNULIB_FPUTS = @GNULIB_FPUTS@
+GNULIB_FREAD = @GNULIB_FREAD@
+GNULIB_FREOPEN = @GNULIB_FREOPEN@
+GNULIB_FSCANF = @GNULIB_FSCANF@
+GNULIB_FSEEK = @GNULIB_FSEEK@
+GNULIB_FSEEKO = @GNULIB_FSEEKO@
+GNULIB_FSTAT = @GNULIB_FSTAT@
+GNULIB_FSTATAT = @GNULIB_FSTATAT@
+GNULIB_FSYNC = @GNULIB_FSYNC@
+GNULIB_FTELL = @GNULIB_FTELL@
+GNULIB_FTELLO = @GNULIB_FTELLO@
+GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
+GNULIB_FUTIMENS = @GNULIB_FUTIMENS@
+GNULIB_FWRITE = @GNULIB_FWRITE@
+GNULIB_GETC = @GNULIB_GETC@
+GNULIB_GETCHAR = @GNULIB_GETCHAR@
+GNULIB_GETCWD = @GNULIB_GETCWD@
+GNULIB_GETDELIM = @GNULIB_GETDELIM@
+GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@
+GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@
+GNULIB_GETGROUPS = @GNULIB_GETGROUPS@
+GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@
+GNULIB_GETLINE = @GNULIB_GETLINE@
+GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@
+GNULIB_GETLOGIN = @GNULIB_GETLOGIN@
+GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
+GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@
+GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
+GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@
+GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@
+GNULIB_GL_UNISTD_H_GETOPT = @GNULIB_GL_UNISTD_H_GETOPT@
+GNULIB_GRANTPT = @GNULIB_GRANTPT@
+GNULIB_GROUP_MEMBER = @GNULIB_GROUP_MEMBER@
+GNULIB_ICONV = @GNULIB_ICONV@
+GNULIB_IMAXABS = @GNULIB_IMAXABS@
+GNULIB_IMAXDIV = @GNULIB_IMAXDIV@
+GNULIB_ISATTY = @GNULIB_ISATTY@
+GNULIB_ISWBLANK = @GNULIB_ISWBLANK@
+GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@
+GNULIB_LCHMOD = @GNULIB_LCHMOD@
+GNULIB_LCHOWN = @GNULIB_LCHOWN@
+GNULIB_LINK = @GNULIB_LINK@
+GNULIB_LINKAT = @GNULIB_LINKAT@
+GNULIB_LOCALECONV = @GNULIB_LOCALECONV@
+GNULIB_LSEEK = @GNULIB_LSEEK@
+GNULIB_LSTAT = @GNULIB_LSTAT@
+GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@
+GNULIB_MBRLEN = @GNULIB_MBRLEN@
+GNULIB_MBRTOWC = @GNULIB_MBRTOWC@
+GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
+GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
+GNULIB_MBSCHR = @GNULIB_MBSCHR@
+GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
+GNULIB_MBSINIT = @GNULIB_MBSINIT@
+GNULIB_MBSLEN = @GNULIB_MBSLEN@
+GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
+GNULIB_MBSNLEN = @GNULIB_MBSNLEN@
+GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@
+GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
+GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
+GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
+GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@
+GNULIB_MBSSEP = @GNULIB_MBSSEP@
+GNULIB_MBSSPN = @GNULIB_MBSSPN@
+GNULIB_MBSSTR = @GNULIB_MBSSTR@
+GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
+GNULIB_MBTOWC = @GNULIB_MBTOWC@
+GNULIB_MEMCHR = @GNULIB_MEMCHR@
+GNULIB_MEMMEM = @GNULIB_MEMMEM@
+GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
+GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
+GNULIB_MKDIRAT = @GNULIB_MKDIRAT@
+GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
+GNULIB_MKFIFO = @GNULIB_MKFIFO@
+GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@
+GNULIB_MKNOD = @GNULIB_MKNOD@
+GNULIB_MKNODAT = @GNULIB_MKNODAT@
+GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@
+GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@
+GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
+GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@
+GNULIB_MKTIME = @GNULIB_MKTIME@
+GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@
+GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@
+GNULIB_NONBLOCKING = @GNULIB_NONBLOCKING@
+GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@
+GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@
+GNULIB_OPEN = @GNULIB_OPEN@
+GNULIB_OPENAT = @GNULIB_OPENAT@
+GNULIB_OPENDIR = @GNULIB_OPENDIR@
+GNULIB_PCLOSE = @GNULIB_PCLOSE@
+GNULIB_PERROR = @GNULIB_PERROR@
+GNULIB_PIPE = @GNULIB_PIPE@
+GNULIB_PIPE2 = @GNULIB_PIPE2@
+GNULIB_POPEN = @GNULIB_POPEN@
+GNULIB_POSIX_OPENPT = @GNULIB_POSIX_OPENPT@
+GNULIB_POSIX_SPAWN = @GNULIB_POSIX_SPAWN@
+GNULIB_POSIX_SPAWNATTR_DESTROY = @GNULIB_POSIX_SPAWNATTR_DESTROY@
+GNULIB_POSIX_SPAWNATTR_GETFLAGS = @GNULIB_POSIX_SPAWNATTR_GETFLAGS@
+GNULIB_POSIX_SPAWNATTR_GETPGROUP = @GNULIB_POSIX_SPAWNATTR_GETPGROUP@
+GNULIB_POSIX_SPAWNATTR_GETSCHEDPARAM = @GNULIB_POSIX_SPAWNATTR_GETSCHEDPARAM@
+GNULIB_POSIX_SPAWNATTR_GETSCHEDPOLICY = @GNULIB_POSIX_SPAWNATTR_GETSCHEDPOLICY@
+GNULIB_POSIX_SPAWNATTR_GETSIGDEFAULT = @GNULIB_POSIX_SPAWNATTR_GETSIGDEFAULT@
+GNULIB_POSIX_SPAWNATTR_GETSIGMASK = @GNULIB_POSIX_SPAWNATTR_GETSIGMASK@
+GNULIB_POSIX_SPAWNATTR_INIT = @GNULIB_POSIX_SPAWNATTR_INIT@
+GNULIB_POSIX_SPAWNATTR_SETFLAGS = @GNULIB_POSIX_SPAWNATTR_SETFLAGS@
+GNULIB_POSIX_SPAWNATTR_SETPGROUP = @GNULIB_POSIX_SPAWNATTR_SETPGROUP@
+GNULIB_POSIX_SPAWNATTR_SETSCHEDPARAM = @GNULIB_POSIX_SPAWNATTR_SETSCHEDPARAM@
+GNULIB_POSIX_SPAWNATTR_SETSCHEDPOLICY = @GNULIB_POSIX_SPAWNATTR_SETSCHEDPOLICY@
+GNULIB_POSIX_SPAWNATTR_SETSIGDEFAULT = @GNULIB_POSIX_SPAWNATTR_SETSIGDEFAULT@
+GNULIB_POSIX_SPAWNATTR_SETSIGMASK = @GNULIB_POSIX_SPAWNATTR_SETSIGMASK@
+GNULIB_POSIX_SPAWNP = @GNULIB_POSIX_SPAWNP@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2 = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_DESTROY = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_DESTROY@
+GNULIB_POSIX_SPAWN_FILE_ACTIONS_INIT = @GNULIB_POSIX_SPAWN_FILE_ACTIONS_INIT@
+GNULIB_PREAD = @GNULIB_PREAD@
+GNULIB_PRINTF = @GNULIB_PRINTF@
+GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@
+GNULIB_PSELECT = @GNULIB_PSELECT@
+GNULIB_PTHREAD_SIGMASK = @GNULIB_PTHREAD_SIGMASK@
+GNULIB_PTSNAME = @GNULIB_PTSNAME@
+GNULIB_PTSNAME_R = @GNULIB_PTSNAME_R@
+GNULIB_PUTC = @GNULIB_PUTC@
+GNULIB_PUTCHAR = @GNULIB_PUTCHAR@
+GNULIB_PUTENV = @GNULIB_PUTENV@
+GNULIB_PUTS = @GNULIB_PUTS@
+GNULIB_PWRITE = @GNULIB_PWRITE@
+GNULIB_QSORT_R = @GNULIB_QSORT_R@
+GNULIB_RAISE = @GNULIB_RAISE@
+GNULIB_RANDOM = @GNULIB_RANDOM@
+GNULIB_RANDOM_R = @GNULIB_RANDOM_R@
+GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@
+GNULIB_READ = @GNULIB_READ@
+GNULIB_READDIR = @GNULIB_READDIR@
+GNULIB_READLINK = @GNULIB_READLINK@
+GNULIB_READLINKAT = @GNULIB_READLINKAT@
+GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@
+GNULIB_REALPATH = @GNULIB_REALPATH@
+GNULIB_REMOVE = @GNULIB_REMOVE@
+GNULIB_RENAME = @GNULIB_RENAME@
+GNULIB_RENAMEAT = @GNULIB_RENAMEAT@
+GNULIB_REWINDDIR = @GNULIB_REWINDDIR@
+GNULIB_RMDIR = @GNULIB_RMDIR@
+GNULIB_RPMATCH = @GNULIB_RPMATCH@
+GNULIB_SCANDIR = @GNULIB_SCANDIR@
+GNULIB_SCANF = @GNULIB_SCANF@
+GNULIB_SECURE_GETENV = @GNULIB_SECURE_GETENV@
+GNULIB_SELECT = @GNULIB_SELECT@
+GNULIB_SETENV = @GNULIB_SETENV@
+GNULIB_SETHOSTNAME = @GNULIB_SETHOSTNAME@
+GNULIB_SETLOCALE = @GNULIB_SETLOCALE@
+GNULIB_SIGACTION = @GNULIB_SIGACTION@
+GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@
+GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@
+GNULIB_SLEEP = @GNULIB_SLEEP@
+GNULIB_SNPRINTF = @GNULIB_SNPRINTF@
+GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@
+GNULIB_STAT = @GNULIB_STAT@
+GNULIB_STDIO_H_NONBLOCKING = @GNULIB_STDIO_H_NONBLOCKING@
+GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@
+GNULIB_STPCPY = @GNULIB_STPCPY@
+GNULIB_STPNCPY = @GNULIB_STPNCPY@
+GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
+GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
+GNULIB_STRDUP = @GNULIB_STRDUP@
+GNULIB_STRERROR = @GNULIB_STRERROR@
+GNULIB_STRERROR_R = @GNULIB_STRERROR_R@
+GNULIB_STRNCAT = @GNULIB_STRNCAT@
+GNULIB_STRNDUP = @GNULIB_STRNDUP@
+GNULIB_STRNLEN = @GNULIB_STRNLEN@
+GNULIB_STRPBRK = @GNULIB_STRPBRK@
+GNULIB_STRPTIME = @GNULIB_STRPTIME@
+GNULIB_STRSEP = @GNULIB_STRSEP@
+GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@
+GNULIB_STRSTR = @GNULIB_STRSTR@
+GNULIB_STRTOD = @GNULIB_STRTOD@
+GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@
+GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
+GNULIB_STRTOLL = @GNULIB_STRTOLL@
+GNULIB_STRTOULL = @GNULIB_STRTOULL@
+GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@
+GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@
+GNULIB_SYMLINK = @GNULIB_SYMLINK@
+GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@
+GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@
+GNULIB_TIMEGM = @GNULIB_TIMEGM@
+GNULIB_TIME_R = @GNULIB_TIME_R@
+GNULIB_TMPFILE = @GNULIB_TMPFILE@
+GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@
+GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@
+GNULIB_UNISTD_H_NONBLOCKING = @GNULIB_UNISTD_H_NONBLOCKING@
+GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@
+GNULIB_UNLINK = @GNULIB_UNLINK@
+GNULIB_UNLINKAT = @GNULIB_UNLINKAT@
+GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@
+GNULIB_UNSETENV = @GNULIB_UNSETENV@
+GNULIB_USLEEP = @GNULIB_USLEEP@
+GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@
+GNULIB_VASPRINTF = @GNULIB_VASPRINTF@
+GNULIB_VDPRINTF = @GNULIB_VDPRINTF@
+GNULIB_VFPRINTF = @GNULIB_VFPRINTF@
+GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@
+GNULIB_VFSCANF = @GNULIB_VFSCANF@
+GNULIB_VPRINTF = @GNULIB_VPRINTF@
+GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@
+GNULIB_VSCANF = @GNULIB_VSCANF@
+GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@
+GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@
+GNULIB_WAITPID = @GNULIB_WAITPID@
+GNULIB_WCPCPY = @GNULIB_WCPCPY@
+GNULIB_WCPNCPY = @GNULIB_WCPNCPY@
+GNULIB_WCRTOMB = @GNULIB_WCRTOMB@
+GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@
+GNULIB_WCSCAT = @GNULIB_WCSCAT@
+GNULIB_WCSCHR = @GNULIB_WCSCHR@
+GNULIB_WCSCMP = @GNULIB_WCSCMP@
+GNULIB_WCSCOLL = @GNULIB_WCSCOLL@
+GNULIB_WCSCPY = @GNULIB_WCSCPY@
+GNULIB_WCSCSPN = @GNULIB_WCSCSPN@
+GNULIB_WCSDUP = @GNULIB_WCSDUP@
+GNULIB_WCSLEN = @GNULIB_WCSLEN@
+GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@
+GNULIB_WCSNCAT = @GNULIB_WCSNCAT@
+GNULIB_WCSNCMP = @GNULIB_WCSNCMP@
+GNULIB_WCSNCPY = @GNULIB_WCSNCPY@
+GNULIB_WCSNLEN = @GNULIB_WCSNLEN@
+GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@
+GNULIB_WCSPBRK = @GNULIB_WCSPBRK@
+GNULIB_WCSRCHR = @GNULIB_WCSRCHR@
+GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@
+GNULIB_WCSSPN = @GNULIB_WCSSPN@
+GNULIB_WCSSTR = @GNULIB_WCSSTR@
+GNULIB_WCSTOK = @GNULIB_WCSTOK@
+GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@
+GNULIB_WCSXFRM = @GNULIB_WCSXFRM@
+GNULIB_WCTOB = @GNULIB_WCTOB@
+GNULIB_WCTOMB = @GNULIB_WCTOMB@
+GNULIB_WCTRANS = @GNULIB_WCTRANS@
+GNULIB_WCTYPE = @GNULIB_WCTYPE@
+GNULIB_WCWIDTH = @GNULIB_WCWIDTH@
+GNULIB_WMEMCHR = @GNULIB_WMEMCHR@
+GNULIB_WMEMCMP = @GNULIB_WMEMCMP@
+GNULIB_WMEMCPY = @GNULIB_WMEMCPY@
+GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@
+GNULIB_WMEMSET = @GNULIB_WMEMSET@
+GNULIB_WRITE = @GNULIB_WRITE@
+GNULIB__EXIT = @GNULIB__EXIT@
+GREP = @GREP@
+HAVE_ALPHASORT = @HAVE_ALPHASORT@
+HAVE_ASPRINTF = @HAVE_ASPRINTF@
+HAVE_ATOLL = @HAVE_ATOLL@
+HAVE_BTOWC = @HAVE_BTOWC@
+HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@
+HAVE_CHOWN = @HAVE_CHOWN@
+HAVE_CLIX = @HAVE_CLIX@
+HAVE_CLIX_IN_PATH = @HAVE_CLIX_IN_PATH@
+HAVE_CLOSEDIR = @HAVE_CLOSEDIR@
+HAVE_CSC = @HAVE_CSC@
+HAVE_CSCC = @HAVE_CSCC@
+HAVE_CSCC_IN_PATH = @HAVE_CSCC_IN_PATH@
+HAVE_CSC_IN_PATH = @HAVE_CSC_IN_PATH@
+HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@
+HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@
+HAVE_DECL_FDOPENDIR = @HAVE_DECL_FDOPENDIR@
+HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@
+HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@
+HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@
+HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@
+HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@
+HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@
+HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@
+HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@
+HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@
+HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@
+HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@
+HAVE_DECL_SETENV = @HAVE_DECL_SETENV@
+HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@
+HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@
+HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@
+HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@
+HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
+HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DIRENT_H = @HAVE_DIRENT_H@
+HAVE_DPRINTF = @HAVE_DPRINTF@
+HAVE_DUP2 = @HAVE_DUP2@
+HAVE_DUP3 = @HAVE_DUP3@
+HAVE_DUPLOCALE = @HAVE_DUPLOCALE@
+HAVE_EUIDACCESS = @HAVE_EUIDACCESS@
+HAVE_FACCESSAT = @HAVE_FACCESSAT@
+HAVE_FCHDIR = @HAVE_FCHDIR@
+HAVE_FCHMODAT = @HAVE_FCHMODAT@
+HAVE_FCHOWNAT = @HAVE_FCHOWNAT@
+HAVE_FCNTL = @HAVE_FCNTL@
+HAVE_FDATASYNC = @HAVE_FDATASYNC@
+HAVE_FDOPENDIR = @HAVE_FDOPENDIR@
+HAVE_FEATURES_H = @HAVE_FEATURES_H@
+HAVE_FFSL = @HAVE_FFSL@
+HAVE_FFSLL = @HAVE_FFSLL@
+HAVE_FSEEKO = @HAVE_FSEEKO@
+HAVE_FSTATAT = @HAVE_FSTATAT@
+HAVE_FSYNC = @HAVE_FSYNC@
+HAVE_FTELLO = @HAVE_FTELLO@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_FUTIMENS = @HAVE_FUTIMENS@
+HAVE_GCJ = @HAVE_GCJ@
+HAVE_GCJ_C = @HAVE_GCJ_C@
+HAVE_GCJ_IN_PATH = @HAVE_GCJ_IN_PATH@
+HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@
+HAVE_GETGROUPS = @HAVE_GETGROUPS@
+HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@
+HAVE_GETLOGIN = @HAVE_GETLOGIN@
+HAVE_GETOPT_H = @HAVE_GETOPT_H@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@
+HAVE_GIJ = @HAVE_GIJ@
+HAVE_GIJ_IN_PATH = @HAVE_GIJ_IN_PATH@
+HAVE_GLOBAL_SYMBOL_PIPE = @HAVE_GLOBAL_SYMBOL_PIPE@
+HAVE_GRANTPT = @HAVE_GRANTPT@
+HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@
+HAVE_ILRUN = @HAVE_ILRUN@
+HAVE_ILRUN_IN_PATH = @HAVE_ILRUN_IN_PATH@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISWBLANK = @HAVE_ISWBLANK@
+HAVE_ISWCNTRL = @HAVE_ISWCNTRL@
+HAVE_JAVA = @HAVE_JAVA@
+HAVE_JAVAC = @HAVE_JAVAC@
+HAVE_JAVAC_ENVVAR = @HAVE_JAVAC_ENVVAR@
+HAVE_JAVAC_IN_PATH = @HAVE_JAVAC_IN_PATH@
+HAVE_JAVA_ENVVAR = @HAVE_JAVA_ENVVAR@
+HAVE_JAVA_IN_PATH = @HAVE_JAVA_IN_PATH@
+HAVE_JIKES = @HAVE_JIKES@
+HAVE_JIKES_IN_PATH = @HAVE_JIKES_IN_PATH@
+HAVE_JRE = @HAVE_JRE@
+HAVE_JRE_IN_PATH = @HAVE_JRE_IN_PATH@
+HAVE_JVIEW = @HAVE_JVIEW@
+HAVE_JVIEW_IN_PATH = @HAVE_JVIEW_IN_PATH@
+HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@
+HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@
+HAVE_LANGINFO_H = @HAVE_LANGINFO_H@
+HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@
+HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@
+HAVE_LCHMOD = @HAVE_LCHMOD@
+HAVE_LCHOWN = @HAVE_LCHOWN@
+HAVE_LIBEXPAT = @HAVE_LIBEXPAT@
+HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@
+HAVE_LINK = @HAVE_LINK@
+HAVE_LINKAT = @HAVE_LINKAT@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MBRLEN = @HAVE_MBRLEN@
+HAVE_MBRTOWC = @HAVE_MBRTOWC@
+HAVE_MBSINIT = @HAVE_MBSINIT@
+HAVE_MBSLEN = @HAVE_MBSLEN@
+HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@
+HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@
+HAVE_MCS = @HAVE_MCS@
+HAVE_MCS_IN_PATH = @HAVE_MCS_IN_PATH@
+HAVE_MEMCHR = @HAVE_MEMCHR@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDIRAT = @HAVE_MKDIRAT@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_MKFIFO = @HAVE_MKFIFO@
+HAVE_MKFIFOAT = @HAVE_MKFIFOAT@
+HAVE_MKNOD = @HAVE_MKNOD@
+HAVE_MKNODAT = @HAVE_MKNODAT@
+HAVE_MKOSTEMP = @HAVE_MKOSTEMP@
+HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@
+HAVE_MKSTEMP = @HAVE_MKSTEMP@
+HAVE_MKSTEMPS = @HAVE_MKSTEMPS@
+HAVE_MONO = @HAVE_MONO@
+HAVE_MONO_IN_PATH = @HAVE_MONO_IN_PATH@
+HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@
+HAVE_NANOSLEEP = @HAVE_NANOSLEEP@
+HAVE_NEWLOCALE = @HAVE_NEWLOCALE@
+HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@
+HAVE_OPENAT = @HAVE_OPENAT@
+HAVE_OPENDIR = @HAVE_OPENDIR@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_PCLOSE = @HAVE_PCLOSE@
+HAVE_PIPE = @HAVE_PIPE@
+HAVE_PIPE2 = @HAVE_PIPE2@
+HAVE_POPEN = @HAVE_POPEN@
+HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@
+HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
+HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@
+HAVE_POSIX_SPAWN = @HAVE_POSIX_SPAWN@
+HAVE_POSIX_SPAWNATTR_T = @HAVE_POSIX_SPAWNATTR_T@
+HAVE_POSIX_SPAWN_FILE_ACTIONS_T = @HAVE_POSIX_SPAWN_FILE_ACTIONS_T@
+HAVE_PREAD = @HAVE_PREAD@
+HAVE_PSELECT = @HAVE_PSELECT@
+HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@
+HAVE_PTSNAME = @HAVE_PTSNAME@
+HAVE_PTSNAME_R = @HAVE_PTSNAME_R@
+HAVE_PWRITE = @HAVE_PWRITE@
+HAVE_RAISE = @HAVE_RAISE@
+HAVE_RANDOM = @HAVE_RANDOM@
+HAVE_RANDOM_H = @HAVE_RANDOM_H@
+HAVE_RANDOM_R = @HAVE_RANDOM_R@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READDIR = @HAVE_READDIR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_READLINKAT = @HAVE_READLINKAT@
+HAVE_REALPATH = @HAVE_REALPATH@
+HAVE_RENAMEAT = @HAVE_RENAMEAT@
+HAVE_REWINDDIR = @HAVE_REWINDDIR@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SCANDIR = @HAVE_SCANDIR@
+HAVE_SCHED_H = @HAVE_SCHED_H@
+HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@
+HAVE_SIGACTION = @HAVE_SIGACTION@
+HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@
+HAVE_SIGINFO_T = @HAVE_SIGINFO_T@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SIGSET_T = @HAVE_SIGSET_T@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_SNPRINTF = @HAVE_SNPRINTF@
+HAVE_SPAWN_H = @HAVE_SPAWN_H@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRPTIME = @HAVE_STRPTIME@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRTOLL = @HAVE_STRTOLL@
+HAVE_STRTOULL = @HAVE_STRTOULL@
+HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@
+HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@
+HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_STRVERSCMP = @HAVE_STRVERSCMP@
+HAVE_SYMLINK = @HAVE_SYMLINK@
+HAVE_SYMLINKAT = @HAVE_SYMLINKAT@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_TIMEGM = @HAVE_TIMEGM@
+HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNLINKAT = @HAVE_UNLINKAT@
+HAVE_UNLOCKPT = @HAVE_UNLOCKPT@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_USLEEP = @HAVE_USLEEP@
+HAVE_UTIMENSAT = @HAVE_UTIMENSAT@
+HAVE_VASPRINTF = @HAVE_VASPRINTF@
+HAVE_VDPRINTF = @HAVE_VDPRINTF@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCHAR_T = @HAVE_WCHAR_T@
+HAVE_WCPCPY = @HAVE_WCPCPY@
+HAVE_WCPNCPY = @HAVE_WCPNCPY@
+HAVE_WCRTOMB = @HAVE_WCRTOMB@
+HAVE_WCSCASECMP = @HAVE_WCSCASECMP@
+HAVE_WCSCAT = @HAVE_WCSCAT@
+HAVE_WCSCHR = @HAVE_WCSCHR@
+HAVE_WCSCMP = @HAVE_WCSCMP@
+HAVE_WCSCOLL = @HAVE_WCSCOLL@
+HAVE_WCSCPY = @HAVE_WCSCPY@
+HAVE_WCSCSPN = @HAVE_WCSCSPN@
+HAVE_WCSDUP = @HAVE_WCSDUP@
+HAVE_WCSLEN = @HAVE_WCSLEN@
+HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@
+HAVE_WCSNCAT = @HAVE_WCSNCAT@
+HAVE_WCSNCMP = @HAVE_WCSNCMP@
+HAVE_WCSNCPY = @HAVE_WCSNCPY@
+HAVE_WCSNLEN = @HAVE_WCSNLEN@
+HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@
+HAVE_WCSPBRK = @HAVE_WCSPBRK@
+HAVE_WCSRCHR = @HAVE_WCSRCHR@
+HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@
+HAVE_WCSSPN = @HAVE_WCSSPN@
+HAVE_WCSSTR = @HAVE_WCSSTR@
+HAVE_WCSTOK = @HAVE_WCSTOK@
+HAVE_WCSWIDTH = @HAVE_WCSWIDTH@
+HAVE_WCSXFRM = @HAVE_WCSXFRM@
+HAVE_WCTRANS_T = @HAVE_WCTRANS_T@
+HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
+HAVE_WCTYPE_T = @HAVE_WCTYPE_T@
+HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE_WMEMCHR = @HAVE_WMEMCHR@
+HAVE_WMEMCMP = @HAVE_WMEMCMP@
+HAVE_WMEMCPY = @HAVE_WMEMCPY@
+HAVE_WMEMMOVE = @HAVE_WMEMMOVE@
+HAVE_WMEMSET = @HAVE_WMEMSET@
+HAVE_WPRINTF = @HAVE_WPRINTF@
+HAVE_XLOCALE_H = @HAVE_XLOCALE_H@
+HAVE__BOOL = @HAVE__BOOL@
+HAVE__EXIT = @HAVE__EXIT@
+ICONV_CONST = @ICONV_CONST@
+ICONV_H = @ICONV_H@
+INCCROCO = @INCCROCO@
+INCGLIB = @INCGLIB@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@
+INCTERMINFO = @INCTERMINFO@
+INCXML = @INCXML@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_PROGRAM_ENV = @INSTALL_PROGRAM_ENV@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
+INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+INTLBISON = @INTLBISON@
+INTLLIBS = @INTLLIBS@
+INTLOBJS = @INTLOBJS@
+INTL_DEFAULT_VERBOSITY = @INTL_DEFAULT_VERBOSITY@
+INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+JAR = @JAR@
+JAVA_CHOICE = @JAVA_CHOICE@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCROCO = @LIBCROCO@
+LIBEXPAT = @LIBEXPAT@
+LIBEXPAT_PREFIX = @LIBEXPAT_PREFIX@
+LIBGLIB = @LIBGLIB@
+LIBGLIB_H = @LIBGLIB_H@
+LIBGREP_LIBDEPS = @LIBGREP_LIBDEPS@
+LIBGREP_LTLIBDEPS = @LIBGREP_LTLIBDEPS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBMULTITHREAD = @LIBMULTITHREAD@
+LIBOBJS = @LIBOBJS@
+LIBPTH = @LIBPTH@
+LIBPTH_PREFIX = @LIBPTH_PREFIX@
+LIBS = @LIBS@
+LIBTERMINFO = @LIBTERMINFO@
+LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@
+LIBTHREAD = @LIBTHREAD@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIBUNISTRING_PREFIX = @LIBUNISTRING_PREFIX@
+LIBUNISTRING_UNICONV_H = @LIBUNISTRING_UNICONV_H@
+LIBUNISTRING_UNILBRK_H = @LIBUNISTRING_UNILBRK_H@
+LIBUNISTRING_UNINAME_H = @LIBUNISTRING_UNINAME_H@
+LIBUNISTRING_UNISTR_H = @LIBUNISTRING_UNISTR_H@
+LIBUNISTRING_UNITYPES_H = @LIBUNISTRING_UNITYPES_H@
+LIBUNISTRING_UNIWIDTH_H = @LIBUNISTRING_UNIWIDTH_H@
+LIBXML = @LIBXML@
+LIBXML_H = @LIBXML_H@
+LIB_ACL = @LIB_ACL@
+LIB_POSIX_SPAWN = @LIB_POSIX_SPAWN@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@
+LOCALE_FR = @LOCALE_FR@
+LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@
+LOCALE_JA = @LOCALE_JA@
+LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@
+LOCALE_ZH_CN = @LOCALE_ZH_CN@
+LTLIBC = @LTLIBC@
+LTLIBCROCO = @LTLIBCROCO@
+LTLIBEXPAT = @LTLIBEXPAT@
+LTLIBGLIB = @LTLIBGLIB@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBMULTITHREAD = @LTLIBMULTITHREAD@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBPTH = @LTLIBPTH@
+LTLIBTERMINFO = @LTLIBTERMINFO@
+LTLIBTHREAD = @LTLIBTHREAD@
+LTLIBUNISTRING = @LTLIBUNISTRING@
+LTLIBXML = @LTLIBXML@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MONO_PATH = @MONO_PATH@
+MONO_PATH_SEPARATOR = @MONO_PATH_SEPARATOR@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+MSGMERGE_LIBM = @MSGMERGE_LIBM@
+NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@
+NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
+NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
+NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@
+NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@
+NEXT_AS_FIRST_DIRECTIVE_ICONV_H = @NEXT_AS_FIRST_DIRECTIVE_ICONV_H@
+NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@
+NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@
+NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@
+NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@
+NEXT_AS_FIRST_DIRECTIVE_SPAWN_H = @NEXT_AS_FIRST_DIRECTIVE_SPAWN_H@
+NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@
+NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@
+NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@
+NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@
+NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@
+NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H@
+NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@
+NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@
+NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@
+NEXT_DIRENT_H = @NEXT_DIRENT_H@
+NEXT_ERRNO_H = @NEXT_ERRNO_H@
+NEXT_FCNTL_H = @NEXT_FCNTL_H@
+NEXT_FLOAT_H = @NEXT_FLOAT_H@
+NEXT_GETOPT_H = @NEXT_GETOPT_H@
+NEXT_ICONV_H = @NEXT_ICONV_H@
+NEXT_INTTYPES_H = @NEXT_INTTYPES_H@
+NEXT_LANGINFO_H = @NEXT_LANGINFO_H@
+NEXT_LOCALE_H = @NEXT_LOCALE_H@
+NEXT_SCHED_H = @NEXT_SCHED_H@
+NEXT_SIGNAL_H = @NEXT_SIGNAL_H@
+NEXT_SPAWN_H = @NEXT_SPAWN_H@
+NEXT_STDARG_H = @NEXT_STDARG_H@
+NEXT_STDDEF_H = @NEXT_STDDEF_H@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDIO_H = @NEXT_STDIO_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@
+NEXT_SYS_WAIT_H = @NEXT_SYS_WAIT_H@
+NEXT_TIME_H = @NEXT_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NEXT_WCTYPE_H = @NEXT_WCTYPE_H@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NO_CXX = @NO_CXX@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENMP_CFLAGS = @OPENMP_CFLAGS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+POSUB = @POSUB@
+PRAGMA_COLUMNS = @PRAGMA_COLUMNS@
+PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@
+PRIPTR_PREFIX = @PRIPTR_PREFIX@
+PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@
+PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+RC = @RC@
+RELOCATABLE = @RELOCATABLE@
+RELOCATABLE_BUILD_DIR = ../gnulib-lib
+RELOCATABLE_CONFIG_H_DIR = ..
+RELOCATABLE_LDFLAGS = @RELOCATABLE_LDFLAGS@
+
+# Support for relocatability.
+RELOCATABLE_LIBRARY_PATH = $(libdir)
+RELOCATABLE_SRC_DIR = $(top_srcdir)/gnulib-lib
+RELOCATABLE_STRIP = :
+REPLACE_BTOWC = @REPLACE_BTOWC@
+REPLACE_CALLOC = @REPLACE_CALLOC@
+REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_CLOSE = @REPLACE_CLOSE@
+REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@
+REPLACE_DIRFD = @REPLACE_DIRFD@
+REPLACE_DPRINTF = @REPLACE_DPRINTF@
+REPLACE_DUP = @REPLACE_DUP@
+REPLACE_DUP2 = @REPLACE_DUP2@
+REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@
+REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@
+REPLACE_FCLOSE = @REPLACE_FCLOSE@
+REPLACE_FCNTL = @REPLACE_FCNTL@
+REPLACE_FDOPEN = @REPLACE_FDOPEN@
+REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@
+REPLACE_FFLUSH = @REPLACE_FFLUSH@
+REPLACE_FOPEN = @REPLACE_FOPEN@
+REPLACE_FPRINTF = @REPLACE_FPRINTF@
+REPLACE_FPURGE = @REPLACE_FPURGE@
+REPLACE_FREOPEN = @REPLACE_FREOPEN@
+REPLACE_FSEEK = @REPLACE_FSEEK@
+REPLACE_FSEEKO = @REPLACE_FSEEKO@
+REPLACE_FSTAT = @REPLACE_FSTAT@
+REPLACE_FSTATAT = @REPLACE_FSTATAT@
+REPLACE_FTELL = @REPLACE_FTELL@
+REPLACE_FTELLO = @REPLACE_FTELLO@
+REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@
+REPLACE_FUTIMENS = @REPLACE_FUTIMENS@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETDELIM = @REPLACE_GETDELIM@
+REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@
+REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@
+REPLACE_GETGROUPS = @REPLACE_GETGROUPS@
+REPLACE_GETLINE = @REPLACE_GETLINE@
+REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_GMTIME = @REPLACE_GMTIME@
+REPLACE_ICONV = @REPLACE_ICONV@
+REPLACE_ICONV_OPEN = @REPLACE_ICONV_OPEN@
+REPLACE_ICONV_UTF = @REPLACE_ICONV_UTF@
+REPLACE_ISATTY = @REPLACE_ISATTY@
+REPLACE_ISWBLANK = @REPLACE_ISWBLANK@
+REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@
+REPLACE_ITOLD = @REPLACE_ITOLD@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LINK = @REPLACE_LINK@
+REPLACE_LINKAT = @REPLACE_LINKAT@
+REPLACE_LOCALECONV = @REPLACE_LOCALECONV@
+REPLACE_LOCALTIME = @REPLACE_LOCALTIME@
+REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_LSTAT = @REPLACE_LSTAT@
+REPLACE_MALLOC = @REPLACE_MALLOC@
+REPLACE_MBRLEN = @REPLACE_MBRLEN@
+REPLACE_MBRTOWC = @REPLACE_MBRTOWC@
+REPLACE_MBSINIT = @REPLACE_MBSINIT@
+REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@
+REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@
+REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@
+REPLACE_MBTOWC = @REPLACE_MBTOWC@
+REPLACE_MEMCHR = @REPLACE_MEMCHR@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKFIFO = @REPLACE_MKFIFO@
+REPLACE_MKNOD = @REPLACE_MKNOD@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_MKTIME = @REPLACE_MKTIME@
+REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
+REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@
+REPLACE_NULL = @REPLACE_NULL@
+REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@
+REPLACE_OPEN = @REPLACE_OPEN@
+REPLACE_OPENAT = @REPLACE_OPENAT@
+REPLACE_OPENDIR = @REPLACE_OPENDIR@
+REPLACE_PERROR = @REPLACE_PERROR@
+REPLACE_POPEN = @REPLACE_POPEN@
+REPLACE_POSIX_SPAWN = @REPLACE_POSIX_SPAWN@
+REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE = @REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE@
+REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2 = @REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2@
+REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN = @REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN@
+REPLACE_PREAD = @REPLACE_PREAD@
+REPLACE_PRINTF = @REPLACE_PRINTF@
+REPLACE_PSELECT = @REPLACE_PSELECT@
+REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@
+REPLACE_PTSNAME = @REPLACE_PTSNAME@
+REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_PWRITE = @REPLACE_PWRITE@
+REPLACE_QSORT_R = @REPLACE_QSORT_R@
+REPLACE_RAISE = @REPLACE_RAISE@
+REPLACE_RANDOM_R = @REPLACE_RANDOM_R@
+REPLACE_READ = @REPLACE_READ@
+REPLACE_READLINK = @REPLACE_READLINK@
+REPLACE_READLINKAT = @REPLACE_READLINKAT@
+REPLACE_REALLOC = @REPLACE_REALLOC@
+REPLACE_REALPATH = @REPLACE_REALPATH@
+REPLACE_REMOVE = @REPLACE_REMOVE@
+REPLACE_RENAME = @REPLACE_RENAME@
+REPLACE_RENAMEAT = @REPLACE_RENAMEAT@
+REPLACE_RMDIR = @REPLACE_RMDIR@
+REPLACE_SELECT = @REPLACE_SELECT@
+REPLACE_SETENV = @REPLACE_SETENV@
+REPLACE_SETLOCALE = @REPLACE_SETLOCALE@
+REPLACE_SLEEP = @REPLACE_SLEEP@
+REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
+REPLACE_SPRINTF = @REPLACE_SPRINTF@
+REPLACE_STAT = @REPLACE_STAT@
+REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@
+REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@
+REPLACE_STPNCPY = @REPLACE_STPNCPY@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@
+REPLACE_STRDUP = @REPLACE_STRDUP@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRERROR_R = @REPLACE_STRERROR_R@
+REPLACE_STRNCAT = @REPLACE_STRNCAT@
+REPLACE_STRNDUP = @REPLACE_STRNDUP@
+REPLACE_STRNLEN = @REPLACE_STRNLEN@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@
+REPLACE_STRTOK_R = @REPLACE_STRTOK_R@
+REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@
+REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@
+REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@
+REPLACE_SYMLINK = @REPLACE_SYMLINK@
+REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@
+REPLACE_TIMEGM = @REPLACE_TIMEGM@
+REPLACE_TMPFILE = @REPLACE_TMPFILE@
+REPLACE_TOWLOWER = @REPLACE_TOWLOWER@
+REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@
+REPLACE_UNLINK = @REPLACE_UNLINK@
+REPLACE_UNLINKAT = @REPLACE_UNLINKAT@
+REPLACE_UNSETENV = @REPLACE_UNSETENV@
+REPLACE_USLEEP = @REPLACE_USLEEP@
+REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@
+REPLACE_VASPRINTF = @REPLACE_VASPRINTF@
+REPLACE_VDPRINTF = @REPLACE_VDPRINTF@
+REPLACE_VFPRINTF = @REPLACE_VFPRINTF@
+REPLACE_VPRINTF = @REPLACE_VPRINTF@
+REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@
+REPLACE_VSPRINTF = @REPLACE_VSPRINTF@
+REPLACE_WCRTOMB = @REPLACE_WCRTOMB@
+REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@
+REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@
+REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@
+REPLACE_WCTOB = @REPLACE_WCTOB@
+REPLACE_WCTOMB = @REPLACE_WCTOMB@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+REPLACE_WRITE = @REPLACE_WRITE@
+SCHED_H = @SCHED_H@
+SED = sed
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDARG_H = @STDARG_H@
+STDBOOL_H = @STDBOOL_H@
+STDDEF_H = @STDDEF_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
+TESTCSHARP = @TESTCSHARP@
+TESTJAVA = @TESTJAVA@
+TESTLIBASPRINTF = @TESTLIBASPRINTF@
+TEXI2PDF = @TEXI2PDF@
+TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
+UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@
+UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@
+UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@
+UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@
+UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+USE_ACL = @USE_ACL@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@
+WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@
+WINDRES = @WINDRES@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+WOE32 = @WOE32@
+WOE32DLL = @WOE32DLL@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+YACC = @YACC@ -d
+YFLAGS = @YFLAGS@
+YIELD_LIB = @YIELD_LIB@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+aclocaldir = @aclocaldir@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gl_LIBOBJS = @gl_LIBOBJS@
+gl_LTLIBOBJS = @gl_LTLIBOBJS@
+gltests_LIBOBJS = @gltests_LIBOBJS@
+gltests_LTLIBOBJS = @gltests_LTLIBOBJS@
+gltests_WITNESS = @gltests_WITNESS@
+grgl_LIBOBJS = @grgl_LIBOBJS@
+grgl_LTLIBOBJS = @grgl_LTLIBOBJS@
+grgltests_LIBOBJS = @grgltests_LIBOBJS@
+grgltests_LTLIBOBJS = @grgltests_LTLIBOBJS@
+grgltests_WITNESS = @grgltests_WITNESS@
+gtpo_LIBOBJS = @gtpo_LIBOBJS@
+gtpo_LTLIBOBJS = @gtpo_LTLIBOBJS@
+gtpotests_LIBOBJS = @gtpotests_LIBOBJS@
+gtpotests_LTLIBOBJS = @gtpotests_LTLIBOBJS@
+gtpotests_WITNESS = @gtpotests_WITNESS@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+lispdir = @lispdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = 1.5 gnits no-dependencies subdir-objects
+
+# Special rules for C# auxiliary programs.
+
+# Special rules for Tcl auxiliary program.
+EXTRA_DIST = FILES project-id ChangeLog.0 \
+ gnu/gettext/DumpResource.java gnu/gettext/GetURL.java \
+ msgfmt.cs msgunfmt.cs msgunfmt.tcl
+MOSTLYCLEANFILES = core *.stackdump $(am__append_4)
+CLEANFILES = gnu.gettext.DumpResource$(EXEEXT) \
+ gnu.gettext.GetURL$(EXEEXT) gettext.jar gnu/gettext/*.class \
+ msgfmt.net.exe msgfmt.net.exe.mdb msgunfmt.net.exe \
+ msgunfmt.net.exe.mdb
+DISTCLEANFILES = user-email
+RM = rm -f
+lib_LTLIBRARIES = libgettextsrc.la
+noinst_HEADERS = pos.h message.h po-error.h po-xerror.h po-gram.h po-charset.h \
+po-lex.h open-catalog.h read-catalog-abstract.h read-catalog.h \
+read-po.h read-properties.h read-stringtable.h \
+str-list.h \
+color.h write-catalog.h write-po.h write-properties.h write-stringtable.h \
+dir-list.h file-list.h po-gram-gen.h po-gram-gen2.h \
+msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-cat.h msgl-header.h \
+msgl-english.h msgl-check.h msgl-fsearch.h msgfmt.h msgunfmt.h \
+plural-count.h plural-eval.h plural-distrib.h \
+read-mo.h write-mo.h \
+read-java.h write-java.h \
+read-csharp.h write-csharp.h \
+read-resources.h write-resources.h \
+read-tcl.h write-tcl.h \
+write-qt.h \
+read-desktop.h write-desktop.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 \
+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 libexpat-compat.h
+
+aliaspath = $(localedir)
+jardir = $(datadir)/gettext
+projectsdir = $(pkgdatadir)/projects
+AM_CPPFLAGS = \
+ -I. -I$(srcdir) \
+ -I.. -I$(top_srcdir) \
+ -I$(top_srcdir)/libgrep \
+ -I../gnulib-lib -I$(top_srcdir)/gnulib-lib \
+ -I../intl -I$(top_srcdir)/../gettext-runtime/intl
+
+# Ensure that <stdint.h> defines SIZE_MAX in C++ mode, like it does in C mode.
+AM_CXXFLAGS = -D__STDC_LIMIT_MACROS
+LDADD = ../gnulib-lib/libgettextlib.la $(LTLIBUNISTRING) @LTLIBINTL@ $(WOE32_LDADD)
+OTHERPROGDEPENDENCIES = ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+JAVACOMP = $(SHELL) ../javacomp.sh
+CSHARPCOMP = $(SHELL) ../csharpcomp.sh
+
+# All programs deal with message lists.
+# All programs must read PO files. (msgunfmt also, for read-java.c,
+# read-csharp.c and read-resources.c.)
+# message.c -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> open-catalog.c -> dir-list.c -> str-list.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> po-charset.c.
+# (read-catalog-abstract.c <--> po-gram-gen.y <--> po-lex.c) -> message.c -> str-list.c.
+COMMON_SOURCE = message.c po-error.c po-xerror.c \
+read-catalog-abstract.c po-lex.c po-gram-gen.y po-charset.c \
+read-po.c read-properties.c read-stringtable.c open-catalog.c \
+dir-list.c str-list.c
+
+
+# xgettext and msgfmt deal with format strings.
+@WOE32DLL_FALSE@FORMAT_SOURCE = format.c format-invalid.h format-c.c \
+@WOE32DLL_FALSE@ format-c-parse.h format-sh.c format-python.c \
+@WOE32DLL_FALSE@ format-python-brace.c format-lisp.c \
+@WOE32DLL_FALSE@ format-elisp.c format-librep.c format-scheme.c \
+@WOE32DLL_FALSE@ format-java.c format-csharp.c format-awk.c \
+@WOE32DLL_FALSE@ format-pascal.c format-ycp.c format-tcl.c \
+@WOE32DLL_FALSE@ format-perl.c format-perl-brace.c format-php.c \
+@WOE32DLL_FALSE@ format-gcc-internal.c format-gfc-internal.c \
+@WOE32DLL_FALSE@ format-qt.c format-qt-plural.c format-kde.c \
+@WOE32DLL_FALSE@ format-boost.c format-lua.c \
+@WOE32DLL_FALSE@ format-javascript.c
+@WOE32DLL_TRUE@FORMAT_SOURCE = ../woe32dll/c++format.cc \
+@WOE32DLL_TRUE@ format-invalid.h format-c.c format-c-parse.h \
+@WOE32DLL_TRUE@ format-sh.c format-python.c \
+@WOE32DLL_TRUE@ format-python-brace.c format-lisp.c \
+@WOE32DLL_TRUE@ format-elisp.c format-librep.c format-scheme.c \
+@WOE32DLL_TRUE@ format-java.c format-csharp.c format-awk.c \
+@WOE32DLL_TRUE@ format-pascal.c format-ycp.c format-tcl.c \
+@WOE32DLL_TRUE@ format-perl.c format-perl-brace.c format-php.c \
+@WOE32DLL_TRUE@ format-gcc-internal.c format-gfc-internal.c \
+@WOE32DLL_TRUE@ format-qt.c format-qt-plural.c format-kde.c \
+@WOE32DLL_TRUE@ format-boost.c format-lua.c format-javascript.c
+
+# libgettextsrc contains all code that is needed by at least two programs.
+libgettextsrc_la_SOURCES = $(COMMON_SOURCE) read-catalog.c color.c \
+ write-catalog.c write-properties.c write-stringtable.c \
+ write-po.c msgl-ascii.c msgl-iconv.c msgl-equal.c msgl-cat.c \
+ msgl-header.c msgl-english.c msgl-check.c file-list.c \
+ msgl-charset.c po-time.c plural-exp.c plural-eval.c \
+ plural-table.c $(FORMAT_SOURCE) read-desktop.c $(am__append_1)
+
+# msggrep needs pattern matching.
+LIBGREP = ../libgrep/libgrep.a
+
+# Source dependencies.
+msgcmp_SOURCES = msgcmp.c msgl-fsearch.c
+msgfmt_SOURCES = msgfmt.c write-mo.c write-java.c write-csharp.c \
+ write-resources.c write-tcl.c write-qt.c write-desktop.c \
+ ../../gettext-runtime/intl/hash-string.c
+@WOE32DLL_FALSE@msgmerge_SOURCES = msgmerge.c msgl-fsearch.c \
+@WOE32DLL_FALSE@ lang-table.c plural-count.c
+@WOE32DLL_TRUE@msgmerge_SOURCES = ../woe32dll/c++msgmerge.cc \
+@WOE32DLL_TRUE@ msgl-fsearch.c lang-table.c plural-count.c
+msgunfmt_SOURCES = msgunfmt.c read-mo.c read-java.c read-csharp.c \
+ read-resources.c read-tcl.c
+@WOE32DLL_FALSE@xgettext_SOURCES = xgettext.c x-c.c x-po.c x-sh.c \
+@WOE32DLL_FALSE@ x-python.c x-lisp.c x-elisp.c x-librep.c \
+@WOE32DLL_FALSE@ x-scheme.c x-smalltalk.c x-java.c x-csharp.c \
+@WOE32DLL_FALSE@ x-awk.c x-ycp.c x-tcl.c x-perl.c x-php.c \
+@WOE32DLL_FALSE@ x-rst.c x-glade.c x-lua.c x-javascript.c \
+@WOE32DLL_FALSE@ x-vala.c x-gsettings.c libexpat-compat.c \
+@WOE32DLL_FALSE@ x-desktop.c
+@WOE32DLL_TRUE@xgettext_SOURCES = ../woe32dll/c++xgettext.cc x-c.c \
+@WOE32DLL_TRUE@ x-po.c x-sh.c x-python.c x-lisp.c x-elisp.c \
+@WOE32DLL_TRUE@ x-librep.c x-scheme.c x-smalltalk.c x-java.c \
+@WOE32DLL_TRUE@ x-csharp.c x-awk.c x-ycp.c x-tcl.c x-perl.c \
+@WOE32DLL_TRUE@ x-php.c x-rst.c x-glade.c x-lua.c \
+@WOE32DLL_TRUE@ x-javascript.c x-vala.c x-gsettings.c \
+@WOE32DLL_TRUE@ libexpat-compat.c x-desktop.c
+@WOE32DLL_FALSE@msgattrib_SOURCES = msgattrib.c
+@WOE32DLL_TRUE@msgattrib_SOURCES = ../woe32dll/c++msgattrib.cc
+@WOE32DLL_FALSE@msgcat_SOURCES = msgcat.c
+@WOE32DLL_TRUE@msgcat_SOURCES = ../woe32dll/c++msgcat.cc
+@WOE32DLL_FALSE@msgcomm_SOURCES = msgcomm.c
+@WOE32DLL_TRUE@msgcomm_SOURCES = ../woe32dll/c++msgcomm.cc
+@WOE32DLL_FALSE@msgconv_SOURCES = msgconv.c
+@WOE32DLL_TRUE@msgconv_SOURCES = ../woe32dll/c++msgconv.cc
+@WOE32DLL_FALSE@msgen_SOURCES = msgen.c
+@WOE32DLL_TRUE@msgen_SOURCES = ../woe32dll/c++msgen.cc
+msgexec_SOURCES = msgexec.c
+@WOE32DLL_FALSE@msgfilter_SOURCES = msgfilter.c filter-sr-latin.c \
+@WOE32DLL_FALSE@ filter-quote.c
+@WOE32DLL_TRUE@msgfilter_SOURCES = ../woe32dll/c++msgfilter.cc \
+@WOE32DLL_TRUE@ filter-sr-latin.c filter-quote.c
+@WOE32DLL_FALSE@msggrep_SOURCES = msggrep.c
+@WOE32DLL_TRUE@msggrep_SOURCES = ../woe32dll/c++msggrep.cc
+# This is needed because on Solaris, localealias.c requires the symbol
+# libintl_thread_in_use which is defined in lock.c. The copy of lock.c inside
+# libintl.so is not sufficient, because libintl.so doesn't export the symbol
+# libintl_thread_in_use.
+msginit_SOURCES = msginit.c lang-table.c plural-count.c \
+ ../../gettext-runtime/intl/localealias.c \
+ ../../gettext-runtime/intl/lock.c
+@WOE32DLL_FALSE@msguniq_SOURCES = msguniq.c
+@WOE32DLL_TRUE@msguniq_SOURCES = ../woe32dll/c++msguniq.cc
+recode_sr_latin_SOURCES = recode-sr-latin.c filter-sr-latin.c
+hostname_SOURCES = hostname.c
+urlget_SOURCES = urlget.c
+
+# How to build libgettextsrc.la.
+# Need ../gnulib-lib/libgettextlib.la.
+# Need $(LTLIBUNISTRING) because ulc_width_linebreaks, uc_width, etc. may be
+# taken from libunistring, when the configure option --with-libunistring-prefix
+# was given.
+# Need @LTLIBINTL@ because many source files use gettext().
+# Need @LTLIBICONV@ because po-charset.c, po-lex.c, msgl-iconv.c, write-po.c
+# use iconv().
+libgettextsrc_la_LDFLAGS = -release @VERSION@ \
+ ../gnulib-lib/libgettextlib.la $(LTLIBUNISTRING) @LTLIBINTL@ \
+ @LTLIBICONV@ -lc -no-undefined $(am__append_2)
+libgettextsrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_3)
+
+# Compile-time flags for particular source files.
+msgmerge_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS)
+msgmerge_CXXFLAGS = $(AM_CXXFLAGS) $(OPENMP_CFLAGS)
+
+# Link dependencies.
+# INTL_MACOSX_LIBS is needed because the programs depend on libintl.la
+# but libtool doesn't put -Wl,-framework options into .la files.
+# For msginit, it is also needed because of localename.c.
+msgcmp_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@ $(WOE32_LDADD)
+msgfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgmerge_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@ $(WOE32_LDADD) $(OPENMP_CFLAGS)
+msgunfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+xgettext_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @LTLIBEXPAT@ @LTLIBICONV@ $(WOE32_LDADD)
+msgattrib_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgcat_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgcomm_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgconv_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgen_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgexec_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msgfilter_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msggrep_LDADD = $(LIBGREP) libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msginit_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+msguniq_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ $(WOE32_LDADD)
+
+# Specify when to relink the programs.
+msgcmp_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgfmt_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgmerge_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgunfmt_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+xgettext_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgattrib_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgcat_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgcomm_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgconv_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgen_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgexec_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msgfilter_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msggrep_DEPENDENCIES = $(LIBGREP) libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msginit_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+msguniq_DEPENDENCIES = libgettextsrc.la ../gnulib-lib/libgettextlib.la $(WOE32_LDADD)
+recode_sr_latin_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+hostname_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+urlget_DEPENDENCIES = $(OTHERPROGDEPENDENCIES)
+
+# Specify installation directory, for --enable-relocatable.
+msgcmp_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgfmt_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgmerge_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgunfmt_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+xgettext_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgattrib_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgcat_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgcomm_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgconv_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgen_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgexec_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msgfilter_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msggrep_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msginit_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+msguniq_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+recode_sr_latin_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
+hostname_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(pkglibdir)\"
+urlget_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(pkglibdir)\"
+@RELOCATABLE_VIA_LD_TRUE@msgcmp_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgfmt_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgmerge_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgunfmt_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@xgettext_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgattrib_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgcat_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgcomm_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgconv_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgen_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgexec_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msgfilter_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msggrep_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msginit_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@msguniq_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@recode_sr_latin_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
+@RELOCATABLE_VIA_LD_TRUE@hostname_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(pkglibdir)`
+@RELOCATABLE_VIA_LD_TRUE@urlget_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(pkglibdir)`
+
+# Linking with C++ libraries is needed _only_ on mingw and Cygwin.
+@WOE32DLL_FALSE@libgettextsrc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(libgettextsrc_la_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@libgettextsrc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(libgettextsrc_la_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgattrib_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgattrib_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgattrib_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgattrib_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgcat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgcat_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgcat_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgcat_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgcomm_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgcomm_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgcomm_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgcomm_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgconv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgconv_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgconv_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgconv_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgen_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgen_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgfilter_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msgfilter_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgfilter_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msgfilter_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msggrep_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msggrep_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msggrep_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msggrep_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msgmerge_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(msgmerge_CFLAGS) $(CFLAGS) $(msgmerge_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msgmerge_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(msgmerge_CXXFLAGS) $(CXXFLAGS) $(msgmerge_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@msguniq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(msguniq_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@msguniq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(msguniq_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+@WOE32DLL_FALSE@xgettext_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+@WOE32DLL_FALSE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_FALSE@ $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(xgettext_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_FALSE@ -o $@
+
+@WOE32DLL_TRUE@xgettext_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+@WOE32DLL_TRUE@ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+@WOE32DLL_TRUE@ $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(xgettext_LDFLAGS) $(LDFLAGS) \
+@WOE32DLL_TRUE@ -o $@
+
+
+# Special rules for bison and flex generated files.
+BUILT_SOURCES = \
+ po-gram-gen.c po-gram-gen.h po-gram-gen2.h
+
+
+# Special rules for Java compilation.
+USEJAVA = $(USEJAVA_@BUILDJAVAEXE@)
+USEJAVA_yes = 1
+USEJAVA_no = $(USEJAVA_no_@BUILDJAVA@)
+USEJAVA_no_yes = 1
+USEJAVA_no_no = 0
+USEJEXE = $(USEJEXE_@BUILDJAVAEXE@)
+USEJEXE_yes = 1
+USEJEXE_no = 0
+@WOE32_FALSE@WOE32_LDADD =
+
+# Version information according to Woe32 conventions.
+@WOE32_TRUE@WOE32_LDADD = gettext.res
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .lo .o .obj .y
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnits src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+user-email: $(top_builddir)/config.status $(srcdir)/user-email.sh.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+../woe32dll/$(am__dirstamp):
+ @$(MKDIR_P) ../woe32dll
+ @: > ../woe32dll/$(am__dirstamp)
+../woe32dll/libgettextsrc_la-c++format.lo: \
+ ../woe32dll/$(am__dirstamp)
+../woe32dll/libgettextsrc_la-gettextsrc-exports.lo: \
+ ../woe32dll/$(am__dirstamp)
+
+libgettextsrc.la: $(libgettextsrc_la_OBJECTS) $(libgettextsrc_la_DEPENDENCIES) $(EXTRA_libgettextsrc_la_DEPENDENCIES)
+ $(AM_V_GEN)$(libgettextsrc_la_LINK) -rpath $(libdir) $(libgettextsrc_la_OBJECTS) $(libgettextsrc_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+installcheck-binPROGRAMS: $(bin_PROGRAMS)
+ bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
+ case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+ *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+ esac; \
+ f=`echo "$$p" | \
+ sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ for opt in --help --version; do \
+ if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \
+ 2>c$${pid}_.err </dev/null \
+ && test -n "`cat c$${pid}_.out`" \
+ && test -z "`cat c$${pid}_.err`"; then :; \
+ else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+ done; \
+ done; rm -f c$${pid}_.???; exit $$bad
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+hostname$(EXEEXT): $(hostname_OBJECTS) $(hostname_DEPENDENCIES) $(EXTRA_hostname_DEPENDENCIES)
+ @rm -f hostname$(EXEEXT)
+ $(AM_V_CCLD)$(hostname_LINK) $(hostname_OBJECTS) $(hostname_LDADD) $(LIBS)
+../woe32dll/msgattrib-c++msgattrib.$(OBJEXT): \
+ ../woe32dll/$(am__dirstamp)
+
+msgattrib$(EXEEXT): $(msgattrib_OBJECTS) $(msgattrib_DEPENDENCIES) $(EXTRA_msgattrib_DEPENDENCIES)
+ @rm -f msgattrib$(EXEEXT)
+ $(AM_V_GEN)$(msgattrib_LINK) $(msgattrib_OBJECTS) $(msgattrib_LDADD) $(LIBS)
+../woe32dll/msgcat-c++msgcat.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msgcat$(EXEEXT): $(msgcat_OBJECTS) $(msgcat_DEPENDENCIES) $(EXTRA_msgcat_DEPENDENCIES)
+ @rm -f msgcat$(EXEEXT)
+ $(AM_V_GEN)$(msgcat_LINK) $(msgcat_OBJECTS) $(msgcat_LDADD) $(LIBS)
+
+msgcmp$(EXEEXT): $(msgcmp_OBJECTS) $(msgcmp_DEPENDENCIES) $(EXTRA_msgcmp_DEPENDENCIES)
+ @rm -f msgcmp$(EXEEXT)
+ $(AM_V_CCLD)$(msgcmp_LINK) $(msgcmp_OBJECTS) $(msgcmp_LDADD) $(LIBS)
+../woe32dll/msgcomm-c++msgcomm.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msgcomm$(EXEEXT): $(msgcomm_OBJECTS) $(msgcomm_DEPENDENCIES) $(EXTRA_msgcomm_DEPENDENCIES)
+ @rm -f msgcomm$(EXEEXT)
+ $(AM_V_GEN)$(msgcomm_LINK) $(msgcomm_OBJECTS) $(msgcomm_LDADD) $(LIBS)
+../woe32dll/msgconv-c++msgconv.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msgconv$(EXEEXT): $(msgconv_OBJECTS) $(msgconv_DEPENDENCIES) $(EXTRA_msgconv_DEPENDENCIES)
+ @rm -f msgconv$(EXEEXT)
+ $(AM_V_GEN)$(msgconv_LINK) $(msgconv_OBJECTS) $(msgconv_LDADD) $(LIBS)
+../woe32dll/msgen-c++msgen.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msgen$(EXEEXT): $(msgen_OBJECTS) $(msgen_DEPENDENCIES) $(EXTRA_msgen_DEPENDENCIES)
+ @rm -f msgen$(EXEEXT)
+ $(AM_V_GEN)$(msgen_LINK) $(msgen_OBJECTS) $(msgen_LDADD) $(LIBS)
+
+msgexec$(EXEEXT): $(msgexec_OBJECTS) $(msgexec_DEPENDENCIES) $(EXTRA_msgexec_DEPENDENCIES)
+ @rm -f msgexec$(EXEEXT)
+ $(AM_V_CCLD)$(msgexec_LINK) $(msgexec_OBJECTS) $(msgexec_LDADD) $(LIBS)
+../woe32dll/msgfilter-c++msgfilter.$(OBJEXT): \
+ ../woe32dll/$(am__dirstamp)
+
+msgfilter$(EXEEXT): $(msgfilter_OBJECTS) $(msgfilter_DEPENDENCIES) $(EXTRA_msgfilter_DEPENDENCIES)
+ @rm -f msgfilter$(EXEEXT)
+ $(AM_V_GEN)$(msgfilter_LINK) $(msgfilter_OBJECTS) $(msgfilter_LDADD) $(LIBS)
+../../gettext-runtime/intl/$(am__dirstamp):
+ @$(MKDIR_P) ../../gettext-runtime/intl
+ @: > ../../gettext-runtime/intl/$(am__dirstamp)
+../../gettext-runtime/intl/msgfmt-hash-string.$(OBJEXT): \
+ ../../gettext-runtime/intl/$(am__dirstamp)
+
+msgfmt$(EXEEXT): $(msgfmt_OBJECTS) $(msgfmt_DEPENDENCIES) $(EXTRA_msgfmt_DEPENDENCIES)
+ @rm -f msgfmt$(EXEEXT)
+ $(AM_V_CCLD)$(msgfmt_LINK) $(msgfmt_OBJECTS) $(msgfmt_LDADD) $(LIBS)
+../woe32dll/msggrep-c++msggrep.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msggrep$(EXEEXT): $(msggrep_OBJECTS) $(msggrep_DEPENDENCIES) $(EXTRA_msggrep_DEPENDENCIES)
+ @rm -f msggrep$(EXEEXT)
+ $(AM_V_GEN)$(msggrep_LINK) $(msggrep_OBJECTS) $(msggrep_LDADD) $(LIBS)
+../../gettext-runtime/intl/msginit-localealias.$(OBJEXT): \
+ ../../gettext-runtime/intl/$(am__dirstamp)
+../../gettext-runtime/intl/msginit-lock.$(OBJEXT): \
+ ../../gettext-runtime/intl/$(am__dirstamp)
+
+msginit$(EXEEXT): $(msginit_OBJECTS) $(msginit_DEPENDENCIES) $(EXTRA_msginit_DEPENDENCIES)
+ @rm -f msginit$(EXEEXT)
+ $(AM_V_CCLD)$(msginit_LINK) $(msginit_OBJECTS) $(msginit_LDADD) $(LIBS)
+../woe32dll/msgmerge-c++msgmerge.$(OBJEXT): \
+ ../woe32dll/$(am__dirstamp)
+
+msgmerge$(EXEEXT): $(msgmerge_OBJECTS) $(msgmerge_DEPENDENCIES) $(EXTRA_msgmerge_DEPENDENCIES)
+ @rm -f msgmerge$(EXEEXT)
+ $(AM_V_GEN)$(msgmerge_LINK) $(msgmerge_OBJECTS) $(msgmerge_LDADD) $(LIBS)
+
+msgunfmt$(EXEEXT): $(msgunfmt_OBJECTS) $(msgunfmt_DEPENDENCIES) $(EXTRA_msgunfmt_DEPENDENCIES)
+ @rm -f msgunfmt$(EXEEXT)
+ $(AM_V_CCLD)$(msgunfmt_LINK) $(msgunfmt_OBJECTS) $(msgunfmt_LDADD) $(LIBS)
+../woe32dll/msguniq-c++msguniq.$(OBJEXT): ../woe32dll/$(am__dirstamp)
+
+msguniq$(EXEEXT): $(msguniq_OBJECTS) $(msguniq_DEPENDENCIES) $(EXTRA_msguniq_DEPENDENCIES)
+ @rm -f msguniq$(EXEEXT)
+ $(AM_V_GEN)$(msguniq_LINK) $(msguniq_OBJECTS) $(msguniq_LDADD) $(LIBS)
+
+recode-sr-latin$(EXEEXT): $(recode_sr_latin_OBJECTS) $(recode_sr_latin_DEPENDENCIES) $(EXTRA_recode_sr_latin_DEPENDENCIES)
+ @rm -f recode-sr-latin$(EXEEXT)
+ $(AM_V_CCLD)$(recode_sr_latin_LINK) $(recode_sr_latin_OBJECTS) $(recode_sr_latin_LDADD) $(LIBS)
+
+urlget$(EXEEXT): $(urlget_OBJECTS) $(urlget_DEPENDENCIES) $(EXTRA_urlget_DEPENDENCIES)
+ @rm -f urlget$(EXEEXT)
+ $(AM_V_CCLD)$(urlget_LINK) $(urlget_OBJECTS) $(urlget_LDADD) $(LIBS)
+../woe32dll/xgettext-c++xgettext.$(OBJEXT): \
+ ../woe32dll/$(am__dirstamp)
+
+xgettext$(EXEEXT): $(xgettext_OBJECTS) $(xgettext_DEPENDENCIES) $(EXTRA_xgettext_DEPENDENCIES)
+ @rm -f xgettext$(EXEEXT)
+ $(AM_V_GEN)$(xgettext_LINK) $(xgettext_OBJECTS) $(xgettext_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f ../../gettext-runtime/intl/*.$(OBJEXT)
+ -rm -f ../woe32dll/*.$(OBJEXT)
+ -rm -f ../woe32dll/*.lo
+
+distclean-compile:
+ -rm -f *.tab.c
+
+.c.o:
+ $(AM_V_CC)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ $(AM_V_CC)$(LTCOMPILE) -c -o $@ $<
+
+libgettextsrc_la-message.lo: message.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-message.lo `test -f 'message.c' || echo '$(srcdir)/'`message.c
+
+libgettextsrc_la-po-error.lo: po-error.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-error.lo `test -f 'po-error.c' || echo '$(srcdir)/'`po-error.c
+
+libgettextsrc_la-po-xerror.lo: po-xerror.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-xerror.lo `test -f 'po-xerror.c' || echo '$(srcdir)/'`po-xerror.c
+
+libgettextsrc_la-read-catalog-abstract.lo: read-catalog-abstract.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-catalog-abstract.lo `test -f 'read-catalog-abstract.c' || echo '$(srcdir)/'`read-catalog-abstract.c
+
+libgettextsrc_la-po-lex.lo: po-lex.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-lex.lo `test -f 'po-lex.c' || echo '$(srcdir)/'`po-lex.c
+
+libgettextsrc_la-po-gram-gen.lo: po-gram-gen.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-gram-gen.lo `test -f 'po-gram-gen.c' || echo '$(srcdir)/'`po-gram-gen.c
+
+libgettextsrc_la-po-charset.lo: po-charset.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-charset.lo `test -f 'po-charset.c' || echo '$(srcdir)/'`po-charset.c
+
+libgettextsrc_la-read-po.lo: read-po.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-po.lo `test -f 'read-po.c' || echo '$(srcdir)/'`read-po.c
+
+libgettextsrc_la-read-properties.lo: read-properties.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-properties.lo `test -f 'read-properties.c' || echo '$(srcdir)/'`read-properties.c
+
+libgettextsrc_la-read-stringtable.lo: read-stringtable.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-stringtable.lo `test -f 'read-stringtable.c' || echo '$(srcdir)/'`read-stringtable.c
+
+libgettextsrc_la-open-catalog.lo: open-catalog.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-open-catalog.lo `test -f 'open-catalog.c' || echo '$(srcdir)/'`open-catalog.c
+
+libgettextsrc_la-dir-list.lo: dir-list.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-dir-list.lo `test -f 'dir-list.c' || echo '$(srcdir)/'`dir-list.c
+
+libgettextsrc_la-str-list.lo: str-list.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-str-list.lo `test -f 'str-list.c' || echo '$(srcdir)/'`str-list.c
+
+libgettextsrc_la-read-catalog.lo: read-catalog.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-catalog.lo `test -f 'read-catalog.c' || echo '$(srcdir)/'`read-catalog.c
+
+libgettextsrc_la-color.lo: color.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-color.lo `test -f 'color.c' || echo '$(srcdir)/'`color.c
+
+libgettextsrc_la-write-catalog.lo: write-catalog.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-write-catalog.lo `test -f 'write-catalog.c' || echo '$(srcdir)/'`write-catalog.c
+
+libgettextsrc_la-write-properties.lo: write-properties.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-write-properties.lo `test -f 'write-properties.c' || echo '$(srcdir)/'`write-properties.c
+
+libgettextsrc_la-write-stringtable.lo: write-stringtable.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-write-stringtable.lo `test -f 'write-stringtable.c' || echo '$(srcdir)/'`write-stringtable.c
+
+libgettextsrc_la-write-po.lo: write-po.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-write-po.lo `test -f 'write-po.c' || echo '$(srcdir)/'`write-po.c
+
+libgettextsrc_la-msgl-ascii.lo: msgl-ascii.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-ascii.lo `test -f 'msgl-ascii.c' || echo '$(srcdir)/'`msgl-ascii.c
+
+libgettextsrc_la-msgl-iconv.lo: msgl-iconv.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-iconv.lo `test -f 'msgl-iconv.c' || echo '$(srcdir)/'`msgl-iconv.c
+
+libgettextsrc_la-msgl-equal.lo: msgl-equal.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-equal.lo `test -f 'msgl-equal.c' || echo '$(srcdir)/'`msgl-equal.c
+
+libgettextsrc_la-msgl-cat.lo: msgl-cat.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-cat.lo `test -f 'msgl-cat.c' || echo '$(srcdir)/'`msgl-cat.c
+
+libgettextsrc_la-msgl-header.lo: msgl-header.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-header.lo `test -f 'msgl-header.c' || echo '$(srcdir)/'`msgl-header.c
+
+libgettextsrc_la-msgl-english.lo: msgl-english.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-english.lo `test -f 'msgl-english.c' || echo '$(srcdir)/'`msgl-english.c
+
+libgettextsrc_la-msgl-check.lo: msgl-check.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-check.lo `test -f 'msgl-check.c' || echo '$(srcdir)/'`msgl-check.c
+
+libgettextsrc_la-file-list.lo: file-list.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-file-list.lo `test -f 'file-list.c' || echo '$(srcdir)/'`file-list.c
+
+libgettextsrc_la-msgl-charset.lo: msgl-charset.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-msgl-charset.lo `test -f 'msgl-charset.c' || echo '$(srcdir)/'`msgl-charset.c
+
+libgettextsrc_la-po-time.lo: po-time.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-po-time.lo `test -f 'po-time.c' || echo '$(srcdir)/'`po-time.c
+
+libgettextsrc_la-plural-exp.lo: plural-exp.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-plural-exp.lo `test -f 'plural-exp.c' || echo '$(srcdir)/'`plural-exp.c
+
+libgettextsrc_la-plural-eval.lo: plural-eval.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-plural-eval.lo `test -f 'plural-eval.c' || echo '$(srcdir)/'`plural-eval.c
+
+libgettextsrc_la-plural-table.lo: plural-table.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-plural-table.lo `test -f 'plural-table.c' || echo '$(srcdir)/'`plural-table.c
+
+libgettextsrc_la-format.lo: format.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format.lo `test -f 'format.c' || echo '$(srcdir)/'`format.c
+
+libgettextsrc_la-format-c.lo: format-c.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-c.lo `test -f 'format-c.c' || echo '$(srcdir)/'`format-c.c
+
+libgettextsrc_la-format-sh.lo: format-sh.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-sh.lo `test -f 'format-sh.c' || echo '$(srcdir)/'`format-sh.c
+
+libgettextsrc_la-format-python.lo: format-python.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-python.lo `test -f 'format-python.c' || echo '$(srcdir)/'`format-python.c
+
+libgettextsrc_la-format-python-brace.lo: format-python-brace.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-python-brace.lo `test -f 'format-python-brace.c' || echo '$(srcdir)/'`format-python-brace.c
+
+libgettextsrc_la-format-lisp.lo: format-lisp.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-lisp.lo `test -f 'format-lisp.c' || echo '$(srcdir)/'`format-lisp.c
+
+libgettextsrc_la-format-elisp.lo: format-elisp.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-elisp.lo `test -f 'format-elisp.c' || echo '$(srcdir)/'`format-elisp.c
+
+libgettextsrc_la-format-librep.lo: format-librep.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-librep.lo `test -f 'format-librep.c' || echo '$(srcdir)/'`format-librep.c
+
+libgettextsrc_la-format-scheme.lo: format-scheme.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-scheme.lo `test -f 'format-scheme.c' || echo '$(srcdir)/'`format-scheme.c
+
+libgettextsrc_la-format-java.lo: format-java.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-java.lo `test -f 'format-java.c' || echo '$(srcdir)/'`format-java.c
+
+libgettextsrc_la-format-csharp.lo: format-csharp.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-csharp.lo `test -f 'format-csharp.c' || echo '$(srcdir)/'`format-csharp.c
+
+libgettextsrc_la-format-awk.lo: format-awk.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-awk.lo `test -f 'format-awk.c' || echo '$(srcdir)/'`format-awk.c
+
+libgettextsrc_la-format-pascal.lo: format-pascal.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-pascal.lo `test -f 'format-pascal.c' || echo '$(srcdir)/'`format-pascal.c
+
+libgettextsrc_la-format-ycp.lo: format-ycp.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-ycp.lo `test -f 'format-ycp.c' || echo '$(srcdir)/'`format-ycp.c
+
+libgettextsrc_la-format-tcl.lo: format-tcl.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-tcl.lo `test -f 'format-tcl.c' || echo '$(srcdir)/'`format-tcl.c
+
+libgettextsrc_la-format-perl.lo: format-perl.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-perl.lo `test -f 'format-perl.c' || echo '$(srcdir)/'`format-perl.c
+
+libgettextsrc_la-format-perl-brace.lo: format-perl-brace.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-perl-brace.lo `test -f 'format-perl-brace.c' || echo '$(srcdir)/'`format-perl-brace.c
+
+libgettextsrc_la-format-php.lo: format-php.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-php.lo `test -f 'format-php.c' || echo '$(srcdir)/'`format-php.c
+
+libgettextsrc_la-format-gcc-internal.lo: format-gcc-internal.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-gcc-internal.lo `test -f 'format-gcc-internal.c' || echo '$(srcdir)/'`format-gcc-internal.c
+
+libgettextsrc_la-format-gfc-internal.lo: format-gfc-internal.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-gfc-internal.lo `test -f 'format-gfc-internal.c' || echo '$(srcdir)/'`format-gfc-internal.c
+
+libgettextsrc_la-format-qt.lo: format-qt.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-qt.lo `test -f 'format-qt.c' || echo '$(srcdir)/'`format-qt.c
+
+libgettextsrc_la-format-qt-plural.lo: format-qt-plural.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-qt-plural.lo `test -f 'format-qt-plural.c' || echo '$(srcdir)/'`format-qt-plural.c
+
+libgettextsrc_la-format-kde.lo: format-kde.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-kde.lo `test -f 'format-kde.c' || echo '$(srcdir)/'`format-kde.c
+
+libgettextsrc_la-format-boost.lo: format-boost.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-boost.lo `test -f 'format-boost.c' || echo '$(srcdir)/'`format-boost.c
+
+libgettextsrc_la-format-lua.lo: format-lua.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-lua.lo `test -f 'format-lua.c' || echo '$(srcdir)/'`format-lua.c
+
+libgettextsrc_la-format-javascript.lo: format-javascript.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-format-javascript.lo `test -f 'format-javascript.c' || echo '$(srcdir)/'`format-javascript.c
+
+libgettextsrc_la-read-desktop.lo: read-desktop.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libgettextsrc_la-read-desktop.lo `test -f 'read-desktop.c' || echo '$(srcdir)/'`read-desktop.c
+
+../woe32dll/libgettextsrc_la-gettextsrc-exports.lo: ../woe32dll/gettextsrc-exports.c
+ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../woe32dll/libgettextsrc_la-gettextsrc-exports.lo `test -f '../woe32dll/gettextsrc-exports.c' || echo '$(srcdir)/'`../woe32dll/gettextsrc-exports.c
+
+hostname-hostname.o: hostname.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hostname_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname-hostname.o `test -f 'hostname.c' || echo '$(srcdir)/'`hostname.c
+
+hostname-hostname.obj: hostname.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hostname_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname-hostname.obj `if test -f 'hostname.c'; then $(CYGPATH_W) 'hostname.c'; else $(CYGPATH_W) '$(srcdir)/hostname.c'; fi`
+
+msgattrib-msgattrib.o: msgattrib.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgattrib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgattrib-msgattrib.o `test -f 'msgattrib.c' || echo '$(srcdir)/'`msgattrib.c
+
+msgattrib-msgattrib.obj: msgattrib.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgattrib_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgattrib-msgattrib.obj `if test -f 'msgattrib.c'; then $(CYGPATH_W) 'msgattrib.c'; else $(CYGPATH_W) '$(srcdir)/msgattrib.c'; fi`
+
+msgcat-msgcat.o: msgcat.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcat_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcat-msgcat.o `test -f 'msgcat.c' || echo '$(srcdir)/'`msgcat.c
+
+msgcat-msgcat.obj: msgcat.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcat_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcat-msgcat.obj `if test -f 'msgcat.c'; then $(CYGPATH_W) 'msgcat.c'; else $(CYGPATH_W) '$(srcdir)/msgcat.c'; fi`
+
+msgcmp-msgcmp.o: msgcmp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcmp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcmp-msgcmp.o `test -f 'msgcmp.c' || echo '$(srcdir)/'`msgcmp.c
+
+msgcmp-msgcmp.obj: msgcmp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcmp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcmp-msgcmp.obj `if test -f 'msgcmp.c'; then $(CYGPATH_W) 'msgcmp.c'; else $(CYGPATH_W) '$(srcdir)/msgcmp.c'; fi`
+
+msgcmp-msgl-fsearch.o: msgl-fsearch.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcmp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcmp-msgl-fsearch.o `test -f 'msgl-fsearch.c' || echo '$(srcdir)/'`msgl-fsearch.c
+
+msgcmp-msgl-fsearch.obj: msgl-fsearch.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcmp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcmp-msgl-fsearch.obj `if test -f 'msgl-fsearch.c'; then $(CYGPATH_W) 'msgl-fsearch.c'; else $(CYGPATH_W) '$(srcdir)/msgl-fsearch.c'; fi`
+
+msgcomm-msgcomm.o: msgcomm.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcomm_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcomm-msgcomm.o `test -f 'msgcomm.c' || echo '$(srcdir)/'`msgcomm.c
+
+msgcomm-msgcomm.obj: msgcomm.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcomm_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgcomm-msgcomm.obj `if test -f 'msgcomm.c'; then $(CYGPATH_W) 'msgcomm.c'; else $(CYGPATH_W) '$(srcdir)/msgcomm.c'; fi`
+
+msgconv-msgconv.o: msgconv.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgconv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgconv-msgconv.o `test -f 'msgconv.c' || echo '$(srcdir)/'`msgconv.c
+
+msgconv-msgconv.obj: msgconv.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgconv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgconv-msgconv.obj `if test -f 'msgconv.c'; then $(CYGPATH_W) 'msgconv.c'; else $(CYGPATH_W) '$(srcdir)/msgconv.c'; fi`
+
+msgen-msgen.o: msgen.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgen-msgen.o `test -f 'msgen.c' || echo '$(srcdir)/'`msgen.c
+
+msgen-msgen.obj: msgen.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgen-msgen.obj `if test -f 'msgen.c'; then $(CYGPATH_W) 'msgen.c'; else $(CYGPATH_W) '$(srcdir)/msgen.c'; fi`
+
+msgexec-msgexec.o: msgexec.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgexec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgexec-msgexec.o `test -f 'msgexec.c' || echo '$(srcdir)/'`msgexec.c
+
+msgexec-msgexec.obj: msgexec.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgexec_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgexec-msgexec.obj `if test -f 'msgexec.c'; then $(CYGPATH_W) 'msgexec.c'; else $(CYGPATH_W) '$(srcdir)/msgexec.c'; fi`
+
+msgfilter-msgfilter.o: msgfilter.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-msgfilter.o `test -f 'msgfilter.c' || echo '$(srcdir)/'`msgfilter.c
+
+msgfilter-msgfilter.obj: msgfilter.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-msgfilter.obj `if test -f 'msgfilter.c'; then $(CYGPATH_W) 'msgfilter.c'; else $(CYGPATH_W) '$(srcdir)/msgfilter.c'; fi`
+
+msgfilter-filter-sr-latin.o: filter-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-filter-sr-latin.o `test -f 'filter-sr-latin.c' || echo '$(srcdir)/'`filter-sr-latin.c
+
+msgfilter-filter-sr-latin.obj: filter-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-filter-sr-latin.obj `if test -f 'filter-sr-latin.c'; then $(CYGPATH_W) 'filter-sr-latin.c'; else $(CYGPATH_W) '$(srcdir)/filter-sr-latin.c'; fi`
+
+msgfilter-filter-quote.o: filter-quote.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-filter-quote.o `test -f 'filter-quote.c' || echo '$(srcdir)/'`filter-quote.c
+
+msgfilter-filter-quote.obj: filter-quote.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfilter-filter-quote.obj `if test -f 'filter-quote.c'; then $(CYGPATH_W) 'filter-quote.c'; else $(CYGPATH_W) '$(srcdir)/filter-quote.c'; fi`
+
+msgfmt-msgfmt.o: msgfmt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-msgfmt.o `test -f 'msgfmt.c' || echo '$(srcdir)/'`msgfmt.c
+
+msgfmt-msgfmt.obj: msgfmt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-msgfmt.obj `if test -f 'msgfmt.c'; then $(CYGPATH_W) 'msgfmt.c'; else $(CYGPATH_W) '$(srcdir)/msgfmt.c'; fi`
+
+msgfmt-write-mo.o: write-mo.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-mo.o `test -f 'write-mo.c' || echo '$(srcdir)/'`write-mo.c
+
+msgfmt-write-mo.obj: write-mo.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-mo.obj `if test -f 'write-mo.c'; then $(CYGPATH_W) 'write-mo.c'; else $(CYGPATH_W) '$(srcdir)/write-mo.c'; fi`
+
+msgfmt-write-java.o: write-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-java.o `test -f 'write-java.c' || echo '$(srcdir)/'`write-java.c
+
+msgfmt-write-java.obj: write-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-java.obj `if test -f 'write-java.c'; then $(CYGPATH_W) 'write-java.c'; else $(CYGPATH_W) '$(srcdir)/write-java.c'; fi`
+
+msgfmt-write-csharp.o: write-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-csharp.o `test -f 'write-csharp.c' || echo '$(srcdir)/'`write-csharp.c
+
+msgfmt-write-csharp.obj: write-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-csharp.obj `if test -f 'write-csharp.c'; then $(CYGPATH_W) 'write-csharp.c'; else $(CYGPATH_W) '$(srcdir)/write-csharp.c'; fi`
+
+msgfmt-write-resources.o: write-resources.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-resources.o `test -f 'write-resources.c' || echo '$(srcdir)/'`write-resources.c
+
+msgfmt-write-resources.obj: write-resources.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-resources.obj `if test -f 'write-resources.c'; then $(CYGPATH_W) 'write-resources.c'; else $(CYGPATH_W) '$(srcdir)/write-resources.c'; fi`
+
+msgfmt-write-tcl.o: write-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-tcl.o `test -f 'write-tcl.c' || echo '$(srcdir)/'`write-tcl.c
+
+msgfmt-write-tcl.obj: write-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-tcl.obj `if test -f 'write-tcl.c'; then $(CYGPATH_W) 'write-tcl.c'; else $(CYGPATH_W) '$(srcdir)/write-tcl.c'; fi`
+
+msgfmt-write-qt.o: write-qt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-qt.o `test -f 'write-qt.c' || echo '$(srcdir)/'`write-qt.c
+
+msgfmt-write-qt.obj: write-qt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-qt.obj `if test -f 'write-qt.c'; then $(CYGPATH_W) 'write-qt.c'; else $(CYGPATH_W) '$(srcdir)/write-qt.c'; fi`
+
+msgfmt-write-desktop.o: write-desktop.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-desktop.o `test -f 'write-desktop.c' || echo '$(srcdir)/'`write-desktop.c
+
+msgfmt-write-desktop.obj: write-desktop.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgfmt-write-desktop.obj `if test -f 'write-desktop.c'; then $(CYGPATH_W) 'write-desktop.c'; else $(CYGPATH_W) '$(srcdir)/write-desktop.c'; fi`
+
+../../gettext-runtime/intl/msgfmt-hash-string.o: ../../gettext-runtime/intl/hash-string.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msgfmt-hash-string.o `test -f '../../gettext-runtime/intl/hash-string.c' || echo '$(srcdir)/'`../../gettext-runtime/intl/hash-string.c
+
+../../gettext-runtime/intl/msgfmt-hash-string.obj: ../../gettext-runtime/intl/hash-string.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msgfmt-hash-string.obj `if test -f '../../gettext-runtime/intl/hash-string.c'; then $(CYGPATH_W) '../../gettext-runtime/intl/hash-string.c'; else $(CYGPATH_W) '$(srcdir)/../../gettext-runtime/intl/hash-string.c'; fi`
+
+msggrep-msggrep.o: msggrep.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msggrep_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msggrep-msggrep.o `test -f 'msggrep.c' || echo '$(srcdir)/'`msggrep.c
+
+msggrep-msggrep.obj: msggrep.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msggrep_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msggrep-msggrep.obj `if test -f 'msggrep.c'; then $(CYGPATH_W) 'msggrep.c'; else $(CYGPATH_W) '$(srcdir)/msggrep.c'; fi`
+
+msginit-msginit.o: msginit.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-msginit.o `test -f 'msginit.c' || echo '$(srcdir)/'`msginit.c
+
+msginit-msginit.obj: msginit.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-msginit.obj `if test -f 'msginit.c'; then $(CYGPATH_W) 'msginit.c'; else $(CYGPATH_W) '$(srcdir)/msginit.c'; fi`
+
+msginit-lang-table.o: lang-table.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-lang-table.o `test -f 'lang-table.c' || echo '$(srcdir)/'`lang-table.c
+
+msginit-lang-table.obj: lang-table.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-lang-table.obj `if test -f 'lang-table.c'; then $(CYGPATH_W) 'lang-table.c'; else $(CYGPATH_W) '$(srcdir)/lang-table.c'; fi`
+
+msginit-plural-count.o: plural-count.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-plural-count.o `test -f 'plural-count.c' || echo '$(srcdir)/'`plural-count.c
+
+msginit-plural-count.obj: plural-count.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msginit-plural-count.obj `if test -f 'plural-count.c'; then $(CYGPATH_W) 'plural-count.c'; else $(CYGPATH_W) '$(srcdir)/plural-count.c'; fi`
+
+../../gettext-runtime/intl/msginit-localealias.o: ../../gettext-runtime/intl/localealias.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msginit-localealias.o `test -f '../../gettext-runtime/intl/localealias.c' || echo '$(srcdir)/'`../../gettext-runtime/intl/localealias.c
+
+../../gettext-runtime/intl/msginit-localealias.obj: ../../gettext-runtime/intl/localealias.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msginit-localealias.obj `if test -f '../../gettext-runtime/intl/localealias.c'; then $(CYGPATH_W) '../../gettext-runtime/intl/localealias.c'; else $(CYGPATH_W) '$(srcdir)/../../gettext-runtime/intl/localealias.c'; fi`
+
+../../gettext-runtime/intl/msginit-lock.o: ../../gettext-runtime/intl/lock.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msginit-lock.o `test -f '../../gettext-runtime/intl/lock.c' || echo '$(srcdir)/'`../../gettext-runtime/intl/lock.c
+
+../../gettext-runtime/intl/msginit-lock.obj: ../../gettext-runtime/intl/lock.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msginit_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ../../gettext-runtime/intl/msginit-lock.obj `if test -f '../../gettext-runtime/intl/lock.c'; then $(CYGPATH_W) '../../gettext-runtime/intl/lock.c'; else $(CYGPATH_W) '$(srcdir)/../../gettext-runtime/intl/lock.c'; fi`
+
+msgmerge-msgmerge.o: msgmerge.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-msgmerge.o `test -f 'msgmerge.c' || echo '$(srcdir)/'`msgmerge.c
+
+msgmerge-msgmerge.obj: msgmerge.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-msgmerge.obj `if test -f 'msgmerge.c'; then $(CYGPATH_W) 'msgmerge.c'; else $(CYGPATH_W) '$(srcdir)/msgmerge.c'; fi`
+
+msgmerge-msgl-fsearch.o: msgl-fsearch.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-msgl-fsearch.o `test -f 'msgl-fsearch.c' || echo '$(srcdir)/'`msgl-fsearch.c
+
+msgmerge-msgl-fsearch.obj: msgl-fsearch.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-msgl-fsearch.obj `if test -f 'msgl-fsearch.c'; then $(CYGPATH_W) 'msgl-fsearch.c'; else $(CYGPATH_W) '$(srcdir)/msgl-fsearch.c'; fi`
+
+msgmerge-lang-table.o: lang-table.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-lang-table.o `test -f 'lang-table.c' || echo '$(srcdir)/'`lang-table.c
+
+msgmerge-lang-table.obj: lang-table.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-lang-table.obj `if test -f 'lang-table.c'; then $(CYGPATH_W) 'lang-table.c'; else $(CYGPATH_W) '$(srcdir)/lang-table.c'; fi`
+
+msgmerge-plural-count.o: plural-count.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-plural-count.o `test -f 'plural-count.c' || echo '$(srcdir)/'`plural-count.c
+
+msgmerge-plural-count.obj: plural-count.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CFLAGS) $(CFLAGS) -c -o msgmerge-plural-count.obj `if test -f 'plural-count.c'; then $(CYGPATH_W) 'plural-count.c'; else $(CYGPATH_W) '$(srcdir)/plural-count.c'; fi`
+
+msgunfmt-msgunfmt.o: msgunfmt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-msgunfmt.o `test -f 'msgunfmt.c' || echo '$(srcdir)/'`msgunfmt.c
+
+msgunfmt-msgunfmt.obj: msgunfmt.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-msgunfmt.obj `if test -f 'msgunfmt.c'; then $(CYGPATH_W) 'msgunfmt.c'; else $(CYGPATH_W) '$(srcdir)/msgunfmt.c'; fi`
+
+msgunfmt-read-mo.o: read-mo.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-mo.o `test -f 'read-mo.c' || echo '$(srcdir)/'`read-mo.c
+
+msgunfmt-read-mo.obj: read-mo.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-mo.obj `if test -f 'read-mo.c'; then $(CYGPATH_W) 'read-mo.c'; else $(CYGPATH_W) '$(srcdir)/read-mo.c'; fi`
+
+msgunfmt-read-java.o: read-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-java.o `test -f 'read-java.c' || echo '$(srcdir)/'`read-java.c
+
+msgunfmt-read-java.obj: read-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-java.obj `if test -f 'read-java.c'; then $(CYGPATH_W) 'read-java.c'; else $(CYGPATH_W) '$(srcdir)/read-java.c'; fi`
+
+msgunfmt-read-csharp.o: read-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-csharp.o `test -f 'read-csharp.c' || echo '$(srcdir)/'`read-csharp.c
+
+msgunfmt-read-csharp.obj: read-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-csharp.obj `if test -f 'read-csharp.c'; then $(CYGPATH_W) 'read-csharp.c'; else $(CYGPATH_W) '$(srcdir)/read-csharp.c'; fi`
+
+msgunfmt-read-resources.o: read-resources.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-resources.o `test -f 'read-resources.c' || echo '$(srcdir)/'`read-resources.c
+
+msgunfmt-read-resources.obj: read-resources.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-resources.obj `if test -f 'read-resources.c'; then $(CYGPATH_W) 'read-resources.c'; else $(CYGPATH_W) '$(srcdir)/read-resources.c'; fi`
+
+msgunfmt-read-tcl.o: read-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-tcl.o `test -f 'read-tcl.c' || echo '$(srcdir)/'`read-tcl.c
+
+msgunfmt-read-tcl.obj: read-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgunfmt_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msgunfmt-read-tcl.obj `if test -f 'read-tcl.c'; then $(CYGPATH_W) 'read-tcl.c'; else $(CYGPATH_W) '$(srcdir)/read-tcl.c'; fi`
+
+msguniq-msguniq.o: msguniq.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msguniq_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msguniq-msguniq.o `test -f 'msguniq.c' || echo '$(srcdir)/'`msguniq.c
+
+msguniq-msguniq.obj: msguniq.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msguniq_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msguniq-msguniq.obj `if test -f 'msguniq.c'; then $(CYGPATH_W) 'msguniq.c'; else $(CYGPATH_W) '$(srcdir)/msguniq.c'; fi`
+
+recode_sr_latin-recode-sr-latin.o: recode-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(recode_sr_latin_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o recode_sr_latin-recode-sr-latin.o `test -f 'recode-sr-latin.c' || echo '$(srcdir)/'`recode-sr-latin.c
+
+recode_sr_latin-recode-sr-latin.obj: recode-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(recode_sr_latin_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o recode_sr_latin-recode-sr-latin.obj `if test -f 'recode-sr-latin.c'; then $(CYGPATH_W) 'recode-sr-latin.c'; else $(CYGPATH_W) '$(srcdir)/recode-sr-latin.c'; fi`
+
+recode_sr_latin-filter-sr-latin.o: filter-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(recode_sr_latin_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o recode_sr_latin-filter-sr-latin.o `test -f 'filter-sr-latin.c' || echo '$(srcdir)/'`filter-sr-latin.c
+
+recode_sr_latin-filter-sr-latin.obj: filter-sr-latin.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(recode_sr_latin_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o recode_sr_latin-filter-sr-latin.obj `if test -f 'filter-sr-latin.c'; then $(CYGPATH_W) 'filter-sr-latin.c'; else $(CYGPATH_W) '$(srcdir)/filter-sr-latin.c'; fi`
+
+urlget-urlget.o: urlget.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(urlget_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o urlget-urlget.o `test -f 'urlget.c' || echo '$(srcdir)/'`urlget.c
+
+urlget-urlget.obj: urlget.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(urlget_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o urlget-urlget.obj `if test -f 'urlget.c'; then $(CYGPATH_W) 'urlget.c'; else $(CYGPATH_W) '$(srcdir)/urlget.c'; fi`
+
+xgettext-xgettext.o: xgettext.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-xgettext.o `test -f 'xgettext.c' || echo '$(srcdir)/'`xgettext.c
+
+xgettext-xgettext.obj: xgettext.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-xgettext.obj `if test -f 'xgettext.c'; then $(CYGPATH_W) 'xgettext.c'; else $(CYGPATH_W) '$(srcdir)/xgettext.c'; fi`
+
+xgettext-x-c.o: x-c.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-c.o `test -f 'x-c.c' || echo '$(srcdir)/'`x-c.c
+
+xgettext-x-c.obj: x-c.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-c.obj `if test -f 'x-c.c'; then $(CYGPATH_W) 'x-c.c'; else $(CYGPATH_W) '$(srcdir)/x-c.c'; fi`
+
+xgettext-x-po.o: x-po.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-po.o `test -f 'x-po.c' || echo '$(srcdir)/'`x-po.c
+
+xgettext-x-po.obj: x-po.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-po.obj `if test -f 'x-po.c'; then $(CYGPATH_W) 'x-po.c'; else $(CYGPATH_W) '$(srcdir)/x-po.c'; fi`
+
+xgettext-x-sh.o: x-sh.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-sh.o `test -f 'x-sh.c' || echo '$(srcdir)/'`x-sh.c
+
+xgettext-x-sh.obj: x-sh.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-sh.obj `if test -f 'x-sh.c'; then $(CYGPATH_W) 'x-sh.c'; else $(CYGPATH_W) '$(srcdir)/x-sh.c'; fi`
+
+xgettext-x-python.o: x-python.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-python.o `test -f 'x-python.c' || echo '$(srcdir)/'`x-python.c
+
+xgettext-x-python.obj: x-python.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-python.obj `if test -f 'x-python.c'; then $(CYGPATH_W) 'x-python.c'; else $(CYGPATH_W) '$(srcdir)/x-python.c'; fi`
+
+xgettext-x-lisp.o: x-lisp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-lisp.o `test -f 'x-lisp.c' || echo '$(srcdir)/'`x-lisp.c
+
+xgettext-x-lisp.obj: x-lisp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-lisp.obj `if test -f 'x-lisp.c'; then $(CYGPATH_W) 'x-lisp.c'; else $(CYGPATH_W) '$(srcdir)/x-lisp.c'; fi`
+
+xgettext-x-elisp.o: x-elisp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-elisp.o `test -f 'x-elisp.c' || echo '$(srcdir)/'`x-elisp.c
+
+xgettext-x-elisp.obj: x-elisp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-elisp.obj `if test -f 'x-elisp.c'; then $(CYGPATH_W) 'x-elisp.c'; else $(CYGPATH_W) '$(srcdir)/x-elisp.c'; fi`
+
+xgettext-x-librep.o: x-librep.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-librep.o `test -f 'x-librep.c' || echo '$(srcdir)/'`x-librep.c
+
+xgettext-x-librep.obj: x-librep.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-librep.obj `if test -f 'x-librep.c'; then $(CYGPATH_W) 'x-librep.c'; else $(CYGPATH_W) '$(srcdir)/x-librep.c'; fi`
+
+xgettext-x-scheme.o: x-scheme.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-scheme.o `test -f 'x-scheme.c' || echo '$(srcdir)/'`x-scheme.c
+
+xgettext-x-scheme.obj: x-scheme.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-scheme.obj `if test -f 'x-scheme.c'; then $(CYGPATH_W) 'x-scheme.c'; else $(CYGPATH_W) '$(srcdir)/x-scheme.c'; fi`
+
+xgettext-x-smalltalk.o: x-smalltalk.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-smalltalk.o `test -f 'x-smalltalk.c' || echo '$(srcdir)/'`x-smalltalk.c
+
+xgettext-x-smalltalk.obj: x-smalltalk.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-smalltalk.obj `if test -f 'x-smalltalk.c'; then $(CYGPATH_W) 'x-smalltalk.c'; else $(CYGPATH_W) '$(srcdir)/x-smalltalk.c'; fi`
+
+xgettext-x-java.o: x-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-java.o `test -f 'x-java.c' || echo '$(srcdir)/'`x-java.c
+
+xgettext-x-java.obj: x-java.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-java.obj `if test -f 'x-java.c'; then $(CYGPATH_W) 'x-java.c'; else $(CYGPATH_W) '$(srcdir)/x-java.c'; fi`
+
+xgettext-x-csharp.o: x-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-csharp.o `test -f 'x-csharp.c' || echo '$(srcdir)/'`x-csharp.c
+
+xgettext-x-csharp.obj: x-csharp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-csharp.obj `if test -f 'x-csharp.c'; then $(CYGPATH_W) 'x-csharp.c'; else $(CYGPATH_W) '$(srcdir)/x-csharp.c'; fi`
+
+xgettext-x-awk.o: x-awk.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-awk.o `test -f 'x-awk.c' || echo '$(srcdir)/'`x-awk.c
+
+xgettext-x-awk.obj: x-awk.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-awk.obj `if test -f 'x-awk.c'; then $(CYGPATH_W) 'x-awk.c'; else $(CYGPATH_W) '$(srcdir)/x-awk.c'; fi`
+
+xgettext-x-ycp.o: x-ycp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-ycp.o `test -f 'x-ycp.c' || echo '$(srcdir)/'`x-ycp.c
+
+xgettext-x-ycp.obj: x-ycp.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-ycp.obj `if test -f 'x-ycp.c'; then $(CYGPATH_W) 'x-ycp.c'; else $(CYGPATH_W) '$(srcdir)/x-ycp.c'; fi`
+
+xgettext-x-tcl.o: x-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-tcl.o `test -f 'x-tcl.c' || echo '$(srcdir)/'`x-tcl.c
+
+xgettext-x-tcl.obj: x-tcl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-tcl.obj `if test -f 'x-tcl.c'; then $(CYGPATH_W) 'x-tcl.c'; else $(CYGPATH_W) '$(srcdir)/x-tcl.c'; fi`
+
+xgettext-x-perl.o: x-perl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-perl.o `test -f 'x-perl.c' || echo '$(srcdir)/'`x-perl.c
+
+xgettext-x-perl.obj: x-perl.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-perl.obj `if test -f 'x-perl.c'; then $(CYGPATH_W) 'x-perl.c'; else $(CYGPATH_W) '$(srcdir)/x-perl.c'; fi`
+
+xgettext-x-php.o: x-php.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-php.o `test -f 'x-php.c' || echo '$(srcdir)/'`x-php.c
+
+xgettext-x-php.obj: x-php.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-php.obj `if test -f 'x-php.c'; then $(CYGPATH_W) 'x-php.c'; else $(CYGPATH_W) '$(srcdir)/x-php.c'; fi`
+
+xgettext-x-rst.o: x-rst.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-rst.o `test -f 'x-rst.c' || echo '$(srcdir)/'`x-rst.c
+
+xgettext-x-rst.obj: x-rst.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-rst.obj `if test -f 'x-rst.c'; then $(CYGPATH_W) 'x-rst.c'; else $(CYGPATH_W) '$(srcdir)/x-rst.c'; fi`
+
+xgettext-x-glade.o: x-glade.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-glade.o `test -f 'x-glade.c' || echo '$(srcdir)/'`x-glade.c
+
+xgettext-x-glade.obj: x-glade.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-glade.obj `if test -f 'x-glade.c'; then $(CYGPATH_W) 'x-glade.c'; else $(CYGPATH_W) '$(srcdir)/x-glade.c'; fi`
+
+xgettext-x-lua.o: x-lua.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-lua.o `test -f 'x-lua.c' || echo '$(srcdir)/'`x-lua.c
+
+xgettext-x-lua.obj: x-lua.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-lua.obj `if test -f 'x-lua.c'; then $(CYGPATH_W) 'x-lua.c'; else $(CYGPATH_W) '$(srcdir)/x-lua.c'; fi`
+
+xgettext-x-javascript.o: x-javascript.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-javascript.o `test -f 'x-javascript.c' || echo '$(srcdir)/'`x-javascript.c
+
+xgettext-x-javascript.obj: x-javascript.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-javascript.obj `if test -f 'x-javascript.c'; then $(CYGPATH_W) 'x-javascript.c'; else $(CYGPATH_W) '$(srcdir)/x-javascript.c'; fi`
+
+xgettext-x-vala.o: x-vala.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-vala.o `test -f 'x-vala.c' || echo '$(srcdir)/'`x-vala.c
+
+xgettext-x-vala.obj: x-vala.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-vala.obj `if test -f 'x-vala.c'; then $(CYGPATH_W) 'x-vala.c'; else $(CYGPATH_W) '$(srcdir)/x-vala.c'; fi`
+
+xgettext-x-gsettings.o: x-gsettings.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-gsettings.o `test -f 'x-gsettings.c' || echo '$(srcdir)/'`x-gsettings.c
+
+xgettext-x-gsettings.obj: x-gsettings.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-gsettings.obj `if test -f 'x-gsettings.c'; then $(CYGPATH_W) 'x-gsettings.c'; else $(CYGPATH_W) '$(srcdir)/x-gsettings.c'; fi`
+
+xgettext-libexpat-compat.o: libexpat-compat.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-libexpat-compat.o `test -f 'libexpat-compat.c' || echo '$(srcdir)/'`libexpat-compat.c
+
+xgettext-libexpat-compat.obj: libexpat-compat.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-libexpat-compat.obj `if test -f 'libexpat-compat.c'; then $(CYGPATH_W) 'libexpat-compat.c'; else $(CYGPATH_W) '$(srcdir)/libexpat-compat.c'; fi`
+
+xgettext-x-desktop.o: x-desktop.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-desktop.o `test -f 'x-desktop.c' || echo '$(srcdir)/'`x-desktop.c
+
+xgettext-x-desktop.obj: x-desktop.c
+ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xgettext-x-desktop.obj `if test -f 'x-desktop.c'; then $(CYGPATH_W) 'x-desktop.c'; else $(CYGPATH_W) '$(srcdir)/x-desktop.c'; fi`
+
+.cc.o:
+ $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+ $(AM_V_CXX)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+ $(AM_V_CXX)$(LTCXXCOMPILE) -c -o $@ $<
+
+../woe32dll/libgettextsrc_la-c++format.lo: ../woe32dll/c++format.cc
+ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgettextsrc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/libgettextsrc_la-c++format.lo `test -f '../woe32dll/c++format.cc' || echo '$(srcdir)/'`../woe32dll/c++format.cc
+
+../woe32dll/msgattrib-c++msgattrib.o: ../woe32dll/c++msgattrib.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgattrib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgattrib-c++msgattrib.o `test -f '../woe32dll/c++msgattrib.cc' || echo '$(srcdir)/'`../woe32dll/c++msgattrib.cc
+
+../woe32dll/msgattrib-c++msgattrib.obj: ../woe32dll/c++msgattrib.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgattrib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgattrib-c++msgattrib.obj `if test -f '../woe32dll/c++msgattrib.cc'; then $(CYGPATH_W) '../woe32dll/c++msgattrib.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgattrib.cc'; fi`
+
+../woe32dll/msgcat-c++msgcat.o: ../woe32dll/c++msgcat.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcat_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgcat-c++msgcat.o `test -f '../woe32dll/c++msgcat.cc' || echo '$(srcdir)/'`../woe32dll/c++msgcat.cc
+
+../woe32dll/msgcat-c++msgcat.obj: ../woe32dll/c++msgcat.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcat_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgcat-c++msgcat.obj `if test -f '../woe32dll/c++msgcat.cc'; then $(CYGPATH_W) '../woe32dll/c++msgcat.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgcat.cc'; fi`
+
+../woe32dll/msgcomm-c++msgcomm.o: ../woe32dll/c++msgcomm.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcomm_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgcomm-c++msgcomm.o `test -f '../woe32dll/c++msgcomm.cc' || echo '$(srcdir)/'`../woe32dll/c++msgcomm.cc
+
+../woe32dll/msgcomm-c++msgcomm.obj: ../woe32dll/c++msgcomm.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgcomm_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgcomm-c++msgcomm.obj `if test -f '../woe32dll/c++msgcomm.cc'; then $(CYGPATH_W) '../woe32dll/c++msgcomm.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgcomm.cc'; fi`
+
+../woe32dll/msgconv-c++msgconv.o: ../woe32dll/c++msgconv.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgconv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgconv-c++msgconv.o `test -f '../woe32dll/c++msgconv.cc' || echo '$(srcdir)/'`../woe32dll/c++msgconv.cc
+
+../woe32dll/msgconv-c++msgconv.obj: ../woe32dll/c++msgconv.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgconv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgconv-c++msgconv.obj `if test -f '../woe32dll/c++msgconv.cc'; then $(CYGPATH_W) '../woe32dll/c++msgconv.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgconv.cc'; fi`
+
+../woe32dll/msgen-c++msgen.o: ../woe32dll/c++msgen.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgen_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgen-c++msgen.o `test -f '../woe32dll/c++msgen.cc' || echo '$(srcdir)/'`../woe32dll/c++msgen.cc
+
+../woe32dll/msgen-c++msgen.obj: ../woe32dll/c++msgen.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgen_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgen-c++msgen.obj `if test -f '../woe32dll/c++msgen.cc'; then $(CYGPATH_W) '../woe32dll/c++msgen.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgen.cc'; fi`
+
+../woe32dll/msgfilter-c++msgfilter.o: ../woe32dll/c++msgfilter.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgfilter-c++msgfilter.o `test -f '../woe32dll/c++msgfilter.cc' || echo '$(srcdir)/'`../woe32dll/c++msgfilter.cc
+
+../woe32dll/msgfilter-c++msgfilter.obj: ../woe32dll/c++msgfilter.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgfilter_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgfilter-c++msgfilter.obj `if test -f '../woe32dll/c++msgfilter.cc'; then $(CYGPATH_W) '../woe32dll/c++msgfilter.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgfilter.cc'; fi`
+
+../woe32dll/msggrep-c++msggrep.o: ../woe32dll/c++msggrep.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msggrep_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msggrep-c++msggrep.o `test -f '../woe32dll/c++msggrep.cc' || echo '$(srcdir)/'`../woe32dll/c++msggrep.cc
+
+../woe32dll/msggrep-c++msggrep.obj: ../woe32dll/c++msggrep.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msggrep_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msggrep-c++msggrep.obj `if test -f '../woe32dll/c++msggrep.cc'; then $(CYGPATH_W) '../woe32dll/c++msggrep.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msggrep.cc'; fi`
+
+../woe32dll/msgmerge-c++msgmerge.o: ../woe32dll/c++msgmerge.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgmerge-c++msgmerge.o `test -f '../woe32dll/c++msgmerge.cc' || echo '$(srcdir)/'`../woe32dll/c++msgmerge.cc
+
+../woe32dll/msgmerge-c++msgmerge.obj: ../woe32dll/c++msgmerge.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msgmerge_CPPFLAGS) $(CPPFLAGS) $(msgmerge_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msgmerge-c++msgmerge.obj `if test -f '../woe32dll/c++msgmerge.cc'; then $(CYGPATH_W) '../woe32dll/c++msgmerge.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msgmerge.cc'; fi`
+
+../woe32dll/msguniq-c++msguniq.o: ../woe32dll/c++msguniq.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msguniq_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msguniq-c++msguniq.o `test -f '../woe32dll/c++msguniq.cc' || echo '$(srcdir)/'`../woe32dll/c++msguniq.cc
+
+../woe32dll/msguniq-c++msguniq.obj: ../woe32dll/c++msguniq.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(msguniq_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/msguniq-c++msguniq.obj `if test -f '../woe32dll/c++msguniq.cc'; then $(CYGPATH_W) '../woe32dll/c++msguniq.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++msguniq.cc'; fi`
+
+../woe32dll/xgettext-c++xgettext.o: ../woe32dll/c++xgettext.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/xgettext-c++xgettext.o `test -f '../woe32dll/c++xgettext.cc' || echo '$(srcdir)/'`../woe32dll/c++xgettext.cc
+
+../woe32dll/xgettext-c++xgettext.obj: ../woe32dll/c++xgettext.cc
+ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(xgettext_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ../woe32dll/xgettext-c++xgettext.obj `if test -f '../woe32dll/c++xgettext.cc'; then $(CYGPATH_W) '../woe32dll/c++xgettext.cc'; else $(CYGPATH_W) '$(srcdir)/../woe32dll/c++xgettext.cc'; fi`
+
+.y.c:
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf ../woe32dll/.libs ../woe32dll/_libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) all-local
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs: installdirs-local
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f ../../gettext-runtime/intl/$(am__dirstamp)
+ -rm -f ../woe32dll/$(am__dirstamp)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f po-gram-gen.c
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-libLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am: installcheck-binPROGRAMS
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \
+ uninstall-local
+
+.MAKE: all check install install-am install-exec-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \
+ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-data-local install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-hook \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installcheck-binPROGRAMS \
+ installdirs installdirs-local maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-libLTLIBRARIES uninstall-local
+
+
+# No need to install libgettextsrc.a, except on AIX.
+install-exec-hook: install-exec-clean
+install-exec-clean:
+ case "@host_os@" in \
+ aix*) ;; \
+ *) $(RM) $(DESTDIR)$(libdir)/libgettextsrc.a ;; \
+ esac
+
+po-lex.o po-lex.lo: po-gram-gen2.h
+po-gram-gen2.h: po-gram-gen.h
+ srcdir=''; \
+ test -f ./po-gram-gen.h || srcdir=$(srcdir)/; \
+ $(SED) -e 's/yy/po_gram_/g' -e 's/extern /extern DLL_VARIABLE /' \
+ $${srcdir}po-gram-gen.h > $@-tmp && \
+ mv $@-tmp $@
+
+# Special rules for installation of auxiliary programs.
+
+install-exec-local:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) hostname$(EXEEXT) $(DESTDIR)$(pkglibdir)/hostname$(EXEEXT)
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) urlget$(EXEEXT) $(DESTDIR)$(pkglibdir)/urlget$(EXEEXT)
+ $(INSTALL_SCRIPT) user-email $(DESTDIR)$(pkglibdir)/user-email
+ $(INSTALL_SCRIPT) $(srcdir)/project-id $(DESTDIR)$(pkglibdir)/project-id
+
+installdirs-local:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+uninstall-local:
+ $(RM) $(DESTDIR)$(pkglibdir)/hostname$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/urlget$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/user-email
+ $(RM) $(DESTDIR)$(pkglibdir)/project-id
+
+all-local: all-java-@BUILDJAVAEXE@
+all-java-yes: gnu.gettext.DumpResource$(EXEEXT) gnu.gettext.GetURL$(EXEEXT)
+all-java-no: all-java-no-@BUILDJAVA@
+all-java-no-yes: gettext.jar
+all-java-no-no:
+
+gnu.gettext.DumpResource$(EXEEXT): $(srcdir)/gnu/gettext/DumpResource.java
+ $(GCJ) $(GCJFLAGS) $(srcdir)/gnu/gettext/DumpResource.java --main=gnu.gettext.DumpResource -o $@
+
+gnu.gettext.GetURL$(EXEEXT): $(srcdir)/gnu/gettext/GetURL.java
+ $(GCJ) $(GCJFLAGS) $(srcdir)/gnu/gettext/GetURL.java --main=gnu.gettext.GetURL -o $@
+
+gnu/gettext/DumpResource.class: $(srcdir)/gnu/gettext/DumpResource.java
+ $(JAVACOMP) -d . $(srcdir)/gnu/gettext/DumpResource.java
+
+gnu/gettext/GetURL.class: $(srcdir)/gnu/gettext/GetURL.java
+ $(JAVACOMP) -d . $(srcdir)/gnu/gettext/GetURL.java
+
+gettext.jar: gnu/gettext/DumpResource.class gnu/gettext/GetURL.class
+ $(JAR) cf $@ gnu/gettext/DumpResource*.class gnu/gettext/GetURL*.class
+
+install-exec-local: install-exec-java-@BUILDJAVAEXE@
+install-exec-java-yes: all-java-yes
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) gnu.gettext.DumpResource$(EXEEXT) $(DESTDIR)$(pkglibdir)/gnu.gettext.DumpResource$(EXEEXT)
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) gnu.gettext.GetURL$(EXEEXT) $(DESTDIR)$(pkglibdir)/gnu.gettext.GetURL$(EXEEXT)
+install-exec-java-no:
+
+install-data-local: install-data-java-@BUILDJAVAEXE@
+install-data-java-yes:
+install-data-java-no: install-data-java-no-@BUILDJAVA@
+install-data-java-no-yes: all-java-no-yes
+ $(INSTALL_DATA) gettext.jar $(DESTDIR)$(jardir)/gettext.jar
+install-data-java-no-no:
+
+installdirs-local: installdirs-java-@BUILDJAVAEXE@
+installdirs-java-yes:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+installdirs-java-no: installdirs-java-no-@BUILDJAVA@
+installdirs-java-no-yes:
+ $(MKDIR_P) $(DESTDIR)$(jardir)
+installdirs-java-no-no:
+
+uninstall-local: uninstall-java-@BUILDJAVAEXE@
+uninstall-java-yes:
+ $(RM) $(DESTDIR)$(pkglibdir)/gnu.gettext.DumpResource$(EXEEXT)
+ $(RM) $(DESTDIR)$(pkglibdir)/gnu.gettext.GetURL$(EXEEXT)
+uninstall-java-no: uninstall-java-no-@BUILDJAVA@
+uninstall-java-no-yes:
+ $(RM) $(DESTDIR)$(jardir)/gettext.jar
+uninstall-java-no-no:
+
+all-local: all-csharp-@BUILDCSHARP@
+all-csharp-yes: msgfmt.net.exe msgunfmt.net.exe
+all-csharp-no:
+
+msgfmt.net.exe: msgfmt.cs
+ $(CSHARPCOMP) $(CSHARPCOMPFLAGS) -o $@ $(srcdir)/msgfmt.cs
+
+msgunfmt.net.exe: msgunfmt.cs
+ $(CSHARPCOMP) $(CSHARPCOMPFLAGS) -o $@ -L ../../gettext-runtime/intl-csharp -l GNU.Gettext $(srcdir)/msgunfmt.cs
+
+install-exec-local: install-exec-csharp-@BUILDCSHARP@
+install-exec-csharp-yes: all-csharp-yes
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL_DATA) msgfmt.net.exe $(DESTDIR)$(pkglibdir)/msgfmt.net.exe
+ $(INSTALL_DATA) msgunfmt.net.exe $(DESTDIR)$(pkglibdir)/msgunfmt.net.exe
+install-exec-csharp-no:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+installdirs-local: install-csharp
+installdirs-csharp:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+
+uninstall-local: uninstall-csharp-@BUILDCSHARP@
+uninstall-csharp-yes: all-csharp-yes
+ $(RM) $(DESTDIR)$(pkglibdir)/msgfmt.net.exe
+ $(RM) $(DESTDIR)$(pkglibdir)/msgunfmt.net.exe
+uninstall-csharp-no:
+
+install-data-local: install-tcl
+install-tcl:
+ $(MKDIR_P) $(DESTDIR)$(pkgdatadir)
+ $(INSTALL_DATA) $(srcdir)/msgunfmt.tcl $(DESTDIR)$(pkgdatadir)/msgunfmt.tcl
+
+installdirs-local: installdirs-tcl
+installdirs-tcl:
+ $(MKDIR_P) $(DESTDIR)$(pkgdatadir)
+
+uninstall-local: uninstall-tcl
+uninstall-tcl:
+ $(RM) $(DESTDIR)$(pkgdatadir)/msgunfmt.tcl
+@WOE32_TRUE@gettext.res : $(top_srcdir)/../windows/gettext.rc
+@WOE32_TRUE@ $(WINDRES) `$(SHELL) $(top_srcdir)/../windows/windres-options --escape $(VERSION)` -i $(top_srcdir)/../windows/gettext.rc -o gettext.res --output-format=coff
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/gettext-tools/src/color.c b/gettext-tools/src/color.c
new file mode 100644
index 0000000..47daf00
--- /dev/null
+++ b/gettext-tools/src/color.c
@@ -0,0 +1,445 @@
+/* Color and styling handling.
+ Copyright (C) 2006-2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "color.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "term-ostream.h"
+#include "xalloc.h"
+#include "relocatable.h"
+#include "filename.h"
+#include "concat-filename.h"
+
+
+/* Whether to output a test page. */
+bool color_test_mode;
+
+/* Color option. */
+enum color_option color_mode = color_tty;
+
+/* Style to use when coloring. */
+const char *style_file_name;
+
+/* --color argument handling. Return an error indicator. */
+bool
+handle_color_option (const char *option)
+{
+ if (option != NULL)
+ {
+ if (strcmp (option, "never") == 0 || strcmp (option, "no") == 0)
+ color_mode = color_no;
+ else if (strcmp (option, "auto") == 0 || strcmp (option, "tty") == 0)
+ color_mode = color_tty;
+ else if (strcmp (option, "always") == 0 || strcmp (option, "yes") == 0)
+ color_mode = color_yes;
+ else if (strcmp (option, "html") == 0)
+ color_mode = color_html;
+ else if (strcmp (option, "test") == 0)
+ color_test_mode = true;
+ else
+ {
+ fprintf (stderr, "invalid --color argument: %s\n", option);
+ return true;
+ }
+ }
+ else
+ /* --color is equivalent to --color=yes. */
+ color_mode = color_yes;
+ return false;
+}
+
+/* --style argument handling. */
+void
+handle_style_option (const char *option)
+{
+ style_file_name = option;
+}
+
+/* Print a color test page. */
+void
+print_color_test ()
+{
+ /* Code copied from test-term-ostream.c. */
+ static struct { const char *name; term_color_t c; int r; int g; int b; }
+ colors[] =
+ {
+ { "black", -2, 0, 0, 0 },
+ { "blue", -2, 0, 0, 255 },
+ { "green", -2, 0, 255, 0 },
+ { "cyan", -2, 0, 255, 255 },
+ { "red", -2, 255, 0, 0 },
+ { "magenta", -2, 255, 0, 255 },
+ { "yellow", -2, 255, 255, 0 },
+ { "white", -2, 255, 255, 255 },
+ { "default", COLOR_DEFAULT }
+ };
+ term_ostream_t stream;
+ int i, row, col;
+
+ stream = term_ostream_create (1, "stdout");
+
+ for (i = 0; i < 8; i++)
+ colors[i].c =
+ term_ostream_rgb_to_color (stream, colors[i].r, colors[i].g, colors[i].b);
+
+ ostream_write_str (stream, "Colors (foreground/background):\n");
+ ostream_write_str (stream, " ");
+ for (col = 0; col <= 8; col++)
+ {
+ const char *name = colors[col].name;
+ ostream_write_str (stream, "|");
+ ostream_write_str (stream, name);
+ ostream_write_mem (stream, " ", 7 - strlen (name));
+ }
+ ostream_write_str (stream, "\n");
+ for (row = 0; row <= 8; row++)
+ {
+ const char *name = colors[row].name;
+ ostream_write_str (stream, name);
+ ostream_write_mem (stream, " ", 7 - strlen (name));
+ for (col = 0; col <= 8; col++)
+ {
+ term_color_t row_color = colors[row].c;
+ term_color_t col_color = colors[col].c;
+
+ ostream_write_str (stream, "|");
+ term_ostream_set_color (stream, row_color);
+ term_ostream_set_bgcolor (stream, col_color);
+ if (!(term_ostream_get_color (stream) == row_color
+ && term_ostream_get_bgcolor (stream) == col_color))
+ abort ();
+ ostream_write_str (stream, " Words ");
+ term_ostream_set_color (stream, COLOR_DEFAULT);
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ if (!(term_ostream_get_color (stream) == COLOR_DEFAULT
+ && term_ostream_get_bgcolor (stream) == COLOR_DEFAULT))
+ abort ();
+ }
+ ostream_write_str (stream, "\n");
+ }
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Colors (hue/saturation):\n");
+ /* Hue from 0 to 1. */
+ for (row = 0; row <= 17; row++)
+ {
+ ostream_write_str (stream, row == 0 ? "red: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int r = 255;
+ int b = (int) (255.0f / 64.0f * col + 0.5f);
+ int g = b + (int) (row / 17.0f * (r - b) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ /* Hue from 1 to 2. */
+ for (row = 17; row >= 0; row--)
+ {
+ ostream_write_str (stream, row == 17 ? "yellow: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int g = 255;
+ int b = (int) (255.0f / 64.0f * col + 0.5f);
+ int r = b + (int) (row / 17.0f * (g - b) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ /* Hue from 2 to 3. */
+ for (row = 0; row <= 17; row++)
+ {
+ ostream_write_str (stream, row == 0 ? "green: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int g = 255;
+ int r = (int) (255.0f / 64.0f * col + 0.5f);
+ int b = r + (int) (row / 17.0f * (g - r) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ /* Hue from 3 to 4. */
+ for (row = 17; row >= 0; row--)
+ {
+ ostream_write_str (stream, row == 17 ? "cyan: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int b = 255;
+ int r = (int) (255.0f / 64.0f * col + 0.5f);
+ int g = r + (int) (row / 17.0f * (b - r) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ /* Hue from 4 to 5. */
+ for (row = 0; row <= 17; row++)
+ {
+ ostream_write_str (stream, row == 0 ? "blue: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int b = 255;
+ int g = (int) (255.0f / 64.0f * col + 0.5f);
+ int r = g + (int) (row / 17.0f * (b - g) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ /* Hue from 5 to 6. */
+ for (row = 17; row >= 0; row--)
+ {
+ ostream_write_str (stream, row == 17 ? "magenta: " :
+ row == 0 ? "red: " : " ");
+ for (col = 0; col <= 64; col++)
+ {
+ int r = 255;
+ int g = (int) (255.0f / 64.0f * col + 0.5f);
+ int b = g + (int) (row / 17.0f * (r - g) + 0.5f);
+ term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
+ term_ostream_set_bgcolor (stream, c);
+ ostream_write_str (stream, " ");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ }
+ ostream_write_str (stream, "\n");
+ }
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Weights:\n");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ if (term_ostream_get_weight (stream) != WEIGHT_NORMAL)
+ abort ();
+ ostream_write_str (stream, "normal, ");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ if (term_ostream_get_weight (stream) != WEIGHT_BOLD)
+ abort ();
+ ostream_write_str (stream, "bold, ");
+ term_ostream_set_weight (stream, WEIGHT_DEFAULT);
+ if (term_ostream_get_weight (stream) != WEIGHT_DEFAULT)
+ abort ();
+ ostream_write_str (stream, "default \n");
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Postures:\n");
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ if (term_ostream_get_posture (stream) != POSTURE_NORMAL)
+ abort ();
+ ostream_write_str (stream, "normal, ");
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ if (term_ostream_get_posture (stream) != POSTURE_ITALIC)
+ abort ();
+ ostream_write_str (stream, "italic, ");
+ term_ostream_set_posture (stream, POSTURE_DEFAULT);
+ if (term_ostream_get_posture (stream) != POSTURE_DEFAULT)
+ abort ();
+ ostream_write_str (stream, "default \n");
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Text decorations:\n");
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ if (term_ostream_get_underline (stream) != UNDERLINE_OFF)
+ abort ();
+ ostream_write_str (stream, "normal, ");
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ if (term_ostream_get_underline (stream) != UNDERLINE_ON)
+ abort ();
+ ostream_write_str (stream, "underlined, ");
+ term_ostream_set_underline (stream, UNDERLINE_DEFAULT);
+ if (term_ostream_get_underline (stream) != UNDERLINE_DEFAULT)
+ abort ();
+ ostream_write_str (stream, "default \n");
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Colors (foreground) mixed with attributes:\n");
+ for (row = 0; row <= 8; row++)
+ {
+ const char *name = colors[row].name;
+ ostream_write_str (stream, name);
+ ostream_write_mem (stream, " ", 7 - strlen (name));
+ term_ostream_set_color (stream, colors[row].c);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ ostream_write_str (stream, "bold");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ ostream_write_str (stream, "italic");
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "underlined");
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_color (stream, COLOR_DEFAULT);
+ ostream_write_str (stream, "\n ");
+ term_ostream_set_color (stream, colors[row].c);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ ostream_write_str (stream, "bold+italic");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "bold+underl");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "italic+underl");
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_color (stream, COLOR_DEFAULT);
+ ostream_write_str (stream, "\n");
+ }
+ ostream_write_str (stream, "\n");
+
+ ostream_write_str (stream, "Colors (background) mixed with attributes:\n");
+ for (row = 0; row <= 8; row++)
+ {
+ const char *name = colors[row].name;
+ ostream_write_str (stream, name);
+ ostream_write_mem (stream, " ", 7 - strlen (name));
+ term_ostream_set_bgcolor (stream, colors[row].c);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ ostream_write_str (stream, "bold");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ ostream_write_str (stream, "italic");
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "underlined");
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ ostream_write_str (stream, "\n ");
+ term_ostream_set_bgcolor (stream, colors[row].c);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ ostream_write_str (stream, "bold+italic");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_weight (stream, WEIGHT_BOLD);
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "bold+underl");
+ term_ostream_set_weight (stream, WEIGHT_NORMAL);
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_posture (stream, POSTURE_ITALIC);
+ term_ostream_set_underline (stream, UNDERLINE_ON);
+ ostream_write_str (stream, "italic+underl");
+ term_ostream_set_posture (stream, POSTURE_NORMAL);
+ term_ostream_set_underline (stream, UNDERLINE_OFF);
+ ostream_write_str (stream, "|normal|");
+ term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
+ ostream_write_str (stream, "\n");
+ }
+ ostream_write_str (stream, "\n");
+
+ ostream_free (stream);
+}
+
+/* Lookup the location of the style file. */
+static const char *
+style_file_lookup (const char *file_name)
+{
+ if (!IS_PATH_WITH_DIR (file_name))
+ {
+ /* It's a file name without a directory specification.
+ If it does not exist in the current directory... */
+ struct stat statbuf;
+
+ if (stat (file_name, &statbuf) < 0)
+ {
+ /* ... but it exists in the styles installation location... */
+ const char *gettextstylesdir = relocate (GETTEXTDATADIR "/styles");
+ char *possible_file_name =
+ xconcatenated_filename (gettextstylesdir, file_name, NULL);
+
+ if (stat (possible_file_name, &statbuf) >= 0)
+ {
+ /* ... then use the file in the styles installation directory. */
+ return possible_file_name;
+ }
+ free (possible_file_name);
+ }
+
+ /* Let the CSS library show a warning. */
+ }
+ return file_name;
+}
+
+/* Assign a default value to style_file_name if necessary. */
+void
+style_file_prepare ()
+{
+ if (style_file_name == NULL)
+ {
+ const char *user_preference = getenv ("PO_STYLE");
+
+ if (user_preference != NULL && user_preference[0] != '\0')
+ style_file_name = style_file_lookup (xstrdup (user_preference));
+ else
+ {
+ const char *gettextdatadir;
+
+ /* Make it possible to override the po-default.css location. This is
+ necessary for running the testsuite before "make install". */
+ gettextdatadir = getenv ("GETTEXTDATADIR");
+ if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
+ gettextdatadir = relocate (GETTEXTDATADIR);
+
+ style_file_name =
+ xconcatenated_filename (gettextdatadir, "styles/po-default.css",
+ NULL);
+ }
+ }
+ else
+ style_file_name = style_file_lookup (style_file_name);
+}
diff --git a/gettext-tools/src/color.h b/gettext-tools/src/color.h
new file mode 100644
index 0000000..f47f40f
--- /dev/null
+++ b/gettext-tools/src/color.h
@@ -0,0 +1,57 @@
+/* Color and styling handling.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _COLOR_H
+#define _COLOR_H
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Whether to output a test page. */
+extern DLL_VARIABLE bool color_test_mode;
+
+/* Color option. */
+enum color_option { color_no, color_tty, color_yes, color_html };
+extern DLL_VARIABLE enum color_option color_mode;
+
+/* Style to use when coloring. */
+extern DLL_VARIABLE const char *style_file_name;
+
+/* --color argument handling. Return an error indicator. */
+extern bool handle_color_option (const char *option);
+
+/* --style argument handling. */
+extern void handle_style_option (const char *option);
+
+/* Print a color test page. */
+extern void print_color_test (void);
+
+/* Assign a default value to style_file_name if necessary. */
+extern void style_file_prepare (void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _COLOR_H */
diff --git a/gettext-tools/src/dir-list.c b/gettext-tools/src/dir-list.c
new file mode 100644
index 0000000..ee46d05
--- /dev/null
+++ b/gettext-tools/src/dir-list.c
@@ -0,0 +1,85 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1996, 1998, 2000-2002, 2006 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Specification. */
+#include "dir-list.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "str-list.h"
+
+static string_list_ty *directory /* = NULL */;
+
+
+/* Append a directory to the end of the list of directories. */
+void
+dir_list_append (const char *s)
+{
+ if (directory == NULL)
+ directory = string_list_alloc ();
+ string_list_append_unique (directory, s);
+}
+
+
+/* Return the nth directory, or NULL of n is out of range. */
+const char *
+dir_list_nth (int n)
+{
+ /* The default value of the list consists of the single directory ".". */
+ if (directory == NULL)
+ dir_list_append (".");
+
+ if (n < 0 || n >= directory->nitems)
+ return NULL;
+ return directory->item[n];
+}
+
+
+/* Return the current list of directories, for later use with dir_list_restore.
+ Reset the list to empty. */
+void *
+dir_list_save_reset ()
+{
+ void *saved_value = directory;
+
+ directory = NULL;
+ return saved_value;
+}
+
+
+/* Restore a previously saved list of directories. */
+void
+dir_list_restore (void *saved_value)
+{
+ /* Don't free the contained strings, because they may have been returned
+ by dir_list_nth and may still be in use. */
+ if (directory != NULL)
+ {
+ if (directory->item != NULL)
+ free (directory->item);
+ free (directory);
+ }
+
+ directory = (string_list_ty *) saved_value;
+}
diff --git a/gettext-tools/src/dir-list.h b/gettext-tools/src/dir-list.h
new file mode 100644
index 0000000..6ce32bb
--- /dev/null
+++ b/gettext-tools/src/dir-list.h
@@ -0,0 +1,51 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1996, 1998, 2000-2003 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _DIR_LIST_H
+#define _DIR_LIST_H
+
+/* Management of the list of directories where PO files are searched.
+ It is an ordered list, without duplicates. The default value of the
+ list consists of the single directory ".". */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Append a directory to the end of the list of directories. */
+extern void dir_list_append (const char *directory);
+
+/* Return the nth directory, or NULL of n is out of range. */
+extern const char *dir_list_nth (int n);
+
+/* Return the current list of directories, for later use with dir_list_restore.
+ Reset the list to empty. */
+extern void *dir_list_save_reset (void);
+
+/* Restore a previously saved list of directories. */
+extern void dir_list_restore (void *saved_value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _DIR_LIST_H */
diff --git a/gettext-tools/src/file-list.c b/gettext-tools/src/file-list.c
new file mode 100644
index 0000000..2ee983d
--- /dev/null
+++ b/gettext-tools/src/file-list.c
@@ -0,0 +1,92 @@
+/* Reading file lists.
+ Copyright (C) 1995-1998, 2000-2002, 2007 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "file-list.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "str-list.h"
+#include "error.h"
+#include "gettext.h"
+
+/* A convenience macro. I don't like writing gettext() every time. */
+#define _(str) gettext (str)
+
+
+/* Read list of filenames from a file. */
+string_list_ty *
+read_names_from_file (const char *file_name)
+{
+ size_t line_len = 0;
+ char *line_buf = NULL;
+ FILE *fp;
+ string_list_ty *result;
+
+ if (strcmp (file_name, "-") == 0)
+ fp = stdin;
+ else
+ {
+ fp = fopen (file_name, "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno,
+ _("error while opening \"%s\" for reading"), file_name);
+ }
+
+ result = string_list_alloc ();
+
+ while (!feof (fp))
+ {
+ /* Read next line from file. */
+ int len = getline (&line_buf, &line_len, fp);
+
+ /* In case of an error leave loop. */
+ if (len < 0)
+ break;
+
+ /* Remove trailing '\n' and trailing whitespace. */
+ if (len > 0 && line_buf[len - 1] == '\n')
+ line_buf[--len] = '\0';
+ while (len > 0
+ && (line_buf[len - 1] == ' '
+ || line_buf[len - 1] == '\t'
+ || line_buf[len - 1] == '\r'))
+ line_buf[--len] = '\0';
+
+ /* Test if we have to ignore the line. */
+ if (*line_buf == '\0' || *line_buf == '#')
+ continue;
+
+ string_list_append_unique (result, line_buf);
+ }
+
+ /* Free buffer allocated through getline. */
+ if (line_buf != NULL)
+ free (line_buf);
+
+ /* Close input stream. */
+ if (fp != stdin)
+ fclose (fp);
+
+ return result;
+}
diff --git a/gettext-tools/src/file-list.h b/gettext-tools/src/file-list.h
new file mode 100644
index 0000000..9665fbe
--- /dev/null
+++ b/gettext-tools/src/file-list.h
@@ -0,0 +1,40 @@
+/* Reading file lists.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _FILE_LIST_H
+#define _FILE_LIST_H
+
+#include "str-list.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Read list of filenames from a file.
+ One filename per line. Lines starting with # and whitespace lines are
+ ignored. Trailing whitespace is removed. */
+extern string_list_ty *read_names_from_file (const char *file_name);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _FILE_LIST_H */
diff --git a/gettext-tools/src/filter-quote.c b/gettext-tools/src/filter-quote.c
new file mode 100644
index 0000000..2e9b7dc
--- /dev/null
+++ b/gettext-tools/src/filter-quote.c
@@ -0,0 +1,224 @@
+/* Convert ASCII quotations to Unicode quotations.
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Daiki Ueno <ueno@gnu.org>, 2014.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "filters.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xalloc.h"
+
+#define BOLD_START "\x1b[1m"
+#define BOLD_END "\x1b[0m"
+
+/* This is a direct translation of po/quot.sed and po/boldquot.sed. */
+static void
+convert_ascii_quote_to_unicode (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p,
+ bool bold)
+{
+ const char *start, *end, *p;
+ char *output, *r;
+ bool state;
+ size_t quote_count;
+
+ start = input;
+ end = &input[input_len - 1];
+
+ /* True if we have seen a character which could be an opening
+ quotation mark. Note that we can't determine if it is really an
+ opening quotation mark until we see a closing quotation mark. */
+ state = false;
+
+ /* Count the number of quotation characters. */
+ quote_count = 0;
+ for (p = start; p <= end; p++)
+ {
+ size_t len;
+
+ p = strpbrk (p, "`'\"");
+ if (!p)
+ break;
+
+ len = strspn (p, "`'\"");
+ quote_count += len;
+ p += len;
+ }
+
+ /* Large enough. */
+ r = output = XNMALLOC (input_len - quote_count
+ + (bold ? 7 : 3) * quote_count + 1,
+ char);
+
+#undef COPY_SEEN
+#define COPY_SEEN \
+ do \
+ { \
+ memcpy (r, start, p - start); \
+ r += p - start; \
+ start = p; \
+ } \
+ while (0)
+
+ for (p = start; p <= end; p++)
+ {
+ switch (*p)
+ {
+ case '"':
+ if (state)
+ {
+ if (*start == '"')
+ {
+ if (p > start + 1)
+ {
+ /* U+201C: LEFT DOUBLE QUOTATION MARK */
+ memcpy (r, "\xe2\x80\x9c", 3);
+ r += 3;
+ if (bold)
+ {
+ memcpy (r, BOLD_START, 4);
+ r += 4;
+ }
+ memcpy (r, start + 1, p - start - 1);
+ r += p - start - 1;
+ if (bold)
+ {
+ memcpy (r, BOLD_END, 4);
+ r += 4;
+ }
+ /* U+201D: RIGHT DOUBLE QUOTATION MARK */
+ memcpy (r, "\xe2\x80\x9d", 3);
+ r += 3;
+ }
+ else
+ {
+ /* Consider "" as "". */
+ memcpy (r, "\"\"", 2);
+ r += 2;
+ }
+ start = p + 1;
+ state = false;
+ }
+ }
+ else
+ {
+ COPY_SEEN;
+ state = true;
+ }
+ break;
+
+ case '`':
+ if (state)
+ {
+ if (*start == '`')
+ COPY_SEEN;
+ }
+ else
+ {
+ COPY_SEEN;
+ state = true;
+ }
+ break;
+
+ case '\'':
+ if (state)
+ {
+ if (/* `...' */
+ *start == '`'
+ /* '...', where:
+ - The left quote is preceded by a space, and the
+ right quote is followed by a space.
+ - The left quote is preceded by a space, and the
+ right quote is at the end of line.
+ - The left quote is at the beginning of the line, and
+ the right quote is followed by a space.
+ */
+ || (*start == '\''
+ && (((start > input && *(start - 1) == ' ')
+ && (p == end || *(p + 1) == '\n' || *(p + 1) == ' '))
+ || ((start == input || *(start - 1) == '\n')
+ && p < end && *(p + 1) == ' '))))
+ {
+ /* U+2018: LEFT SINGLE QUOTATION MARK */
+ memcpy (r, "\xe2\x80\x98", 3);
+ r += 3;
+ if (bold)
+ {
+ memcpy (r, BOLD_START, 4);
+ r += 4;
+ }
+ memcpy (r, start + 1, p - start - 1);
+ r += p - start - 1;
+ if (bold)
+ {
+ memcpy (r, BOLD_END, 4);
+ r += 4;
+ }
+ /* U+2019: RIGHT SINGLE QUOTATION MARK */
+ memcpy (r, "\xe2\x80\x99", 3);
+ r += 3;
+ start = p + 1;
+ }
+ else
+ COPY_SEEN;
+ state = false;
+ }
+ else if (p == input || *(p - 1) == '\n' || *(p - 1) == ' ')
+ {
+ COPY_SEEN;
+ state = true;
+ }
+ break;
+ }
+ }
+
+#undef COPY_SEEN
+
+ /* Copy the rest to R. */
+ if (p > start)
+ {
+ memcpy (r, start, p - start);
+ r += p - start;
+ }
+ *r = '\0';
+
+ *output_p = output;
+ *output_len_p = r - output;
+}
+
+void
+ascii_quote_to_unicode (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p)
+{
+ convert_ascii_quote_to_unicode (input, input_len,
+ output_p, output_len_p,
+ false);
+}
+
+void
+ascii_quote_to_unicode_bold (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p)
+{
+ convert_ascii_quote_to_unicode (input, input_len,
+ output_p, output_len_p,
+ true);
+}
diff --git a/gettext-tools/src/filter-sr-latin.c b/gettext-tools/src/filter-sr-latin.c
new file mode 100644
index 0000000..d6dbd95
--- /dev/null
+++ b/gettext-tools/src/filter-sr-latin.c
@@ -0,0 +1,400 @@
+/* Recode Serbian text from Cyrillic to Latin script.
+ Copyright (C) 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Danilo Šegan <danilo@gnome.org>, 2006,
+ and Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "filters.h"
+
+#include <stdlib.h>
+
+#include "xalloc.h"
+
+
+/* Table for Serbian Cyrillic to Latin transcription.
+ The table is indexed by the Unicode code point, in the range 0x0400..0x04ef.
+ The longest table entry is three bytes long. */
+static const char table[240][3 + 1] =
+{
+ /* U+0400 */ "\xC3\x88", /* "È" */
+ /* U+0401 */ "",
+ /* U+0402 */ "\xC4\x90", /* "Đ" */
+ /* U+0403 */ "",
+ /* U+0404 */ "",
+ /* U+0405 */ "",
+ /* U+0406 */ "",
+ /* U+0407 */ "",
+ /* U+0408 */ "J",
+ /* U+0409 */ "Lj",
+ /* U+040A */ "Nj",
+ /* U+040B */ "\xC4\x86", /* "Ć" */
+ /* U+040C */ "",
+ /* U+040D */ "\xC3\x8C", /* "Ì" */
+ /* U+040E */ "",
+ /* U+040F */ "D\xC5\xBE", /* "Dž" */
+ /* U+0410 */ "A",
+ /* U+0411 */ "B",
+ /* U+0412 */ "V",
+ /* U+0413 */ "G",
+ /* U+0414 */ "D",
+ /* U+0415 */ "E",
+ /* U+0416 */ "\xC5\xBD", /* "Ž" */
+ /* U+0417 */ "Z",
+ /* U+0418 */ "I",
+ /* U+0419 */ "",
+ /* U+041A */ "K",
+ /* U+041B */ "L",
+ /* U+041C */ "M",
+ /* U+041D */ "N",
+ /* U+041E */ "O",
+ /* U+041F */ "P",
+ /* U+0420 */ "R",
+ /* U+0421 */ "S",
+ /* U+0422 */ "T",
+ /* U+0423 */ "U",
+ /* U+0424 */ "F",
+ /* U+0425 */ "H",
+ /* U+0426 */ "C",
+ /* U+0427 */ "\xC4\x8C", /* "Č" */
+ /* U+0428 */ "\xC5\xA0", /* "Š" */
+ /* U+0429 */ "",
+ /* U+042A */ "",
+ /* U+042B */ "",
+ /* U+042C */ "",
+ /* U+042D */ "",
+ /* U+042E */ "",
+ /* U+042F */ "",
+ /* U+0430 */ "a",
+ /* U+0431 */ "b",
+ /* U+0432 */ "v",
+ /* U+0433 */ "g",
+ /* U+0434 */ "d",
+ /* U+0435 */ "e",
+ /* U+0436 */ "\xC5\xBE", /* "ž" */
+ /* U+0437 */ "z",
+ /* U+0438 */ "i",
+ /* U+0439 */ "",
+ /* U+043A */ "k",
+ /* U+043B */ "l",
+ /* U+043C */ "m",
+ /* U+043D */ "n",
+ /* U+043E */ "o",
+ /* U+043F */ "p",
+ /* U+0440 */ "r",
+ /* U+0441 */ "s",
+ /* U+0442 */ "t",
+ /* U+0443 */ "u",
+ /* U+0444 */ "f",
+ /* U+0445 */ "h",
+ /* U+0446 */ "c",
+ /* U+0447 */ "\xC4\x8D", /* "č" */
+ /* U+0448 */ "\xC5\xA1", /* "š" */
+ /* U+0449 */ "",
+ /* U+044A */ "",
+ /* U+044B */ "",
+ /* U+044C */ "",
+ /* U+044D */ "",
+ /* U+044E */ "",
+ /* U+044F */ "",
+ /* U+0450 */ "\xC3\xA8", /* "è" */
+ /* U+0451 */ "",
+ /* U+0452 */ "\xC4\x91", /* "đ" */
+ /* U+0453 */ "",
+ /* U+0454 */ "",
+ /* U+0455 */ "",
+ /* U+0456 */ "",
+ /* U+0457 */ "",
+ /* U+0458 */ "j",
+ /* U+0459 */ "lj",
+ /* U+045A */ "nj",
+ /* U+045B */ "\xC4\x87", /* "ć" */
+ /* U+045C */ "",
+ /* U+045D */ "\xC3\xAC", /* "ì" */
+ /* U+045E */ "",
+ /* U+045F */ "d\xC5\xBE", /* "dž" */
+ /* U+0460 */ "",
+ /* U+0461 */ "",
+ /* U+0462 */ "",
+ /* U+0463 */ "",
+ /* U+0464 */ "",
+ /* U+0465 */ "",
+ /* U+0466 */ "",
+ /* U+0467 */ "",
+ /* U+0468 */ "",
+ /* U+0469 */ "",
+ /* U+046A */ "",
+ /* U+046B */ "",
+ /* U+046C */ "",
+ /* U+046D */ "",
+ /* U+046E */ "",
+ /* U+046F */ "",
+ /* U+0470 */ "",
+ /* U+0471 */ "",
+ /* U+0472 */ "",
+ /* U+0473 */ "",
+ /* U+0474 */ "",
+ /* U+0475 */ "",
+ /* U+0476 */ "",
+ /* U+0477 */ "",
+ /* U+0478 */ "",
+ /* U+0479 */ "",
+ /* U+047A */ "",
+ /* U+047B */ "",
+ /* U+047C */ "",
+ /* U+047D */ "",
+ /* U+047E */ "",
+ /* U+047F */ "",
+ /* U+0480 */ "",
+ /* U+0481 */ "",
+ /* U+0482 */ "",
+ /* U+0483 */ "",
+ /* U+0484 */ "",
+ /* U+0485 */ "",
+ /* U+0486 */ "",
+ /* U+0487 */ "",
+ /* U+0488 */ "",
+ /* U+0489 */ "",
+ /* U+048A */ "",
+ /* U+048B */ "",
+ /* U+048C */ "",
+ /* U+048D */ "",
+ /* U+048E */ "",
+ /* U+048F */ "",
+ /* U+0490 */ "",
+ /* U+0491 */ "",
+ /* U+0492 */ "",
+ /* U+0493 */ "",
+ /* U+0494 */ "",
+ /* U+0495 */ "",
+ /* U+0496 */ "",
+ /* U+0497 */ "",
+ /* U+0498 */ "",
+ /* U+0499 */ "",
+ /* U+049A */ "",
+ /* U+049B */ "",
+ /* U+049C */ "",
+ /* U+049D */ "",
+ /* U+049E */ "",
+ /* U+049F */ "",
+ /* U+04A0 */ "",
+ /* U+04A1 */ "",
+ /* U+04A2 */ "",
+ /* U+04A3 */ "",
+ /* U+04A4 */ "",
+ /* U+04A5 */ "",
+ /* U+04A6 */ "",
+ /* U+04A7 */ "",
+ /* U+04A8 */ "",
+ /* U+04A9 */ "",
+ /* U+04AA */ "",
+ /* U+04AB */ "",
+ /* U+04AC */ "",
+ /* U+04AD */ "",
+ /* U+04AE */ "",
+ /* U+04AF */ "",
+ /* U+04B0 */ "",
+ /* U+04B1 */ "",
+ /* U+04B2 */ "",
+ /* U+04B3 */ "",
+ /* U+04B4 */ "",
+ /* U+04B5 */ "",
+ /* U+04B6 */ "",
+ /* U+04B7 */ "",
+ /* U+04B8 */ "",
+ /* U+04B9 */ "",
+ /* U+04BA */ "",
+ /* U+04BB */ "",
+ /* U+04BC */ "",
+ /* U+04BD */ "",
+ /* U+04BE */ "",
+ /* U+04BF */ "",
+ /* U+04C0 */ "",
+ /* U+04C1 */ "",
+ /* U+04C2 */ "",
+ /* U+04C3 */ "",
+ /* U+04C4 */ "",
+ /* U+04C5 */ "",
+ /* U+04C6 */ "",
+ /* U+04C7 */ "",
+ /* U+04C8 */ "",
+ /* U+04C9 */ "",
+ /* U+04CA */ "",
+ /* U+04CB */ "",
+ /* U+04CC */ "",
+ /* U+04CD */ "",
+ /* U+04CE */ "",
+ /* U+04CF */ "",
+ /* U+04D0 */ "",
+ /* U+04D1 */ "",
+ /* U+04D2 */ "",
+ /* U+04D3 */ "",
+ /* U+04D4 */ "",
+ /* U+04D5 */ "",
+ /* U+04D6 */ "",
+ /* U+04D7 */ "",
+ /* U+04D8 */ "",
+ /* U+04D9 */ "",
+ /* U+04DA */ "",
+ /* U+04DB */ "",
+ /* U+04DC */ "",
+ /* U+04DD */ "",
+ /* U+04DE */ "",
+ /* U+04DF */ "",
+ /* U+04E0 */ "",
+ /* U+04E1 */ "",
+ /* U+04E2 */ "\xC4\xAA", /* "Ī" */
+ /* U+04E3 */ "\xC4\xAB", /* "ī" */
+ /* U+04E4 */ "",
+ /* U+04E5 */ "",
+ /* U+04E6 */ "",
+ /* U+04E7 */ "",
+ /* U+04E8 */ "",
+ /* U+04E9 */ "",
+ /* U+04EA */ "",
+ /* U+04EB */ "",
+ /* U+04EC */ "",
+ /* U+04ED */ "",
+ /* U+04EE */ "\xC5\xAA", /* "Ū" */
+ /* U+04EF */ "\xC5\xAB" /* "ū" */
+};
+
+/* Quick test for an uppercase character in the range U+0041..U+005A.
+ The argument must be a byte in the range 0..UCHAR_MAX. */
+#define IS_UPPERCASE_LATIN(byte) \
+ ((unsigned char) ((byte) - 'A') <= 'Z' - 'A')
+
+/* Quick test for an uppercase character in the range U+0400..U+042F,
+ or exactly U+04E2 or U+04EE.
+ The arguments must be bytes in the range 0..UCHAR_MAX. */
+#define IS_UPPERCASE_CYRILLIC(byte1,byte2) \
+ (((byte1) == 0xd0 && (unsigned char) ((byte2) - 0x80) < 0x30) \
+ || ((byte1) == 0xd3 && ((byte2) == 0xa2 || (byte2) == 0xae)))
+
+void
+serbian_to_latin (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p)
+{
+ /* Loop through the input string, producing a replacement for each character.
+ Only characters in the range U+0400..U+04EF (\xD0\x80..\xD3\xAF) need to
+ be handled, and more precisely only those for which a replacement exists
+ in the table. Other characters are copied without modification.
+ The characters U+0409, U+040A, U+040F are transliterated to uppercase or
+ mixed-case replacements ("LJ" / "Lj", "NJ" / "Nj", "DŽ" / "Dž"), depending
+ on the case of the surrounding characters.
+ Since we assume UTF-8 encoding, the bytes \xD0..\xD3 can only occur at the
+ beginning of a character; the second and further bytes of a character are
+ all in the range \x80..\xBF. */
+
+ /* Since sequences of 2 bytes are mapped to sequences of at most 3 bytes,
+ the size of the output will be at most 1.5 * input_len. */
+ size_t allocated = input_len + (input_len >> 1);
+ char *output = XNMALLOC (allocated, char);
+
+ const char *input_end = input + input_len;
+ const char *ip;
+ char *op;
+
+ for (ip = input, op = output; ip < input_end; )
+ {
+ unsigned char byte = (unsigned char) *ip;
+
+ /* Test for the first byte of a Cyrillic character. */
+ if ((byte >= 0xd0 && byte <= 0xd3) && (ip + 1 < input_end))
+ {
+ unsigned char second_byte = (unsigned char) ip[1];
+
+ /* Verify the second byte is valid. */
+ if (second_byte >= 0x80 && second_byte < 0xc0)
+ {
+ unsigned int uc = ((byte & 0x1f) << 6) | (second_byte & 0x3f);
+
+ if (uc >= 0x0400 && uc <= 0x04ef)
+ {
+ /* Look up replacement from the table. */
+ const char *repl = table[uc - 0x0400];
+
+ if (repl[0] != '\0')
+ {
+ /* Found a replacement.
+ Now handle the special cases. */
+ if (uc == 0x0409 || uc == 0x040a || uc == 0x040f)
+ if ((ip + 2 < input_end
+ && IS_UPPERCASE_LATIN ((unsigned char) ip[2]))
+ || (ip + 3 < input_end
+ && IS_UPPERCASE_CYRILLIC ((unsigned char) ip[2],
+ (unsigned char) ip[3]))
+ || (ip >= input + 1
+ && IS_UPPERCASE_LATIN ((unsigned char) ip[-1]))
+ || (ip >= input + 2
+ && IS_UPPERCASE_CYRILLIC ((unsigned char) ip[-2],
+ (unsigned char) ip[-1])))
+ {
+ /* Use the upper-case replacement instead of
+ the mixed-case replacement. */
+ switch (uc)
+ {
+ case 0x0409:
+ repl = "LJ"; break;
+ case 0x040a:
+ repl = "NJ"; break;
+ case 0x040f:
+ repl = "D\xC5\xBD"/* "DŽ" */; break;
+ default:
+ abort ();
+ }
+ }
+
+ /* Use the replacement. */
+ *op++ = *repl++;
+ if (*repl != '\0')
+ {
+ *op++ = *repl++;
+ if (*repl != '\0')
+ {
+ *op++ = *repl++;
+ /* All replacements have at most 3 bytes. */
+ if (*repl != '\0')
+ abort ();
+ }
+ }
+ ip += 2;
+ continue;
+ }
+ }
+ }
+ }
+ *op++ = *ip++;
+ }
+
+ {
+ size_t output_len = op - output;
+
+ /* Verify that the allocated size was not exceeded. */
+ if (output_len > allocated)
+ abort ();
+ /* Shrink the result. */
+ if (output_len < allocated)
+ output = (char *) xrealloc (output, output_len);
+
+ /* Done. */
+ *output_p = output;
+ *output_len_p = output_len;
+ }
+}
diff --git a/gettext-tools/src/filters.h b/gettext-tools/src/filters.h
new file mode 100644
index 0000000..7c6d90f
--- /dev/null
+++ b/gettext-tools/src/filters.h
@@ -0,0 +1,51 @@
+/* Recoding functions.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Convert a string INPUT of INPUT_LEN bytes containing Serbian input
+ to Latin script (not Latin language :-)), converting Cyrillic letters to
+ Latin letters.
+ Store the freshly allocated result in *OUTPUT_P and its length (in bytes)
+ in *OUTPUT_LEN_P.
+ Input and output are in UTF-8 encoding. */
+extern void serbian_to_latin (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p);
+
+/* Convert a string INPUT of INPUT_LEN bytes, converting ASCII
+ quotations to Unicode quotations.
+ Store the freshly allocated result in *OUTPUT_P and its length (in bytes)
+ in *OUTPUT_LEN_P.
+ Input and output are in UTF-8 encoding. */
+extern void ascii_quote_to_unicode (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p);
+
+/* Convert a string INPUT of INPUT_LEN bytes, converting ASCII
+ quotations to Unicode quotations, adding bold escape sequence.
+ Store the freshly allocated result in *OUTPUT_P and its length (in bytes)
+ in *OUTPUT_LEN_P.
+ Input and output are in UTF-8 encoding. */
+extern void ascii_quote_to_unicode_bold (const char *input, size_t input_len,
+ char **output_p, size_t *output_len_p);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/gettext-tools/src/format-awk.c b/gettext-tools/src/format-awk.c
new file mode 100644
index 0000000..91251c4
--- /dev/null
+++ b/gettext-tools/src/format-awk.c
@@ -0,0 +1,663 @@
+/* awk format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* awk format strings are described in the gawk-3.1 documentation and
+ implemented in gawk-3.1.0/builtin.c: format_tree().
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
+ each of which acts as a flag,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or '*m$' or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or '*m$' or a nonempty digit sequence,
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that need a character argument,
+ - 's', that need a string argument,
+ - 'i', 'd', that need a signed integer argument,
+ - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
+ - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
+ Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
+ be used in the same string.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_CHARACTER,
+ FAT_STRING,
+ FAT_INTEGER,
+ FAT_UNSIGNED_INTEGER,
+ FAT_FLOAT
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ unnumbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ unsigned int number = 0;
+ enum format_arg_type type;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags. */
+ while (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0')
+ format++;
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ unsigned int width_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_WIDTH_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ width_number = m;
+ format = ++f;
+ }
+ }
+
+ if (width_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = width_number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ unsigned int precision_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_PRECISION_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ precision_number = m;
+ format = ++f;
+ }
+ }
+
+ if (precision_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = precision_number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case 'i': case 'd':
+ type = FAT_INTEGER;
+ break;
+ case 'u': case 'o': case 'x': case 'X':
+ type = FAT_UNSIGNED_INTEGER;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ type = FAT_FLOAT;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (type != FAT_NONE)
+ {
+ if (number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = type;
+ unnumbered_arg_count++;
+ }
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Convert the unnumbered argument array to numbered arguments. */
+ if (unnumbered_arg_count > 0)
+ spec.numbered_arg_count = unnumbered_arg_count;
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ else if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_awk =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_UNSIGNED_INTEGER:
+ printf ("[unsigned]i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-awk.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-boost.c b/gettext-tools/src/format-boost.c
new file mode 100644
index 0000000..688ba97
--- /dev/null
+++ b/gettext-tools/src/format-boost.c
@@ -0,0 +1,772 @@
+/* Boost format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Boost format strings are described in
+ boost_1_33_1/libs/format/doc/format.html
+ and implemented in
+ boost_1_33_1/boost/format/parsing.hpp.
+ A directive (other than '%%')
+ - starts with '%' or '%|'; in the latter case it must end in '|',
+ - is continued either by
+ - 'm%' where m is a positive integer, starting with a nonzero digit;
+ in this case the directive must not have started with '%|'; or
+ - the following:
+ - optional: 'm$' where m is a positive integer, starting with a
+ nonzero digit,
+ - optional: any of the characters '#', '0', '-', ' ', '+', "'",
+ '_', '=', 'h', 'l',
+ - optional: a width specification: '*' (reads an argument) or '*m$'
+ or a nonempty digit sequence,
+ - optional: a '.' and a precision specification: '*' (reads an
+ argument) or '*m$' or a nonempty digit sequence,
+ - optional: any of the characters 'h', 'l', 'L',
+ - if the directive started with '%|':
+ an optional specifier and a final '|',
+ otherwise
+ a mandatory specifier.
+ If no specifier is given, it needs an argument of any type.
+ The possible specifiers are:
+ - 'c', 'C', that need a character argument,
+ - 's', 'S', that need an argument of any type,
+ - 'i', 'd', 'o', 'u', 'x', 'X', that need an integer argument,
+ - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
+ - 'p', that needs a 'void *' argument,
+ - 't', that doesn't need an argument,
+ - 'TX', where X is any character, that doesn't need an argument,
+ - 'n', that needs a pointer to integer.
+ The Boost format string interpreter doesn't actually care about
+ the argument types, but we do, because it increases the likelihood
+ of detecting translator mistakes.
+ Numbered ('%m%' or '%m$' or '*m$') and unnumbered argument specifications
+ cannot be used in the same string.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE = 0,
+ /* Basic types */
+ FAT_INTEGER = 1,
+ FAT_DOUBLE = 2,
+ FAT_CHAR = 3,
+ FAT_POINTER = 4,
+ FAT_ANY = 5
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ unnumbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format == '%')
+ format++;
+ else
+ {
+ bool brackets = false;
+ bool done = false;
+ unsigned int number = 0;
+ enum format_arg_type type = FAT_NONE;
+
+ if (*format == '|')
+ {
+ format++;
+ brackets = true;
+ }
+
+ if (isdigit (*format) && *format != '0')
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if ((!brackets && *f == '%') || *f == '$')
+ {
+ if (m == 0) /* can happen if m overflows */
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ if (*f == '%')
+ {
+ type = FAT_ANY;
+ done = true;
+ }
+ format = ++f;
+ }
+ }
+
+ if (!done)
+ {
+ /* Parse flags. */
+ for (;;)
+ {
+ if (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0' || *format == '\''
+ || *format == '_' || *format == '=' || *format == 'h'
+ || *format == 'l')
+ format++;
+ else
+ break;
+ }
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ unsigned int width_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_WIDTH_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ width_number = m;
+ format = ++f;
+ }
+ }
+
+ if (width_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are
+ exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason =
+ INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = width_number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are
+ exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason =
+ INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ unsigned int precision_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_PRECISION_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ precision_number = m;
+ format = ++f;
+ }
+ }
+
+ if (precision_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are
+ exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason =
+ INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = precision_number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are
+ exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason =
+ INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ /* Parse size. */
+ for (;;)
+ {
+ if (*format == 'h' || *format == 'l' || *format == 'L')
+ format++;
+ else
+ break;
+ }
+
+ switch (*format++)
+ {
+ case 'c': case 'C':
+ type = FAT_CHAR;
+ break;
+ case 's': case 'S':
+ type = FAT_ANY;
+ break;
+ case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
+ type = FAT_INTEGER;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ type = FAT_DOUBLE;
+ break;
+ case 'p':
+ type = FAT_POINTER;
+ break;
+ case 't':
+ type = FAT_NONE;
+ break;
+ case 'T':
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+ type = FAT_NONE;
+ break;
+ case 'n':
+ type = FAT_NONE;
+ break;
+ case '|':
+ if (brackets)
+ {
+ --format;
+ type = FAT_ANY;
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ --format;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives,
+ *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ if (brackets)
+ {
+ if (*format != '|')
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("The directive number %u starts with | but does not end with |."),
+ spec.directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ format++;
+ }
+ }
+
+ if (type != FAT_NONE)
+ {
+ if (number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = type;
+ unnumbered_arg_count++;
+ }
+ }
+ }
+
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+
+ /* Convert the unnumbered argument array to numbered arguments. */
+ if (unnumbered_arg_count > 0)
+ spec.numbered_arg_count = unnumbered_arg_count;
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ else if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2 || type2 == FAT_ANY)
+ type_both = type1;
+ else if (type1 == FAT_ANY)
+ type_both = type2;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_boost =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_DOUBLE:
+ printf ("f");
+ break;
+ case FAT_CHAR:
+ printf ("c");
+ break;
+ case FAT_POINTER:
+ printf ("p");
+ break;
+ case FAT_ANY:
+ printf ("*");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-boost.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
+
diff --git a/gettext-tools/src/format-c-parse.h b/gettext-tools/src/format-c-parse.h
new file mode 100644
index 0000000..bacfcef
--- /dev/null
+++ b/gettext-tools/src/format-c-parse.h
@@ -0,0 +1,854 @@
+/* Parsing C format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009-2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+/* C format strings are described in POSIX (IEEE P1003.1 2001), section
+ XSH 3 fprintf(). See also Linux fprintf(3) manual page.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
+ "'", or - only in msgstr strings - the string "I", each of which acts as
+ a flag,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or '*m$' or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or '*m$' or a nonempty digit sequence,
+ - is either continued like this:
+ - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll'
+ 'L' 'q' 'j' 'z' 't',
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', 'C', that need a character argument,
+ - 's', 'S', that need a string argument,
+ - 'i', 'd', that need a signed integer argument,
+ - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
+ - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point
+ argument,
+ - 'p', that needs a 'void *' argument,
+ - 'n', that needs a pointer to integer.
+ or is finished by a specifier '<' inttypes-macro '>' where inttypes-macro
+ is an ISO C 99 section 7.8.1 format directive.
+ Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
+ be used in the same string. When numbered argument specifications are
+ used, specifying the Nth argument requires that all the leading arguments,
+ from the first to the (N-1)th, are specified in the format string.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE = 0,
+ /* Basic types */
+ FAT_INTEGER = 1,
+ FAT_DOUBLE = 2,
+ FAT_CHAR = 3,
+ FAT_STRING = 4,
+ FAT_OBJC_OBJECT = 5,
+ FAT_POINTER = 6,
+ FAT_COUNT_POINTER = 7,
+ /* Flags */
+ FAT_UNSIGNED = 1 << 3,
+ FAT_SIZE_SHORT = 1 << 4,
+ FAT_SIZE_CHAR = 2 << 4,
+ FAT_SIZE_LONG = 1 << 6,
+ FAT_SIZE_LONGLONG = 2 << 6,
+ FAT_SIZE_8_T = 1 << 8,
+ FAT_SIZE_16_T = 1 << 9,
+ FAT_SIZE_32_T = 1 << 10,
+ FAT_SIZE_64_T = 1 << 11,
+ FAT_SIZE_LEAST8_T = 1 << 12,
+ FAT_SIZE_LEAST16_T = 1 << 13,
+ FAT_SIZE_LEAST32_T = 1 << 14,
+ FAT_SIZE_LEAST64_T = 1 << 15,
+ FAT_SIZE_FAST8_T = 1 << 16,
+ FAT_SIZE_FAST16_T = 1 << 17,
+ FAT_SIZE_FAST32_T = 1 << 18,
+ FAT_SIZE_FAST64_T = 1 << 19,
+ FAT_SIZE_INTMAX_T = 1 << 20,
+ FAT_SIZE_INTPTR_T = 1 << 21,
+ FAT_SIZE_SIZE_T = 1 << 22,
+ FAT_SIZE_PTRDIFF_T = 1 << 23,
+ FAT_WIDE = FAT_SIZE_LONG,
+ /* Meaningful combinations of basic types and flags:
+ 'signed char' = FAT_INTEGER | FAT_SIZE_CHAR,
+ 'unsigned char' = FAT_INTEGER | FAT_SIZE_CHAR | FAT_UNSIGNED,
+ 'short' = FAT_INTEGER | FAT_SIZE_SHORT,
+ 'unsigned short' = FAT_INTEGER | FAT_SIZE_SHORT | FAT_UNSIGNED,
+ 'int' = FAT_INTEGER,
+ 'unsigned int' = FAT_INTEGER | FAT_UNSIGNED,
+ 'long int' = FAT_INTEGER | FAT_SIZE_LONG,
+ 'unsigned long int' = FAT_INTEGER | FAT_SIZE_LONG | FAT_UNSIGNED,
+ 'long long int' = FAT_INTEGER | FAT_SIZE_LONGLONG,
+ 'unsigned long long int' = FAT_INTEGER | FAT_SIZE_LONGLONG | FAT_UNSIGNED,
+ 'double' = FAT_DOUBLE,
+ 'long double' = FAT_DOUBLE | FAT_SIZE_LONGLONG,
+ 'char'/'int' = FAT_CHAR,
+ 'wchar_t'/'wint_t' = FAT_CHAR | FAT_SIZE_LONG,
+ 'const char *' = FAT_STRING,
+ 'const wchar_t *' = FAT_STRING | FAT_SIZE_LONG,
+ 'void *' = FAT_POINTER,
+ FAT_COUNT_SCHAR_POINTER = FAT_COUNT_POINTER | FAT_SIZE_CHAR,
+ FAT_COUNT_SHORT_POINTER = FAT_COUNT_POINTER | FAT_SIZE_SHORT,
+ FAT_COUNT_INT_POINTER = FAT_COUNT_POINTER,
+ FAT_COUNT_LONGINT_POINTER = FAT_COUNT_POINTER | FAT_SIZE_LONG,
+ FAT_COUNT_LONGLONGINT_POINTER = FAT_COUNT_POINTER | FAT_SIZE_LONGLONG,
+ */
+ /* Bitmasks */
+ FAT_BASIC_MASK = (FAT_INTEGER | FAT_DOUBLE | FAT_CHAR | FAT_STRING
+ | FAT_OBJC_OBJECT | FAT_POINTER | FAT_COUNT_POINTER),
+ FAT_SIZE_MASK = (FAT_SIZE_SHORT | FAT_SIZE_CHAR
+ | FAT_SIZE_LONG | FAT_SIZE_LONGLONG
+ | FAT_SIZE_8_T | FAT_SIZE_16_T
+ | FAT_SIZE_32_T | FAT_SIZE_64_T
+ | FAT_SIZE_LEAST8_T | FAT_SIZE_LEAST16_T
+ | FAT_SIZE_LEAST32_T | FAT_SIZE_LEAST64_T
+ | FAT_SIZE_FAST8_T | FAT_SIZE_FAST16_T
+ | FAT_SIZE_FAST32_T | FAT_SIZE_FAST64_T
+ | FAT_SIZE_INTMAX_T | FAT_SIZE_INTPTR_T
+ | FAT_SIZE_SIZE_T | FAT_SIZE_PTRDIFF_T)
+};
+#ifdef __cplusplus
+typedef int format_arg_type_t;
+#else
+typedef enum format_arg_type format_arg_type_t;
+#endif
+
+struct numbered_arg
+{
+ unsigned int number;
+ format_arg_type_t type;
+};
+
+struct unnumbered_arg
+{
+ format_arg_type_t type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int unnumbered_arg_count;
+ unsigned int allocated;
+ struct unnumbered_arg *unnumbered;
+ bool unlikely_intentional;
+ unsigned int sysdep_directives_count;
+ const char **sysdep_directives;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+/* Whether to recognize the 'I' flag. */
+#if SYSDEP_SEGMENTS_PROCESSED
+/* The 'I' flag can only occur in glibc >= 2.2. On other platforms, gettext()
+ filters it away even if it is present in the msgstr in the .mo file. */
+# define HANDLE_I_FLAG \
+ ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) \
+ && !defined __UCLIBC__)
+#else
+# define HANDLE_I_FLAG 1
+#endif
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static struct spec *
+format_parse_entrails (const char *format, bool translated,
+ bool objc_extensions, char *fdi, char **invalid_reason,
+ struct spec *result)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ unsigned int numbered_arg_count;
+ struct numbered_arg *numbered;
+
+ spec.directives = 0;
+ numbered_arg_count = 0;
+ spec.unnumbered_arg_count = 0;
+ spec.allocated = 0;
+ numbered = NULL;
+ spec.unnumbered = NULL;
+ spec.unlikely_intentional = false;
+ spec.sysdep_directives_count = 0;
+ spec.sysdep_directives = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ unsigned int number = 0;
+ format_arg_type_t type;
+ format_arg_type_t size;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags. */
+ for (;;)
+ {
+ if (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0' || *format == '\'')
+ format++;
+#if HANDLE_I_FLAG
+ else if (translated && *format == 'I')
+ {
+ spec.sysdep_directives =
+ (const char **)
+ xrealloc (spec.sysdep_directives,
+ 2 * (spec.sysdep_directives_count + 1)
+ * sizeof (const char *));
+ IF_OOM (spec.sysdep_directives, goto bad_format;)
+ spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;
+ spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
+ spec.sysdep_directives_count++;
+ format++;
+ }
+#endif
+ else
+ break;
+ }
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ unsigned int width_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_WIDTH_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ width_number = m;
+ format = ++f;
+ }
+ }
+
+ if (width_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
+ IF_OOM (numbered, goto bad_format;)
+ }
+ numbered[numbered_arg_count].number = width_number;
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
+ IF_OOM (spec.unnumbered, goto bad_format;)
+ }
+ spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
+ spec.unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ unsigned int precision_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason =
+ INVALID_PRECISION_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ precision_number = m;
+ format = ++f;
+ }
+ }
+
+ if (precision_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
+ IF_OOM (numbered, goto bad_format;)
+ }
+ numbered[numbered_arg_count].number = precision_number;
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
+ IF_OOM (spec.unnumbered, goto bad_format;)
+ }
+ spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
+ spec.unnumbered_arg_count++;
+ }
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ if (!SYSDEP_SEGMENTS_PROCESSED && *format == '<')
+ {
+ spec.sysdep_directives =
+ (const char **)
+ xrealloc (spec.sysdep_directives,
+ 2 * (spec.sysdep_directives_count + 1)
+ * sizeof (const char *));
+ IF_OOM (spec.sysdep_directives, goto bad_format;)
+ spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;
+
+ format++;
+ /* Parse ISO C 99 section 7.8.1 format string directive.
+ Syntax:
+ P R I { d | i | o | u | x | X }
+ { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */
+ if (*format != 'P')
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+ if (*format != 'R')
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+ if (*format != 'I')
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+
+ switch (*format)
+ {
+ case 'i': case 'd':
+ type = FAT_INTEGER;
+ break;
+ case 'u': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER | FAT_UNSIGNED;
+ break;
+ default:
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+
+ if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X')
+ {
+ type |= FAT_SIZE_INTMAX_T;
+ format += 3;
+ }
+ else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R')
+ {
+ type |= FAT_SIZE_INTPTR_T;
+ format += 3;
+ }
+ else
+ {
+ if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A'
+ && format[3] == 'S' && format[4] == 'T')
+ {
+ format += 5;
+ if (format[0] == '8')
+ {
+ type |= FAT_SIZE_LEAST8_T;
+ format++;
+ }
+ else if (format[0] == '1' && format[1] == '6')
+ {
+ type |= FAT_SIZE_LEAST16_T;
+ format += 2;
+ }
+ else if (format[0] == '3' && format[1] == '2')
+ {
+ type |= FAT_SIZE_LEAST32_T;
+ format += 2;
+ }
+ else if (format[0] == '6' && format[1] == '4')
+ {
+ type |= FAT_SIZE_LEAST64_T;
+ format += 2;
+ }
+ else
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format,
+ FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ else if (format[0] == 'F' && format[1] == 'A'
+ && format[2] == 'S' && format[3] == 'T')
+ {
+ format += 4;
+ if (format[0] == '8')
+ {
+ type |= FAT_SIZE_FAST8_T;
+ format++;
+ }
+ else if (format[0] == '1' && format[1] == '6')
+ {
+ type |= FAT_SIZE_FAST16_T;
+ format += 2;
+ }
+ else if (format[0] == '3' && format[1] == '2')
+ {
+ type |= FAT_SIZE_FAST32_T;
+ format += 2;
+ }
+ else if (format[0] == '6' && format[1] == '4')
+ {
+ type |= FAT_SIZE_FAST64_T;
+ format += 2;
+ }
+ else
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format,
+ FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ else
+ {
+ if (format[0] == '8')
+ {
+ type |= FAT_SIZE_8_T;
+ format++;
+ }
+ else if (format[0] == '1' && format[1] == '6')
+ {
+ type |= FAT_SIZE_16_T;
+ format += 2;
+ }
+ else if (format[0] == '3' && format[1] == '2')
+ {
+ type |= FAT_SIZE_32_T;
+ format += 2;
+ }
+ else if (format[0] == '6' && format[1] == '4')
+ {
+ type |= FAT_SIZE_64_T;
+ format += 2;
+ }
+ else
+ {
+ *invalid_reason = INVALID_C99_MACRO (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format,
+ FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ }
+
+ if (*format != '>')
+ {
+ *invalid_reason = INVALID_ANGLE_BRACKET (spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
+ spec.sysdep_directives_count++;
+ }
+ else
+ {
+ /* Parse size. */
+ size = 0;
+ for (;; format++)
+ {
+ if (*format == 'h')
+ {
+ if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR))
+ size = FAT_SIZE_CHAR;
+ else
+ size = FAT_SIZE_SHORT;
+ }
+ else if (*format == 'l')
+ {
+ if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG))
+ size = FAT_SIZE_LONGLONG;
+ else
+ size = FAT_SIZE_LONG;
+ }
+ else if (*format == 'L')
+ size = FAT_SIZE_LONGLONG;
+ else if (*format == 'q')
+ /* Old BSD 4.4 convention. */
+ size = FAT_SIZE_LONGLONG;
+ else if (*format == 'j')
+ size = FAT_SIZE_INTMAX_T;
+ else if (*format == 'z' || *format == 'Z')
+ /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
+ because the warning facility in gcc-2.95.2 understands
+ only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
+ size = FAT_SIZE_SIZE_T;
+ else if (*format == 't')
+ size = FAT_SIZE_PTRDIFF_T;
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ else if (SYSDEP_SEGMENTS_PROCESSED
+ && *format == 'I'
+ && format[1] == '6'
+ && format[2] == '4')
+ {
+ size = FAT_SIZE_64_T;
+ format += 2;
+ }
+#endif
+ else
+ break;
+ }
+
+ switch (*format)
+ {
+ case '%':
+ /* Programmers writing _("%2%") most often will not want to
+ use this string as a c-format string, but rather as a
+ literal or as a different kind of format string. */
+ if (format[-1] != '%')
+ spec.unlikely_intentional = true;
+ type = FAT_NONE;
+ break;
+ case 'm': /* glibc extension */
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHAR;
+ type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
+ ? FAT_WIDE : 0);
+ break;
+ case 'C': /* obsolete */
+ type = FAT_CHAR | FAT_WIDE;
+ break;
+ case 's':
+ type = FAT_STRING;
+ type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
+ ? FAT_WIDE : 0);
+ break;
+ case 'S': /* obsolete */
+ type = FAT_STRING | FAT_WIDE;
+ break;
+ case 'i': case 'd':
+ type = FAT_INTEGER;
+ type |= (size & FAT_SIZE_MASK);
+ break;
+ case 'u': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER | FAT_UNSIGNED;
+ type |= (size & FAT_SIZE_MASK);
+ break;
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ case 'a': case 'A':
+ type = FAT_DOUBLE;
+ type |= (size & FAT_SIZE_LONGLONG);
+ break;
+ case '@':
+ if (objc_extensions)
+ {
+ type = FAT_OBJC_OBJECT;
+ break;
+ }
+ goto other;
+ case 'p':
+ type = FAT_POINTER;
+ break;
+ case 'n':
+ type = FAT_COUNT_POINTER;
+ type |= (size & FAT_SIZE_MASK);
+ break;
+ other:
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ }
+
+ if (type != FAT_NONE)
+ {
+ if (number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
+ IF_OOM (numbered, goto bad_format;)
+ }
+ numbered[numbered_arg_count].number = number;
+ numbered[numbered_arg_count].type = type;
+ numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
+ IF_OOM (spec.unnumbered, goto bad_format;)
+ }
+ spec.unnumbered[spec.unnumbered_arg_count].type = type;
+ spec.unnumbered_arg_count++;
+ }
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (numbered, numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < numbered_arg_count; i++)
+ if (j > 0 && numbered[i].number == numbered[j-1].number)
+ {
+ format_arg_type_t type1 = numbered[i].type;
+ format_arg_type_t type2 = numbered[j-1].type;
+ format_arg_type_t type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+ err = true;
+ }
+
+ numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ numbered[j].number = numbered[i].number;
+ numbered[j].type = numbered[i].type;
+ }
+ j++;
+ }
+ numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ /* Verify that the format strings uses all arguments up to the highest
+ numbered one. */
+ if (numbered_arg_count > 0)
+ {
+ unsigned int i;
+
+ for (i = 0; i < numbered_arg_count; i++)
+ if (numbered[i].number != i + 1)
+ {
+ *invalid_reason = INVALID_IGNORED_ARGUMENT (numbered[i].number, i + 1);
+ goto bad_format;
+ }
+
+ /* So now the numbered arguments array is equivalent to a sequence
+ of unnumbered arguments. */
+ spec.unnumbered_arg_count = numbered_arg_count;
+ spec.allocated = spec.unnumbered_arg_count;
+ spec.unnumbered = XNMALLOC (spec.allocated, struct unnumbered_arg);
+ IF_OOM (spec.unnumbered, goto bad_format;)
+ for (i = 0; i < spec.unnumbered_arg_count; i++)
+ spec.unnumbered[i].type = numbered[i].type;
+ free (numbered);
+ numbered_arg_count = 0;
+ }
+
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (numbered != NULL)
+ free (numbered);
+ if (spec.unnumbered != NULL)
+ free (spec.unnumbered);
+ if (spec.sysdep_directives != NULL)
+ free (spec.sysdep_directives);
+ return NULL;
+}
diff --git a/gettext-tools/src/format-c.c b/gettext-tools/src/format-c.c
new file mode 100644
index 0000000..d88f8de
--- /dev/null
+++ b/gettext-tools/src/format-c.c
@@ -0,0 +1,376 @@
+/* C format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#include "format-invalid.h"
+
+#define INVALID_C99_MACRO(directive_number) \
+ xasprintf (_("In the directive number %u, the token after '<' is not the name of a format specifier macro. The valid macro names are listed in ISO C 99 section 7.8.1."), directive_number)
+
+#define INVALID_ANGLE_BRACKET(directive_number) \
+ xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), directive_number)
+
+#define INVALID_IGNORED_ARGUMENT(referenced_arg, ignored_arg) \
+ xasprintf (_("The string refers to argument number %u but ignores argument number %u."), referenced_arg, ignored_arg)
+
+/* Execute statement if memory allocation function returned NULL. */
+#define IF_OOM(allocated_ptr, statement) /* nothing, since we use xalloc.h */
+
+/* Specifies whether the system dependent segments in msgid and msgstr have
+ been processed. This means:
+ - If false, ISO C 99 <inttypes.h> directives are denoted with angle
+ brackets. If true, they have already been expanded, leading in
+ particular to %I64d directives on native Windows platforms.
+ - If false, the 'I' flag may be present in msgstr (also on platforms
+ other than glibc). If true, the 'I' directive may be present in msgstr
+ only on glibc >= 2.2 platforms. */
+#define SYSDEP_SEGMENTS_PROCESSED false
+
+/* Include the bulk of the C format string parsing code. */
+#include "format-c-parse.h"
+
+static void *
+format_parse (const char *format, bool translated, bool objc_extensions,
+ char *fdi, char **invalid_reason)
+{
+ struct spec result_buf;
+ struct spec *result;
+
+ result = format_parse_entrails (format, translated, objc_extensions, fdi, invalid_reason, &result_buf);
+
+ if (result != NULL)
+ {
+ /* Copy the result to a heap-allocated object. */
+ struct spec *safe_result = XMALLOC (struct spec);
+ *safe_result = *result;
+ result = safe_result;
+ }
+ return result;
+}
+
+static void *
+format_c_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ return format_parse (format, translated, false, fdi, invalid_reason);
+}
+
+static void *
+format_objc_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ return format_parse (format, translated, true, fdi, invalid_reason);
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->unnumbered != NULL)
+ free (spec->unnumbered);
+ if (spec->sysdep_directives != NULL)
+ free (spec->sysdep_directives);
+ free (spec);
+}
+
+static bool
+format_is_unlikely_intentional (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->unlikely_intentional;
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+ unsigned int i;
+
+ /* Check the argument types are the same. */
+ if (equality
+ ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
+ : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else
+ for (i = 0; i < spec2->unnumbered_arg_count; i++)
+ if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr, i + 1);
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_c =
+{
+ format_c_parse,
+ format_free,
+ format_get_number_of_directives,
+ format_is_unlikely_intentional,
+ format_check
+};
+
+
+struct formatstring_parser formatstring_objc =
+{
+ format_objc_parse,
+ format_free,
+ format_get_number_of_directives,
+ format_is_unlikely_intentional,
+ format_check
+};
+
+
+void
+get_sysdep_c_format_directives (const char *string, bool translated,
+ struct interval **intervalsp, size_t *lengthp)
+{
+ /* Parse the format string with all possible extensions turned on. (The
+ caller has already verified that the format string is valid for the
+ particular language.) */
+ char *invalid_reason = NULL;
+ struct spec *descr =
+ (struct spec *)
+ format_parse (string, translated, true, NULL, &invalid_reason);
+
+ if (descr != NULL && descr->sysdep_directives_count > 0)
+ {
+ unsigned int n = descr->sysdep_directives_count;
+ struct interval *intervals = XNMALLOC (n, struct interval);
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ {
+ intervals[i].startpos = descr->sysdep_directives[2 * i] - string;
+ intervals[i].endpos = descr->sysdep_directives[2 * i + 1] - string;
+ }
+ *intervalsp = intervals;
+ *lengthp = n;
+ }
+ else
+ {
+ *intervalsp = NULL;
+ *lengthp = 0;
+ }
+
+ if (descr != NULL)
+ format_free (descr);
+ else
+ free (invalid_reason);
+}
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->unnumbered_arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ if (spec->unnumbered[i].type & FAT_UNSIGNED)
+ printf ("[unsigned]");
+ switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
+ {
+ case 0:
+ break;
+ case FAT_SIZE_SHORT:
+ printf ("[short]");
+ break;
+ case FAT_SIZE_CHAR:
+ printf ("[char]");
+ break;
+ case FAT_SIZE_LONG:
+ printf ("[long]");
+ break;
+ case FAT_SIZE_LONGLONG:
+ printf ("[long long]");
+ break;
+ case FAT_SIZE_8_T:
+ printf ("[int8_t]");
+ break;
+ case FAT_SIZE_16_T:
+ printf ("[int16_t]");
+ break;
+ case FAT_SIZE_32_T:
+ printf ("[int32_t]");
+ break;
+ case FAT_SIZE_64_T:
+ printf ("[int64_t]");
+ break;
+ case FAT_SIZE_LEAST8_T:
+ printf ("[int_least8_t]");
+ break;
+ case FAT_SIZE_LEAST16_T:
+ printf ("[int_least16_t]");
+ break;
+ case FAT_SIZE_LEAST32_T:
+ printf ("[int_least32_t]");
+ break;
+ case FAT_SIZE_LEAST64_T:
+ printf ("[int_least64_t]");
+ break;
+ case FAT_SIZE_FAST8_T:
+ printf ("[int_fast8_t]");
+ break;
+ case FAT_SIZE_FAST16_T:
+ printf ("[int_fast16_t]");
+ break;
+ case FAT_SIZE_FAST32_T:
+ printf ("[int_fast32_t]");
+ break;
+ case FAT_SIZE_FAST64_T:
+ printf ("[int_fast64_t]");
+ break;
+ case FAT_SIZE_INTMAX_T:
+ printf ("[intmax_t]");
+ break;
+ case FAT_SIZE_INTPTR_T:
+ printf ("[intptr_t]");
+ break;
+ case FAT_SIZE_SIZE_T:
+ printf ("[size_t]");
+ break;
+ case FAT_SIZE_PTRDIFF_T:
+ printf ("[ptrdiff_t]");
+ break;
+ default:
+ abort ();
+ }
+ switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_DOUBLE:
+ printf ("f");
+ break;
+ case FAT_CHAR:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_OBJC_OBJECT:
+ printf ("@");
+ break;
+ case FAT_POINTER:
+ printf ("p");
+ break;
+ case FAT_COUNT_POINTER:
+ printf ("n");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_c_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-c.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-csharp.c b/gettext-tools/src/format-csharp.c
new file mode 100644
index 0000000..feb3fcf
--- /dev/null
+++ b/gettext-tools/src/format-csharp.c
@@ -0,0 +1,293 @@
+/* C# format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* C# format strings are described in the description of the .NET System.String
+ class and implemented in
+ pnetlib-0.5.6/runtime/System/String.cs
+ and
+ mcs-0.28/class/corlib/System/String.cs
+ A format string consists of literal text (that is output verbatim), doubled
+ braces ('{{' and '}}', that lead to a single brace when output), and
+ directives.
+ A directive
+ - starts with '{',
+ - is followed by a nonnegative integer m,
+ - is optionally followed by ',' and an integer denoting a width,
+ - is optionally followed by ':' and a sequence of format specifiers.
+ (But the interpretation of the format specifiers is up to the IFormattable
+ implementation, depending on the argument's runtime value. New classes
+ implementing IFormattable can be defined by the user.)
+ - is finished with '}'.
+ */
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+};
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ {
+ char c = *format++;
+
+ if (c == '{')
+ {
+ FDI_SET (format - 1, FMTDIR_START);
+ if (*format == '{')
+ format++;
+ else
+ {
+ /* A directive. */
+ unsigned int number;
+
+ spec.directives++;
+
+ if (!c_isdigit (*format))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ return NULL;
+ }
+ number = 0;
+ do
+ {
+ number = 10 * number + (*format - '0');
+ format++;
+ }
+ while (c_isdigit (*format));
+
+ if (*format == ',')
+ {
+ /* Parse width. */
+ format++;
+ if (*format == '-')
+ format++;
+ if (!c_isdigit (*format))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, ',' is not followed by a number."), spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format,
+ FMTDIR_ERROR);
+ return NULL;
+ }
+ do
+ format++;
+ while (c_isdigit (*format));
+ }
+
+ if (*format == ':')
+ {
+ /* Parse format specifiers. */
+ do
+ format++;
+ while (*format != '\0' && *format != '}');
+ }
+
+ if (*format == '\0')
+ {
+ *invalid_reason =
+ xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return NULL;
+ }
+
+ if (*format != '}')
+ {
+ *invalid_reason =
+ (c_isprint (*format)
+ ? xasprintf (_("The directive number %u ends with an invalid character '%c' instead of '}'."), spec.directives, *format)
+ : xasprintf (_("The directive number %u ends with an invalid character instead of '}'."), spec.directives));
+ FDI_SET (format, FMTDIR_ERROR);
+ return NULL;
+ }
+
+ format++;
+
+ if (spec.numbered_arg_count <= number)
+ spec.numbered_arg_count = number + 1;
+ }
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+ else if (c == '}')
+ {
+ FDI_SET (format - 1, FMTDIR_START);
+ if (*format == '}')
+ format++;
+ else
+ {
+ *invalid_reason =
+ (spec.directives == 0
+ ? xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."))
+ : xasprintf (_("The string contains a lone '}' after directive number %u."), spec.directives));
+ FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
+ return NULL;
+ }
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ /* Check that the argument counts are the same. */
+ if (equality
+ ? spec1->numbered_arg_count != spec2->numbered_arg_count
+ : spec1->numbered_arg_count < spec2->numbered_arg_count)
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_csharp =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ printf ("*");
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-csharp.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-elisp.c b/gettext-tools/src/format-elisp.c
new file mode 100644
index 0000000..2e43fde
--- /dev/null
+++ b/gettext-tools/src/format-elisp.c
@@ -0,0 +1,502 @@
+/* Emacs Lisp format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Emacs Lisp format strings are implemented in emacs-21.1/src/editfns.c,
+ xemacs-21.1.14/src/editfns.c and xemacs-21.1.14/src/doprnt.c.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
+ each of which acts as a flag,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or a nonempty digit sequence,
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that need a character argument,
+ - 'd', 'i', 'x', 'X', 'o', that need an integer argument,
+ - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument,
+ - 's', that need an argument and prints it using princ,
+ - 'S', that need an argument and prints it using prin1.
+ Numbered ('%m$') and unnumbered argument specifications can be used in the
+ same string. The effect of '%m$' is to set the current argument number to
+ m. The current argument number is incremented after processing a directive.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_CHARACTER,
+ FAT_INTEGER,
+ FAT_FLOAT,
+ FAT_OBJECT_PRETTY,
+ FAT_OBJECT
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+ unsigned int number;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ number = 1;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ enum format_arg_type type;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$' && m > 0)
+ {
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags. */
+ while (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0')
+ format++;
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ format++;
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ format++;
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 'd': case 'i': case 'x': case 'X': case 'o':
+ type = FAT_INTEGER;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ type = FAT_FLOAT;
+ break;
+ case 's':
+ type = FAT_OBJECT_PRETTY;
+ break;
+ case 'S':
+ type = FAT_OBJECT;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (type != FAT_NONE)
+ {
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_elisp =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ case FAT_OBJECT_PRETTY:
+ printf ("s");
+ break;
+ case FAT_OBJECT:
+ printf ("*");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-elisp.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-gcc-internal.c b/gettext-tools/src/format-gcc-internal.c
new file mode 100644
index 0000000..7057628
--- /dev/null
+++ b/gettext-tools/src/format-gcc-internal.c
@@ -0,0 +1,852 @@
+/* GCC internal format strings.
+ Copyright (C) 2003-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* GCC internal format strings consist of language frontend independent
+ format directives, implemented in gcc-4.3.0/gcc/pretty-print.c (function
+ pp_base_format), plus some frontend dependent extensions:
+ - for the C/ObjC frontend
+ in gcc-4.3.0/gcc/c-objc-common.c (function c_tree_printer)
+ - for the C++ frontend
+ in gcc-4.3.0/gcc/cp/error.c (function cp_printer)
+ Taking these together, GCC internal format strings are specified as follows.
+
+ A directive
+ - starts with '%',
+ - either is finished by one of these:
+ - '%', '<', '>', "'", that need no argument,
+ - 'm', that needs no argument but looks at an err_no variable,
+ - or is continued like this:
+ - optionally 'm$' where m is a positive integer,
+ - optionally any number of flags:
+ 'q' (once only),
+ 'l' (up to twice) or 'w' (once only) (exclusive),
+ '+' (once only),
+ '#' (once only),
+ - finished by a specifier
+
+ - 'c', that needs a character argument,
+ - 's', that needs a string argument,
+ - '.NNNs', where NNN is a nonempty digit sequence, that needs a
+ string argument,
+ - '.*NNN$s' where NNN is a positive integer and NNN = m - 1, that
+ needs a signed integer argument at position NNN and a string
+ argument,
+ - '.*s', that needs a signed integer argument and a string argument,
+ - 'i', 'd', that need a signed integer argument of the specified
+ size,
+ - 'o', 'u', 'x', that need an unsigned integer argument of the
+ specified size,
+ - 'p', that needs a 'void *' argument,
+ - 'H', that needs a 'location_t *' argument,
+ - 'J', that needs a general declaration argument,
+ - 'K', that needs a statement argument,
+ [see gcc/pretty-print.c]
+
+ - 'D', that needs a general declaration argument,
+ - 'F', that needs a function declaration argument,
+ - 'T', that needs a type argument,
+ - 'E', that needs an expression argument,
+ [see gcc/c-objc-common.c and gcc/cp/error.c]
+
+ - 'A', that needs a function argument list argument,
+ - 'C', that needs a tree code argument,
+ - 'L', that needs a language argument,
+ - 'O', that needs a binary operator argument,
+ - 'P', that needs a function parameter argument,
+ - 'Q', that needs an assignment operator argument,
+ - 'V', that needs a const/volatile qualifier argument.
+ [see gcc/cp/error.c]
+
+ Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
+ be used in the same string. */
+
+enum format_arg_type
+{
+ FAT_NONE = 0,
+ /* Basic types */
+ FAT_INTEGER = 1,
+ FAT_CHAR = 2,
+ FAT_STRING = 3,
+ FAT_POINTER = 4,
+ FAT_LOCATION = 5,
+ FAT_TREE = 6,
+ FAT_TREE_CODE = 7,
+ FAT_LANGUAGES = 8,
+ /* Flags */
+ FAT_UNSIGNED = 1 << 4,
+ FAT_SIZE_LONG = 1 << 5,
+ FAT_SIZE_LONGLONG = 2 << 5,
+ FAT_SIZE_WIDE = 3 << 5,
+ FAT_TREE_DECL = 1 << 7,
+ FAT_TREE_STATEMENT = 2 << 7,
+ FAT_TREE_FUNCDECL = 3 << 7,
+ FAT_TREE_TYPE = 4 << 7,
+ FAT_TREE_ARGUMENT = 5 << 7,
+ FAT_TREE_EXPRESSION = 6 << 7,
+ FAT_TREE_CV = 7 << 7,
+ FAT_TREE_CODE_BINOP = 1 << 10,
+ FAT_TREE_CODE_ASSOP = 2 << 10,
+ FAT_FUNCPARAM = 1 << 12,
+ /* Bitmasks */
+ FAT_SIZE_MASK = (FAT_SIZE_LONG | FAT_SIZE_LONGLONG | FAT_SIZE_WIDE)
+};
+#ifdef __cplusplus
+typedef int format_arg_type_t;
+#else
+typedef enum format_arg_type format_arg_type_t;
+#endif
+
+struct numbered_arg
+{
+ unsigned int number;
+ format_arg_type_t type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+ bool uses_err_no;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ spec.uses_err_no = false;
+ unnumbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format == '%' || *format == '<' || *format == '>'
+ || *format == '\'')
+ ;
+ else if (*format == 'm')
+ spec.uses_err_no = true;
+ else
+ {
+ unsigned int number = 0;
+ unsigned int flag_q = 0;
+ unsigned int flag_l = 0;
+ unsigned int flag_w = 0;
+ unsigned int flag_plus = 0;
+ unsigned int flag_sharp = 0;
+ format_arg_type_t size;
+ format_arg_type_t type;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags and size. */
+ for (;; format++)
+ {
+ switch (*format)
+ {
+ case 'q':
+ if (flag_q > 0)
+ goto invalid_flags;
+ flag_q = 1;
+ continue;
+ case 'l':
+ if (flag_l > 1 || flag_w)
+ goto invalid_flags;
+ flag_l++;
+ continue;
+ case 'w':
+ if (flag_w > 0 || flag_l)
+ goto invalid_flags;
+ flag_w = 1;
+ continue;
+ case '+':
+ if (flag_plus > 0)
+ goto invalid_flags;
+ flag_plus = 1;
+ continue;
+ case '#':
+ if (flag_sharp > 0)
+ goto invalid_flags;
+ flag_sharp = 1;
+ continue;
+ invalid_flags:
+ *invalid_reason = xasprintf (_("In the directive number %u, the flags combination is invalid."), spec.directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ default:
+ break;
+ }
+ break;
+ }
+ size = (flag_l == 2 ? FAT_SIZE_LONGLONG :
+ flag_l == 1 ? FAT_SIZE_LONG :
+ flag_w ? FAT_SIZE_WIDE :
+ 0);
+
+ if (*format == 'c')
+ type = FAT_CHAR;
+ else if (*format == 's')
+ type = FAT_STRING;
+ else if (*format == '.')
+ {
+ format++;
+
+ if (isdigit (*format))
+ {
+ do
+ format++;
+ while (isdigit (*format));
+
+ if (*format != 's')
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, a precision is not allowed before '%c'."), spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ type = FAT_STRING;
+ }
+ else if (*format == '*')
+ {
+ unsigned int precision_number = 0;
+
+ format++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ if (unnumbered_arg_count > 0 || number == 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ if (m != number - 1)
+ {
+ *invalid_reason = xasprintf (_("In the directive number %u, the argument number for the precision must be equal to %u."), spec.directives, number - 1);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ precision_number = m;
+ format = ++f;
+ }
+ }
+
+ if (precision_number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = precision_number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ }
+
+ if (*format == 's')
+ type = FAT_STRING;
+ else
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, a precision specification is not allowed before '%c'."), spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ }
+ else
+ {
+ *invalid_reason = xasprintf (_("In the directive number %u, the precision specification is invalid."), spec.directives);
+ FDI_SET (*format == '\0' ? format - 1 : format,
+ FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ else if (*format == 'i' || *format == 'd')
+ type = FAT_INTEGER | size;
+ else if (*format == 'o' || *format == 'u' || *format == 'x')
+ type = FAT_INTEGER | FAT_UNSIGNED | size;
+ else if (*format == 'p')
+ type = FAT_POINTER;
+ else if (*format == 'H')
+ type = FAT_LOCATION;
+ else if (*format == 'J')
+ type = FAT_TREE | FAT_TREE_DECL;
+ else if (*format == 'K')
+ type = FAT_TREE | FAT_TREE_STATEMENT;
+ else
+ {
+ if (*format == 'D')
+ type = FAT_TREE | FAT_TREE_DECL;
+ else if (*format == 'F')
+ type = FAT_TREE | FAT_TREE_FUNCDECL;
+ else if (*format == 'T')
+ type = FAT_TREE | FAT_TREE_TYPE;
+ else if (*format == 'E')
+ type = FAT_TREE | FAT_TREE_EXPRESSION;
+ else if (*format == 'A')
+ type = FAT_TREE | FAT_TREE_ARGUMENT;
+ else if (*format == 'C')
+ type = FAT_TREE_CODE;
+ else if (*format == 'L')
+ type = FAT_LANGUAGES;
+ else if (*format == 'O')
+ type = FAT_TREE_CODE | FAT_TREE_CODE_BINOP;
+ else if (*format == 'P')
+ type = FAT_INTEGER | FAT_FUNCPARAM;
+ else if (*format == 'Q')
+ type = FAT_TREE_CODE | FAT_TREE_CODE_ASSOP;
+ else if (*format == 'V')
+ type = FAT_TREE | FAT_TREE_CV;
+ else
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ (*format == 'c'
+ || *format == 's'
+ || *format == 'i' || *format == 'd'
+ || *format == 'o' || *format == 'u' || *format == 'x'
+ || *format == 'H'
+ ? xasprintf (_("In the directive number %u, flags are not allowed before '%c'."), spec.directives, *format)
+ : INVALID_CONVERSION_SPECIFIER (spec.directives,
+ *format));
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ }
+
+ if (number)
+ {
+ /* Numbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (unnumbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+ }
+ else
+ {
+ /* Unnumbered argument. */
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (spec.numbered_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == unnumbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
+ spec.numbered[unnumbered_arg_count].type = type;
+ unnumbered_arg_count++;
+ }
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Convert the unnumbered argument array to numbered arguments. */
+ if (unnumbered_arg_count > 0)
+ spec.numbered_arg_count = unnumbered_arg_count;
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ else if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ format_arg_type_t type1 = spec.numbered[i].type;
+ format_arg_type_t type2 = spec.numbered[j-1].type;
+ format_arg_type_t type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ /* Check that the use of err_no is the same. */
+ if (spec1->uses_err_no != spec2->uses_err_no)
+ {
+ if (error_logger)
+ {
+ if (spec1->uses_err_no)
+ error_logger (_("'%s' uses %%m but '%s' doesn't"),
+ pretty_msgid, pretty_msgstr);
+ else
+ error_logger (_("'%s' does not use %%m but '%s' uses %%m"),
+ pretty_msgid, pretty_msgstr);
+ }
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_gcc_internal =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ if (spec->numbered[i].type & FAT_UNSIGNED)
+ printf ("[unsigned]");
+ switch (spec->numbered[i].type & FAT_SIZE_MASK)
+ {
+ case 0:
+ break;
+ case FAT_SIZE_LONG:
+ printf ("[long]");
+ break;
+ case FAT_SIZE_LONGLONG:
+ printf ("[long long]");
+ break;
+ case FAT_SIZE_WIDE:
+ printf ("[host-wide]");
+ break;
+ default:
+ abort ();
+ }
+ switch (spec->numbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_INTEGER | FAT_FUNCPARAM:
+ printf ("P");
+ break;
+ case FAT_CHAR:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_POINTER:
+ printf ("p");
+ break;
+ case FAT_LOCATION:
+ printf ("H");
+ break;
+ case FAT_TREE | FAT_TREE_DECL:
+ printf ("D");
+ break;
+ case FAT_TREE | FAT_TREE_STATEMENT:
+ printf ("K");
+ break;
+ case FAT_TREE | FAT_TREE_FUNCDECL:
+ printf ("F");
+ break;
+ case FAT_TREE | FAT_TREE_TYPE:
+ printf ("T");
+ break;
+ case FAT_TREE | FAT_TREE_ARGUMENT:
+ printf ("A");
+ break;
+ case FAT_TREE | FAT_TREE_EXPRESSION:
+ printf ("E");
+ break;
+ case FAT_TREE | FAT_TREE_CV:
+ printf ("V");
+ break;
+ case FAT_TREE_CODE:
+ printf ("C");
+ break;
+ case FAT_TREE_CODE | FAT_TREE_CODE_BINOP:
+ printf ("O");
+ break;
+ case FAT_TREE_CODE | FAT_TREE_CODE_ASSOP:
+ printf ("Q");
+ break;
+ case FAT_LANGUAGES:
+ printf ("L");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+ if (spec->uses_err_no)
+ printf (" ERR_NO");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-gcc-internal.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-gfc-internal.c b/gettext-tools/src/format-gfc-internal.c
new file mode 100644
index 0000000..4fbbc66
--- /dev/null
+++ b/gettext-tools/src/format-gfc-internal.c
@@ -0,0 +1,504 @@
+/* GFC (GNU Fortran Compiler) internal format strings.
+ Copyright (C) 2003-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2009.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* GFC internal format strings consist of format directives that are specific
+ to the GNU Fortran Compiler frontend of GCC, implemented in
+ gcc-4.3.3/gcc/fortran/error.c (function error_print).
+
+ A directive
+ - starts with '%',
+ - either is finished by '%', that needs no argument,
+ - or is continued like this:
+ - optionally 'm$' where m is a positive integer,
+ - finished by a specifier
+ - 'C', that needs no argument but uses a particular variable
+ (but for the purposes of 'm$' numbering it consumes an
+ argument nevertheless, of 'void' type),
+ - 'L', that needs a 'locus *' argument,
+ - 'i', 'd', that need a signed integer argument,
+ - 'u', that needs an unsigned integer argument,
+ - 'li', 'ld', that need a signed long integer argument,
+ - 'lu', that needs an unsigned long integer argument,
+ - 'c', that needs a character argument,
+ - 's', that needs a string argument.
+
+ Numbered ('%m$') and unnumbered argument specifications can be used in the
+ same string. The effect of '%m$' is to set the current argument number to
+ m. The current argument number is incremented after processing a directive.
+
+ When numbered argument specifications are used, specifying the Nth argument
+ requires that all the leading arguments, from the first to the (N-1)th, are
+ specified in the format string. */
+
+enum format_arg_type
+{
+ FAT_NONE = 0,
+ /* Basic types */
+ FAT_VOID = 1,
+ FAT_INTEGER = 2,
+ FAT_CHAR = 3,
+ FAT_STRING = 4,
+ FAT_LOCUS = 5,
+ /* Flags */
+ FAT_UNSIGNED = 1 << 3,
+ FAT_SIZE_LONG = 1 << 4,
+ /* Bitmasks */
+ FAT_SIZE_MASK = FAT_SIZE_LONG
+};
+#ifdef __cplusplus
+typedef int format_arg_type_t;
+#else
+typedef enum format_arg_type format_arg_type_t;
+#endif
+
+struct numbered_arg
+{
+ unsigned int number;
+ format_arg_type_t type;
+};
+
+struct unnumbered_arg
+{
+ format_arg_type_t type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int unnumbered_arg_count;
+ struct unnumbered_arg *unnumbered;
+ bool uses_currentloc;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+ struct spec *result;
+ unsigned int number;
+
+ spec.directives = 0;
+ numbered_arg_count = 0;
+ allocated = 0;
+ numbered = NULL;
+ spec.uses_currentloc = false;
+ number = 1;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format != '%')
+ {
+ format_arg_type_t type;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+ }
+ }
+
+ if (*format == 'C')
+ {
+ type = FAT_VOID;
+ spec.uses_currentloc = true;
+ }
+ else if (*format == 'L')
+ type = FAT_LOCUS;
+ else if (*format == 'c')
+ type = FAT_CHAR;
+ else if (*format == 's')
+ type = FAT_STRING;
+ else
+ {
+ format_arg_type_t size = 0;
+
+ if (*format == 'l')
+ {
+ ++format;
+ size = FAT_SIZE_LONG;
+ }
+
+ if (*format == 'i' || *format == 'd')
+ type = FAT_INTEGER | size;
+ else if (*format == 'u')
+ type = FAT_INTEGER | FAT_UNSIGNED | size;
+ else
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+ }
+
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = number;
+ numbered[numbered_arg_count].type = type;
+ numbered_arg_count++;
+
+ number++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (numbered, numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < numbered_arg_count; i++)
+ if (j > 0 && numbered[i].number == numbered[j-1].number)
+ {
+ format_arg_type_t type1 = numbered[i].type;
+ format_arg_type_t type2 = numbered[j-1].type;
+ format_arg_type_t type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+ err = true;
+ }
+
+ numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ numbered[j].number = numbered[i].number;
+ numbered[j].type = numbered[i].type;
+ }
+ j++;
+ }
+ numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ /* Verify that the format strings uses all arguments up to the highest
+ numbered one. */
+ {
+ unsigned int i;
+
+ for (i = 0; i < numbered_arg_count; i++)
+ if (numbered[i].number != i + 1)
+ {
+ *invalid_reason =
+ xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
+ goto bad_format;
+ }
+ }
+
+ /* So now the numbered arguments array is equivalent to a sequence
+ of unnumbered arguments. Eliminate the FAT_VOID placeholders. */
+ {
+ unsigned int i;
+
+ spec.unnumbered_arg_count = 0;
+ for (i = 0; i < numbered_arg_count; i++)
+ if (numbered[i].type != FAT_VOID)
+ spec.unnumbered_arg_count++;
+
+ if (spec.unnumbered_arg_count > 0)
+ {
+ unsigned int j;
+
+ spec.unnumbered = XNMALLOC (spec.unnumbered_arg_count, struct unnumbered_arg);
+ j = 0;
+ for (i = 0; i < numbered_arg_count; i++)
+ if (numbered[i].type != FAT_VOID)
+ spec.unnumbered[j++].type = numbered[i].type;
+ }
+ else
+ spec.unnumbered = NULL;
+ }
+ free (numbered);
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (numbered != NULL)
+ free (numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->unnumbered != NULL)
+ free (spec->unnumbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+ unsigned int i;
+
+ /* Check the argument types are the same. */
+ if (equality
+ ? spec1->unnumbered_arg_count != spec2->unnumbered_arg_count
+ : spec1->unnumbered_arg_count < spec2->unnumbered_arg_count)
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else
+ for (i = 0; i < spec2->unnumbered_arg_count; i++)
+ if (spec1->unnumbered[i].type != spec2->unnumbered[i].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr, i + 1);
+ err = true;
+ }
+
+ /* Check that the use of currentloc is the same. */
+ if (spec1->uses_currentloc != spec2->uses_currentloc)
+ {
+ if (error_logger)
+ {
+ if (spec1->uses_currentloc)
+ error_logger (_("'%s' uses %%C but '%s' doesn't"),
+ pretty_msgid, pretty_msgstr);
+ else
+ error_logger (_("'%s' does not use %%C but '%s' uses %%C"),
+ pretty_msgid, pretty_msgstr);
+ }
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_gfc_internal =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->unnumbered_arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ if (spec->unnumbered[i].type & FAT_UNSIGNED)
+ printf ("[unsigned]");
+ switch (spec->unnumbered[i].type & FAT_SIZE_MASK)
+ {
+ case 0:
+ break;
+ case FAT_SIZE_LONG:
+ printf ("[long]");
+ break;
+ default:
+ abort ();
+ }
+ switch (spec->unnumbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_CHAR:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_LOCUS:
+ printf ("L");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf (")");
+ if (spec->uses_currentloc)
+ printf (" C");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-gfc-internal.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-invalid.h b/gettext-tools/src/format-invalid.h
new file mode 100644
index 0000000..3ad60aa
--- /dev/null
+++ b/gettext-tools/src/format-invalid.h
@@ -0,0 +1,40 @@
+/* Common reasons that make a format string invalid.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+/* These macros return freshly allocated error message strings, intended
+ to be stored in *invalid_reason. */
+
+#define INVALID_UNTERMINATED_DIRECTIVE() \
+ xstrdup (_("The string ends in the middle of a directive."))
+
+#define INVALID_MIXES_NUMBERED_UNNUMBERED() \
+ xstrdup (_("The string refers to arguments both through absolute argument numbers and through unnumbered argument specifications."))
+
+#define INVALID_ARGNO_0(directive_number) \
+ xasprintf (_("In the directive number %u, the argument number 0 is not a positive integer."), directive_number)
+#define INVALID_WIDTH_ARGNO_0(directive_number) \
+ xasprintf (_("In the directive number %u, the width's argument number 0 is not a positive integer."), directive_number)
+#define INVALID_PRECISION_ARGNO_0(directive_number) \
+ xasprintf (_("In the directive number %u, the precision's argument number 0 is not a positive integer."), directive_number)
+
+#define INVALID_CONVERSION_SPECIFIER(directive_number,conv_char) \
+ (c_isprint (conv_char) \
+ ? xasprintf (_("In the directive number %u, the character '%c' is not a valid conversion specifier."), directive_number, conv_char) \
+ : xasprintf (_("The character that terminates the directive number %u is not a valid conversion specifier."), directive_number))
+
+#define INVALID_INCOMPATIBLE_ARG_TYPES(arg_number) \
+ xasprintf (_("The string refers to argument number %u in incompatible ways."), arg_number)
diff --git a/gettext-tools/src/format-java.c b/gettext-tools/src/format-java.c
new file mode 100644
index 0000000..5196835
--- /dev/null
+++ b/gettext-tools/src/format-java.c
@@ -0,0 +1,893 @@
+/* Java format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Java format strings are described in java/text/MessageFormat.html.
+ See also the ICU documentation class_MessageFormat.html.
+
+ messageFormatPattern := string ( "{" messageFormatElement "}" string )*
+
+ messageFormatElement := argument { "," elementFormat }
+
+ elementFormat := "time" { "," datetimeStyle }
+ | "date" { "," datetimeStyle }
+ | "number" { "," numberStyle }
+ | "choice" { "," choiceStyle }
+
+ datetimeStyle := "short"
+ | "medium"
+ | "long"
+ | "full"
+ | dateFormatPattern
+
+ numberStyle := "currency"
+ | "percent"
+ | "integer"
+ | numberFormatPattern
+
+ choiceStyle := choiceFormatPattern
+
+ dateFormatPattern see SimpleDateFormat.applyPattern
+
+ numberFormatPattern see DecimalFormat.applyPattern
+
+ choiceFormatPattern see ChoiceFormat constructor
+
+ In strings, literal curly braces can be used if quoted between single
+ quotes. A real single quote is represented by ''.
+
+ If a pattern is used, then unquoted braces in the pattern, if any, must
+ match: that is, "ab {0} de" and "ab '}' de" are ok, but "ab {0'}' de" and
+ "ab } de" are not.
+
+ The argument is a number from 0 to 9, which corresponds to the arguments
+ presented in an array to be formatted.
+
+ It is ok to have unused arguments in the array.
+
+ Adding a dateFormatPattern / numberFormatPattern / choiceFormatPattern
+ to an elementFormat is equivalent to creating a SimpleDateFormat /
+ DecimalFormat / ChoiceFormat and use of setFormat. For example,
+
+ MessageFormat form =
+ new MessageFormat("The disk \"{1}\" contains {0,choice,0#no files|1#one file|2#{0,number} files}.");
+
+ is equivalent to
+
+ MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
+ form.setFormat(1, // Number of {} occurrence in the string!
+ new ChoiceFormat(new double[] { 0, 1, 2 },
+ new String[] { "no files", "one file",
+ "{0,number} files" }));
+
+ Note: The behaviour of quotes inside a choiceFormatPattern is not clear.
+ Example 1:
+ "abc{1,choice,0#{1,number,00';'000}}def"
+ JDK 1.1.x: exception
+ JDK 1.3.x: behaves like "abc{1,choice,0#{1,number,00;000}}def"
+ Example 2:
+ "abc{1,choice,0#{1,number,00';'}}def"
+ JDK 1.1.x: interprets the semicolon as number suffix
+ JDK 1.3.x: behaves like "abc{1,choice,0#{1,number,00;}}def"
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_OBJECT, /* java.lang.Object */
+ FAT_NUMBER, /* java.lang.Number */
+ FAT_DATE /* java.util.Date */
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+
+/* Forward declaration of local functions. */
+static bool date_format_parse (const char *format);
+static bool number_format_parse (const char *format);
+static bool choice_format_parse (const char *format, struct spec *spec,
+ char **invalid_reason);
+
+
+/* Quote handling:
+ - When we see a single-quote, ignore it, but toggle the quoting flag.
+ - When we see a double single-quote, ignore the first of the two.
+ Assumes local variables format, quoting. */
+#define HANDLE_QUOTE \
+ if (*format == '\'' && *++format != '\'') \
+ quoting = !quoting;
+
+/* Note that message_format_parse and choice_format_parse are mutually
+ recursive. This is because MessageFormat can use some ChoiceFormats,
+ and a ChoiceFormat is made up from several MessageFormats. */
+
+/* Return true if a format is a valid messageFormatPattern.
+ Extracts argument type information into spec. */
+static bool
+message_format_parse (const char *format, char *fdi, struct spec *spec,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ bool quoting = false;
+
+ for (;;)
+ {
+ HANDLE_QUOTE;
+ if (!quoting && *format == '{')
+ {
+ unsigned int depth;
+ const char *element_start;
+ const char *element_end;
+ size_t n;
+ char *element_alloced;
+ char *element;
+ unsigned int number;
+ enum format_arg_type type;
+
+ FDI_SET (format, FMTDIR_START);
+ spec->directives++;
+
+ element_start = ++format;
+ depth = 0;
+ for (; *format != '\0'; format++)
+ {
+ if (*format == '{')
+ depth++;
+ else if (*format == '}')
+ {
+ if (depth == 0)
+ break;
+ else
+ depth--;
+ }
+ }
+ if (*format == '\0')
+ {
+ *invalid_reason =
+ xstrdup (_("The string ends in the middle of a directive: found '{' without matching '}'."));
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ element_end = format++;
+
+ n = element_end - element_start;
+ element = element_alloced = (char *) xmalloca (n + 1);
+ memcpy (element, element_start, n);
+ element[n] = '\0';
+
+ if (!c_isdigit (*element))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '{' is not followed by an argument number."), spec->directives);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ number = 0;
+ do
+ {
+ number = 10 * number + (*element - '0');
+ element++;
+ }
+ while (c_isdigit (*element));
+
+ type = FAT_OBJECT;
+ if (*element == '\0')
+ ;
+ else if (strncmp (element, ",time", 5) == 0
+ || strncmp (element, ",date", 5) == 0)
+ {
+ type = FAT_DATE;
+ element += 5;
+ if (*element == '\0')
+ ;
+ else if (*element == ',')
+ {
+ element++;
+ if (strcmp (element, "short") == 0
+ || strcmp (element, "medium") == 0
+ || strcmp (element, "long") == 0
+ || strcmp (element, "full") == 0
+ || date_format_parse (element))
+ ;
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid date/time style."), spec->directives, element);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else
+ {
+ *element = '\0';
+ element -= 4;
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else if (strncmp (element, ",number", 7) == 0)
+ {
+ type = FAT_NUMBER;
+ element += 7;
+ if (*element == '\0')
+ ;
+ else if (*element == ',')
+ {
+ element++;
+ if (strcmp (element, "currency") == 0
+ || strcmp (element, "percent") == 0
+ || strcmp (element, "integer") == 0
+ || number_format_parse (element))
+ ;
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the substring \"%s\" is not a valid number style."), spec->directives, element);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else
+ {
+ *element = '\0';
+ element -= 6;
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else if (strncmp (element, ",choice", 7) == 0)
+ {
+ type = FAT_NUMBER; /* because ChoiceFormat extends NumberFormat */
+ element += 7;
+ if (*element == '\0')
+ ;
+ else if (*element == ',')
+ {
+ element++;
+ if (choice_format_parse (element, spec, invalid_reason))
+ ;
+ else
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else
+ {
+ *element = '\0';
+ element -= 6;
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, \"%s\" is not followed by a comma."), spec->directives, element);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the argument number is not followed by a comma and one of \"%s\", \"%s\", \"%s\", \"%s\"."), spec->directives, "time", "date", "number", "choice");
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ freea (element_alloced);
+ return false;
+ }
+ freea (element_alloced);
+
+ if (spec->allocated == spec->numbered_arg_count)
+ {
+ spec->allocated = 2 * spec->allocated + 1;
+ spec->numbered = (struct numbered_arg *) xrealloc (spec->numbered, spec->allocated * sizeof (struct numbered_arg));
+ }
+ spec->numbered[spec->numbered_arg_count].number = number;
+ spec->numbered[spec->numbered_arg_count].type = type;
+ spec->numbered_arg_count++;
+
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+ /* The doc says "ab}de" is invalid. Even though JDK accepts it. */
+ else if (!quoting && *format == '}')
+ {
+ FDI_SET (format, FMTDIR_START);
+ *invalid_reason =
+ xstrdup (_("The string starts in the middle of a directive: found '}' without matching '{'."));
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+ else if (*format != '\0')
+ format++;
+ else
+ break;
+ }
+
+ return true;
+}
+
+/* Return true if a format is a valid dateFormatPattern. */
+static bool
+date_format_parse (const char *format)
+{
+ /* Any string is valid. Single-quote starts a quoted section, to be
+ terminated at the next single-quote or string end. Double single-quote
+ gives a single single-quote. Non-quoted ASCII letters are first grouped
+ into blocks of equal letters. Then each block (e.g. 'yyyy') is
+ interpreted according to some rules. */
+ return true;
+}
+
+/* Return true if a format is a valid numberFormatPattern. */
+static bool
+number_format_parse (const char *format)
+{
+ /* Pattern Syntax:
+ pattern := pos_pattern{';' neg_pattern}
+ pos_pattern := {prefix}number{suffix}
+ neg_pattern := {prefix}number{suffix}
+ number := integer{'.' fraction}{exponent}
+ prefix := '\u0000'..'\uFFFD' - special_characters
+ suffix := '\u0000'..'\uFFFD' - special_characters
+ integer := min_int | '#' | '#' integer | '#' ',' integer
+ min_int := '0' | '0' min_int | '0' ',' min_int
+ fraction := '0'* '#'*
+ exponent := 'E' '0' '0'*
+ Notation:
+ X* 0 or more instances of X
+ { X } 0 or 1 instances of X
+ X | Y either X or Y
+ X..Y any character from X up to Y, inclusive
+ S - T characters in S, except those in T
+ Single-quote starts a quoted section, to be terminated at the next
+ single-quote or string end. Double single-quote gives a single
+ single-quote.
+ */
+ bool quoting = false;
+ bool seen_semicolon = false;
+
+ HANDLE_QUOTE;
+ for (;;)
+ {
+ /* Parse prefix. */
+ while (*format != '\0'
+ && !(!quoting && (*format == '0' || *format == '#')))
+ {
+ if (format[0] == '\\')
+ {
+ if (format[1] == 'u'
+ && c_isxdigit (format[2])
+ && c_isxdigit (format[3])
+ && c_isxdigit (format[4])
+ && c_isxdigit (format[5]))
+ format += 6;
+ else
+ format += 2;
+ }
+ else
+ format += 1;
+ HANDLE_QUOTE;
+ }
+
+ /* Parse integer. */
+ if (!(!quoting && (*format == '0' || *format == '#')))
+ return false;
+ while (!quoting && *format == '#')
+ {
+ format++;
+ HANDLE_QUOTE;
+ if (!quoting && *format == ',')
+ {
+ format++;
+ HANDLE_QUOTE;
+ }
+ }
+ while (!quoting && *format == '0')
+ {
+ format++;
+ HANDLE_QUOTE;
+ if (!quoting && *format == ',')
+ {
+ format++;
+ HANDLE_QUOTE;
+ }
+ }
+
+ /* Parse fraction. */
+ if (!quoting && *format == '.')
+ {
+ format++;
+ HANDLE_QUOTE;
+ while (!quoting && *format == '0')
+ {
+ format++;
+ HANDLE_QUOTE;
+ }
+ while (!quoting && *format == '#')
+ {
+ format++;
+ HANDLE_QUOTE;
+ }
+ }
+
+ /* Parse exponent. */
+ if (!quoting && *format == 'E')
+ {
+ const char *format_save = format;
+ format++;
+ HANDLE_QUOTE;
+ if (!quoting && *format == '0')
+ {
+ do
+ {
+ format++;
+ HANDLE_QUOTE;
+ }
+ while (!quoting && *format == '0');
+ }
+ else
+ {
+ /* Back up. */
+ format = format_save;
+ quoting = false;
+ }
+ }
+
+ /* Parse suffix. */
+ while (*format != '\0'
+ && (seen_semicolon || !(!quoting && *format == ';')))
+ {
+ if (format[0] == '\\')
+ {
+ if (format[1] == 'u'
+ && c_isxdigit (format[2])
+ && c_isxdigit (format[3])
+ && c_isxdigit (format[4])
+ && c_isxdigit (format[5]))
+ format += 6;
+ else
+ format += 2;
+ }
+ else
+ format += 1;
+ HANDLE_QUOTE;
+ }
+
+ if (seen_semicolon || !(!quoting && *format == ';'))
+ break;
+ }
+
+ return (*format == '\0');
+}
+
+/* Return true if a format is a valid choiceFormatPattern.
+ Extracts argument type information into spec. */
+static bool
+choice_format_parse (const char *format, struct spec *spec,
+ char **invalid_reason)
+{
+ /* Pattern syntax:
+ pattern := | choice | choice '|' pattern
+ choice := number separator messageformat
+ separator := '<' | '#' | '\u2264'
+ Single-quote starts a quoted section, to be terminated at the next
+ single-quote or string end. Double single-quote gives a single
+ single-quote.
+ */
+ bool quoting = false;
+
+ HANDLE_QUOTE;
+ if (*format == '\0')
+ return true;
+ for (;;)
+ {
+ /* Don't bother looking too precisely into the syntax of the number.
+ It can contain various Unicode characters. */
+ bool number_nonempty;
+ char *msgformat;
+ char *mp;
+ bool msgformat_valid;
+
+ /* Parse number. */
+ number_nonempty = false;
+ while (*format != '\0'
+ && !(!quoting && (*format == '<' || *format == '#'
+ || strncmp (format, "\\u2264", 6) == 0
+ || *format == '|')))
+ {
+ if (format[0] == '\\')
+ {
+ if (format[1] == 'u'
+ && c_isxdigit (format[2])
+ && c_isxdigit (format[3])
+ && c_isxdigit (format[4])
+ && c_isxdigit (format[5]))
+ format += 6;
+ else
+ format += 2;
+ }
+ else
+ format += 1;
+ number_nonempty = true;
+ HANDLE_QUOTE;
+ }
+
+ /* Short clause at end of pattern is valid and is ignored! */
+ if (*format == '\0')
+ break;
+
+ if (!number_nonempty)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, a choice contains no number."), spec->directives);
+ return false;
+ }
+
+ if (*format == '<' || *format == '#')
+ format += 1;
+ else if (strncmp (format, "\\u2264", 6) == 0)
+ format += 6;
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, a choice contains a number that is not followed by '<', '#' or '%s'."), spec->directives, "\\u2264");
+ return false;
+ }
+ HANDLE_QUOTE;
+
+ msgformat = (char *) xmalloca (strlen (format) + 1);
+ mp = msgformat;
+
+ while (*format != '\0' && !(!quoting && *format == '|'))
+ {
+ *mp++ = *format++;
+ HANDLE_QUOTE;
+ }
+ *mp = '\0';
+
+ msgformat_valid =
+ message_format_parse (msgformat, NULL, spec, invalid_reason);
+
+ freea (msgformat);
+
+ if (!msgformat_valid)
+ return false;
+
+ if (*format == '\0')
+ break;
+
+ format++;
+ HANDLE_QUOTE;
+ }
+
+ return true;
+}
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+
+ if (!message_format_parse (format, fdi, &spec, invalid_reason))
+ goto bad_format;
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2 || type2 == FAT_OBJECT)
+ type_both = type1;
+ else if (type1 == FAT_OBJECT)
+ type_both = type2;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument {%u}, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument {%u} doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument {%u} are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_java =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 0;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_OBJECT:
+ printf ("*");
+ break;
+ case FAT_NUMBER:
+ printf ("Number");
+ break;
+ case FAT_DATE:
+ printf ("Date");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-java.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-javascript.c b/gettext-tools/src/format-javascript.c
new file mode 100644
index 0000000..d76a8d7
--- /dev/null
+++ b/gettext-tools/src/format-javascript.c
@@ -0,0 +1,333 @@
+/* JavaScript format strings.
+ Copyright (C) 2001-2004, 2006-2009, 2013 Free Software Foundation, Inc.
+ Written by Andreas Stricker <andy@knitter.ch>, 2010.
+ It's based on python format module from Bruno Haible.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Although JavaScript specification itself does not define any format
+ strings, many implementations provide printf-like functions.
+ We provide a permissive parser which accepts commonly used format
+ strings, where:
+
+ A directive
+ - starts with '%',
+ - is optionally followed by any of the characters '0', '-', ' ',
+ or, each of which acts as a flag,
+ - is optionally followed by a width specification: a nonempty digit
+ sequence,
+ - is optionally followed by '.' and a precision specification: a nonempty
+ digit sequence,
+ - is finished by a specifier
+ - 's', that needs a string argument,
+ - 'b', 'd', 'u', 'o', 'x', 'X', that need an integer argument,
+ - 'f', that need a floating-point argument,
+ - 'c', that needs a character argument.
+ - 'j', that needs an argument of any type.
+ Additionally there is the directive '%%', which takes no argument. */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_ANY,
+ FAT_CHARACTER,
+ FAT_STRING,
+ FAT_INTEGER,
+ FAT_FLOAT
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int format_args_count;
+ unsigned int allocated;
+ enum format_arg_type *format_args;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.format_args_count = 0;
+ spec.allocated = 0;
+ spec.format_args = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ enum format_arg_type type;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ while (*format == '-' || *format == '+' || *format == ' '
+ || *format == '0' || *format == 'I')
+ format++;
+
+ while (isdigit (*format))
+ format++;
+
+ if (*format == '.')
+ {
+ format++;
+
+ while (isdigit (*format))
+ format++;
+ }
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case 'b': case 'd': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER;
+ break;
+ case 'f':
+ type = FAT_FLOAT;
+ break;
+ case 'j':
+ type = FAT_ANY;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (*format != '%')
+ {
+ if (spec.allocated == spec.format_args_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.format_args = (enum format_arg_type *) xrealloc (spec.format_args, spec.allocated * sizeof (enum format_arg_type));
+ }
+ spec.format_args[spec.format_args_count] = type;
+ spec.format_args_count++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.format_args != NULL)
+ free (spec.format_args);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->format_args != NULL)
+ free (spec->format_args);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->format_args_count + spec2->format_args_count > 0)
+ {
+ unsigned int i;
+
+ /* Check the argument types are the same. */
+ if (spec1->format_args_count != spec2->format_args_count)
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else
+ for (i = 0; i < spec2->format_args_count; i++)
+ if (!(spec1->format_args[i] == spec2->format_args[i]
+ || (!equality
+ && (spec1->format_args[i] == FAT_ANY
+ || spec2->format_args[i] == FAT_ANY))))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr, i + 1);
+ err = true;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_javascript =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->format_args_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ switch (spec->format_args[i])
+ {
+ case FAT_ANY:
+ printf ("*");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-javascript.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+#endif /* TEST */
diff --git a/gettext-tools/src/format-kde.c b/gettext-tools/src/format-kde.c
new file mode 100644
index 0000000..90e887d
--- /dev/null
+++ b/gettext-tools/src/format-kde.c
@@ -0,0 +1,349 @@
+/* KDE format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2007.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* KDE 4 format strings are processed by method
+ KLocalizedStringPrivate::substituteSimple(string,'%',false) in
+ kde4libs-3.93.0.orig/kdecore/localization/klocalizedstring.cpp .
+ A directive
+ - starts with '%',
+ - is followed by a non-zero digit and optionally more digits. All
+ the following digits are eaten up.
+ An unterminated directive ('%' not followed by a digit or at the end) is
+ not an error.
+ %1 denotes the first argument, %2 the second argument, etc.
+ The set of used argument numbers must be of the form {1,...,n} or
+ {1,...,n} \ {m}: one of the supplied arguments may be ignored by the
+ format string. This allows the processing of singular forms (msgstr[0]).
+ Which argument may be skipped, depends on the argument types at runtime;
+ since xgettext cannot extract this info, it is considered unknown here. */
+
+struct numbered_arg
+{
+ unsigned int number;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ /* Subtract 1, because argument number 0 can only occur through overflow. */
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number - 1;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number - 1;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ const char *dir_start = format - 1;
+
+ if (*format > '0' && *format <= '9')
+ {
+ /* A directive. */
+ unsigned int number;
+
+ FDI_SET (dir_start, FMTDIR_START);
+ spec.directives++;
+
+ number = *format - '0';
+ while (format[1] >= '0' && format[1] <= '9')
+ {
+ number = 10 * number + (format[1] - '0');
+ format++;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered_arg_count++;
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ ;
+ else
+ {
+ if (j < i)
+ spec.numbered[j].number = spec.numbered[i].number;
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ }
+ /* Now spec.numbered[i] >= i + 1 for i = 0,..,spec.numbered_arg_count-1
+ (since the numbered argument counts are strictly increasing, considering
+ 0 as overflow). */
+
+ /* Verify that the argument numbers are of the form {1,...,n} or
+ {1,...,n} \ {m}. */
+ if (spec.numbered_arg_count > 0)
+ {
+ unsigned int i;
+
+ i = 0;
+ for (; i < spec.numbered_arg_count; i++)
+ if (spec.numbered[i].number > i + 1)
+ {
+ unsigned int first_gap = i + 1;
+ for (; i < spec.numbered_arg_count; i++)
+ if (spec.numbered[i].number > i + 2)
+ {
+ unsigned int second_gap = i + 2;
+ *invalid_reason =
+ xasprintf (_("The string refers to argument number %u but ignores the arguments %u and %u."),
+ spec.numbered[i].number, first_gap, second_gap);
+ goto bad_format;
+ }
+ break;
+ }
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+ unsigned int missing = 0; /* only used if !equality */
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else if (missing)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for arguments %u and %u doesn't exist in '%s', only one argument may be ignored"),
+ missing, spec1->numbered[i].number,
+ pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ {
+ missing = spec1->numbered[i].number;
+ i++;
+ }
+ }
+ else
+ j++, i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_kde =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-kde.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-librep.c b/gettext-tools/src/format-librep.c
new file mode 100644
index 0000000..05185ad
--- /dev/null
+++ b/gettext-tools/src/format-librep.c
@@ -0,0 +1,463 @@
+/* librep format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* librep format strings are implemented in librep-0.14/src/streams.c.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '-', '^', '0', '+', ' ',
+ each of which acts as a flag,
+ - is optionally followed by a width specification: a nonempty digit
+ sequence,
+ - is optionally followed by '.' and a precision specification: a nonempty
+ digit sequence,
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that need a character argument,
+ - 'd', 'x', 'X', 'o', that need an integer argument,
+ - 's', that need an argument and prints it using princ,
+ - 'S', that need an argument and prints it using prin1.
+ Numbered ('%m$') and unnumbered argument specifications can be used in the
+ same string. The effect of '%m$' is to set the current argument number to
+ m. The current argument number is incremented after processing a directive.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_CHARACTER,
+ FAT_INTEGER,
+ FAT_OBJECT_PRETTY,
+ FAT_OBJECT
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+ unsigned int number;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ number = 1;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ enum format_arg_type type;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$' && m > 0)
+ {
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags. */
+ while (*format == '-' || *format == '^' || *format == '0'
+ || *format == '+' || *format == ' ')
+ format++;
+
+ /* Parse width. */
+ if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 'd': case 'x': case 'X': case 'o':
+ type = FAT_INTEGER;
+ break;
+ case 's':
+ type = FAT_OBJECT_PRETTY;
+ break;
+ case 'S':
+ type = FAT_OBJECT;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (type != FAT_NONE)
+ {
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_librep =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_OBJECT_PRETTY:
+ printf ("s");
+ break;
+ case FAT_OBJECT:
+ printf ("*");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-librep.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-lisp.c b/gettext-tools/src/format-lisp.c
new file mode 100644
index 0000000..7d423a5
--- /dev/null
+++ b/gettext-tools/src/format-lisp.c
@@ -0,0 +1,3658 @@
+/* Lisp format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "gcd.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "minmax.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Assertion macros. Could be defined to empty for speed. */
+#define ASSERT(expr) if (!(expr)) abort ();
+#define VERIFY_LIST(list) verify_list (list)
+
+
+/* Lisp format strings are described in the Common Lisp HyperSpec,
+ chapter 22.3 "Formatted Output". */
+
+/* Data structure describing format string derived constraints for an
+ argument list. It is a recursive list structure. Structure sharing
+ is not allowed. */
+
+enum format_cdr_type
+{
+ FCT_REQUIRED, /* The format argument list cannot end before this argument. */
+ FCT_OPTIONAL /* The format argument list may end before this argument. */
+};
+
+enum format_arg_type
+{
+ FAT_OBJECT, /* Any object, type T. */
+ FAT_CHARACTER_INTEGER_NULL, /* Type (OR CHARACTER INTEGER NULL). */
+ FAT_CHARACTER_NULL, /* Type (OR CHARACTER NULL). */
+ FAT_CHARACTER, /* Type CHARACTER. */
+ FAT_INTEGER_NULL, /* Type (OR INTEGER NULL). */
+ FAT_INTEGER, /* Meant for objects of type INTEGER. */
+ FAT_REAL, /* Meant for objects of type REAL. */
+ FAT_LIST, /* Meant for proper lists. */
+ FAT_FORMATSTRING, /* Format strings. */
+ FAT_FUNCTION /* Function. */
+};
+
+struct format_arg
+{
+ unsigned int repcount; /* Number of consecutive arguments this constraint
+ applies to. Normally 1, but unconstrained
+ arguments are often repeated. */
+ enum format_cdr_type presence; /* Can the argument list end right before
+ this argument? */
+ enum format_arg_type type; /* Possible values for this argument. */
+ struct format_arg_list *list; /* For FAT_LIST: List elements. */
+};
+
+struct segment
+{
+ unsigned int count; /* Number of format_arg records used. */
+ unsigned int allocated;
+ struct format_arg *element; /* Argument constraints. */
+ unsigned int length; /* Number of arguments represented by this segment.
+ This is the sum of all repcounts in the segment. */
+};
+
+struct format_arg_list
+{
+ /* The constraints for the potentially infinite argument list are assumed
+ to become ultimately periodic. (Too complicated argument lists without
+ a-priori period, like
+ (format t "~@{~:[-~;~S~]~}" nil t 1 t 3 nil t 4)
+ are described by a constraint that ends in a length 1 period of
+ unconstrained arguments.) Such a periodic sequence can be split into
+ an initial segment and an endlessly repeated loop segment.
+ A finite sequence is represented entirely in the initial segment; the
+ loop segment is empty. */
+
+ struct segment initial; /* Initial arguments segment. */
+ struct segment repeated; /* Endlessly repeated segment. */
+};
+
+struct spec
+{
+ unsigned int directives;
+ struct format_arg_list *list;
+};
+
+
+/* Parameter for a directive. */
+enum param_type
+{
+ PT_NIL, /* param not present */
+ PT_CHARACTER, /* character */
+ PT_INTEGER, /* integer */
+ PT_ARGCOUNT, /* number of remaining arguments */
+ PT_V /* variable taken from argument list */
+};
+
+struct param
+{
+ enum param_type type;
+ int value; /* for PT_INTEGER: the value, for PT_V: the position */
+};
+
+
+/* Forward declaration of local functions. */
+#define union make_union
+static void verify_list (const struct format_arg_list *list);
+static void free_list (struct format_arg_list *list);
+static struct format_arg_list * copy_list (const struct format_arg_list *list);
+static bool equal_list (const struct format_arg_list *list1,
+ const struct format_arg_list *list2);
+static struct format_arg_list * make_intersected_list
+ (struct format_arg_list *list1,
+ struct format_arg_list *list2);
+static struct format_arg_list * make_intersection_with_empty_list
+ (struct format_arg_list *list);
+static struct format_arg_list * make_union_list
+ (struct format_arg_list *list1,
+ struct format_arg_list *list2);
+
+
+/* ======================= Verify a format_arg_list ======================= */
+
+/* Verify some invariants. */
+static void
+verify_element (const struct format_arg * e)
+{
+ ASSERT (e->repcount > 0);
+ if (e->type == FAT_LIST)
+ verify_list (e->list);
+}
+
+/* Verify some invariants. */
+/* Memory effects: none. */
+static void
+verify_list (const struct format_arg_list *list)
+{
+ unsigned int i;
+ unsigned int total_repcount;
+
+ ASSERT (list->initial.count <= list->initial.allocated);
+ total_repcount = 0;
+ for (i = 0; i < list->initial.count; i++)
+ {
+ verify_element (&list->initial.element[i]);
+ total_repcount += list->initial.element[i].repcount;
+ }
+ ASSERT (total_repcount == list->initial.length);
+
+ ASSERT (list->repeated.count <= list->repeated.allocated);
+ total_repcount = 0;
+ for (i = 0; i < list->repeated.count; i++)
+ {
+ verify_element (&list->repeated.element[i]);
+ total_repcount += list->repeated.element[i].repcount;
+ }
+ ASSERT (total_repcount == list->repeated.length);
+}
+
+#define VERIFY_LIST(list) verify_list (list)
+
+
+/* ======================== Free a format_arg_list ======================== */
+
+/* Free the data belonging to an argument list element. */
+static inline void
+free_element (struct format_arg *element)
+{
+ if (element->type == FAT_LIST)
+ free_list (element->list);
+}
+
+/* Free an argument list. */
+/* Memory effects: Frees list. */
+static void
+free_list (struct format_arg_list *list)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->initial.count; i++)
+ free_element (&list->initial.element[i]);
+ if (list->initial.element != NULL)
+ free (list->initial.element);
+
+ for (i = 0; i < list->repeated.count; i++)
+ free_element (&list->repeated.element[i]);
+ if (list->repeated.element != NULL)
+ free (list->repeated.element);
+}
+
+
+/* ======================== Copy a format_arg_list ======================== */
+
+/* Copy the data belonging to an argument list element. */
+static inline void
+copy_element (struct format_arg *newelement,
+ const struct format_arg *oldelement)
+{
+ newelement->repcount = oldelement->repcount;
+ newelement->presence = oldelement->presence;
+ newelement->type = oldelement->type;
+ if (oldelement->type == FAT_LIST)
+ newelement->list = copy_list (oldelement->list);
+}
+
+/* Copy an argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+copy_list (const struct format_arg_list *list)
+{
+ struct format_arg_list *newlist;
+ unsigned int length;
+ unsigned int i;
+
+ VERIFY_LIST (list);
+
+ newlist = XMALLOC (struct format_arg_list);
+
+ newlist->initial.count = newlist->initial.allocated = list->initial.count;
+ length = 0;
+ if (list->initial.count == 0)
+ newlist->initial.element = NULL;
+ else
+ {
+ newlist->initial.element =
+ XNMALLOC (newlist->initial.allocated, struct format_arg);
+ for (i = 0; i < list->initial.count; i++)
+ {
+ copy_element (&newlist->initial.element[i],
+ &list->initial.element[i]);
+ length += list->initial.element[i].repcount;
+ }
+ }
+ ASSERT (length == list->initial.length);
+ newlist->initial.length = length;
+
+ newlist->repeated.count = newlist->repeated.allocated = list->repeated.count;
+ length = 0;
+ if (list->repeated.count == 0)
+ newlist->repeated.element = NULL;
+ else
+ {
+ newlist->repeated.element =
+ XNMALLOC (newlist->repeated.allocated, struct format_arg);
+ for (i = 0; i < list->repeated.count; i++)
+ {
+ copy_element (&newlist->repeated.element[i],
+ &list->repeated.element[i]);
+ length += list->repeated.element[i].repcount;
+ }
+ }
+ ASSERT (length == list->repeated.length);
+ newlist->repeated.length = length;
+
+ VERIFY_LIST (newlist);
+
+ return newlist;
+}
+
+
+/* ===================== Compare two format_arg_lists ===================== */
+
+/* Tests whether two normalized argument constraints are equivalent,
+ ignoring the repcount. */
+static bool
+equal_element (const struct format_arg * e1, const struct format_arg * e2)
+{
+ return (e1->presence == e2->presence
+ && e1->type == e2->type
+ && (e1->type == FAT_LIST ? equal_list (e1->list, e2->list) : true));
+}
+
+/* Tests whether two normalized argument list constraints are equivalent. */
+/* Memory effects: none. */
+static bool
+equal_list (const struct format_arg_list *list1,
+ const struct format_arg_list *list2)
+{
+ unsigned int n, i;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ n = list1->initial.count;
+ if (n != list2->initial.count)
+ return false;
+ for (i = 0; i < n; i++)
+ {
+ const struct format_arg * e1 = &list1->initial.element[i];
+ const struct format_arg * e2 = &list2->initial.element[i];
+
+ if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
+ return false;
+ }
+
+ n = list1->repeated.count;
+ if (n != list2->repeated.count)
+ return false;
+ for (i = 0; i < n; i++)
+ {
+ const struct format_arg * e1 = &list1->repeated.element[i];
+ const struct format_arg * e2 = &list2->repeated.element[i];
+
+ if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* ===================== Incremental memory allocation ===================== */
+
+/* Ensure list->initial.allocated >= newcount. */
+static inline void
+ensure_initial_alloc (struct format_arg_list *list, unsigned int newcount)
+{
+ if (newcount > list->initial.allocated)
+ {
+ list->initial.allocated =
+ MAX (2 * list->initial.allocated + 1, newcount);
+ list->initial.element =
+ (struct format_arg *)
+ xrealloc (list->initial.element,
+ list->initial.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->initial.allocated > list->initial.count. */
+static inline void
+grow_initial_alloc (struct format_arg_list *list)
+{
+ if (list->initial.count >= list->initial.allocated)
+ {
+ list->initial.allocated =
+ MAX (2 * list->initial.allocated + 1, list->initial.count + 1);
+ list->initial.element =
+ (struct format_arg *)
+ xrealloc (list->initial.element,
+ list->initial.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->repeated.allocated >= newcount. */
+static inline void
+ensure_repeated_alloc (struct format_arg_list *list, unsigned int newcount)
+{
+ if (newcount > list->repeated.allocated)
+ {
+ list->repeated.allocated =
+ MAX (2 * list->repeated.allocated + 1, newcount);
+ list->repeated.element =
+ (struct format_arg *)
+ xrealloc (list->repeated.element,
+ list->repeated.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->repeated.allocated > list->repeated.count. */
+static inline void
+grow_repeated_alloc (struct format_arg_list *list)
+{
+ if (list->repeated.count >= list->repeated.allocated)
+ {
+ list->repeated.allocated =
+ MAX (2 * list->repeated.allocated + 1, list->repeated.count + 1);
+ list->repeated.element =
+ (struct format_arg *)
+ xrealloc (list->repeated.element,
+ list->repeated.allocated * sizeof (struct format_arg));
+ }
+}
+
+
+/* ====================== Normalize a format_arg_list ====================== */
+
+/* Normalize an argument list constraint, assuming all sublists are already
+ normalized. */
+/* Memory effects: Destructively modifies list. */
+static void
+normalize_outermost_list (struct format_arg_list *list)
+{
+ unsigned int n, i, j;
+
+ /* Step 1: Combine adjacent elements.
+ Copy from i to j, keeping 0 <= j <= i. */
+
+ n = list->initial.count;
+ for (i = j = 0; i < n; i++)
+ if (j > 0
+ && equal_element (&list->initial.element[i],
+ &list->initial.element[j-1]))
+ {
+ list->initial.element[j-1].repcount +=
+ list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ }
+ else
+ {
+ if (j < i)
+ list->initial.element[j] = list->initial.element[i];
+ j++;
+ }
+ list->initial.count = j;
+
+ n = list->repeated.count;
+ for (i = j = 0; i < n; i++)
+ if (j > 0
+ && equal_element (&list->repeated.element[i],
+ &list->repeated.element[j-1]))
+ {
+ list->repeated.element[j-1].repcount +=
+ list->repeated.element[i].repcount;
+ free_element (&list->repeated.element[i]);
+ }
+ else
+ {
+ if (j < i)
+ list->repeated.element[j] = list->repeated.element[i];
+ j++;
+ }
+ list->repeated.count = j;
+
+ /* Nothing more to be done if the loop segment is empty. */
+ if (list->repeated.count > 0)
+ {
+ unsigned int m, repcount0_extra;
+
+ /* Step 2: Reduce the loop period. */
+ n = list->repeated.count;
+ repcount0_extra = 0;
+ if (n > 1
+ && equal_element (&list->repeated.element[0],
+ &list->repeated.element[n-1]))
+ {
+ repcount0_extra = list->repeated.element[n-1].repcount;
+ n--;
+ }
+ /* Proceed as if the loop period were n, with
+ list->repeated.element[0].repcount incremented by repcount0_extra. */
+ for (m = 2; m <= n / 2; n++)
+ if ((n % m) == 0)
+ {
+ /* m is a divisor of n. Try to reduce the loop period to n. */
+ bool ok = true;
+
+ for (i = 0; i < n - m; i++)
+ if (!((list->repeated.element[i].repcount
+ + (i == 0 ? repcount0_extra : 0)
+ == list->repeated.element[i+m].repcount)
+ && equal_element (&list->repeated.element[i],
+ &list->repeated.element[i+m])))
+ {
+ ok = false;
+ break;
+ }
+ if (ok)
+ {
+ for (i = m; i < n; i++)
+ free_element (&list->repeated.element[i]);
+ if (n < list->repeated.count)
+ list->repeated.element[m] = list->repeated.element[n];
+ list->repeated.count = list->repeated.count - n + m;
+ list->repeated.length /= n / m;
+ break;
+ }
+ }
+
+ /* Step 3: Roll as much as possible of the initial segment's tail
+ into the loop. */
+ if (list->repeated.count == 1)
+ {
+ if (list->initial.count > 0
+ && equal_element (&list->initial.element[list->initial.count-1],
+ &list->repeated.element[0]))
+ {
+ /* Roll the last element of the initial segment into the loop.
+ Its repcount is irrelevant. The second-to-last element is
+ certainly different and doesn't need to be considered. */
+ list->initial.length -=
+ list->initial.element[list->initial.count-1].repcount;
+ list->initial.count--;
+ }
+ }
+ else
+ {
+ while (list->initial.count > 0
+ && equal_element (&list->initial.element[list->initial.count-1],
+ &list->repeated.element[list->repeated.count-1]))
+ {
+ unsigned int moved_repcount =
+ MIN (list->initial.element[list->initial.count-1].repcount,
+ list->repeated.element[list->repeated.count-1].repcount);
+
+ /* Add the element at the start of list->repeated. */
+ if (equal_element (&list->repeated.element[0],
+ &list->repeated.element[list->repeated.count-1]))
+ list->repeated.element[0].repcount += moved_repcount;
+ else
+ {
+ unsigned int newcount = list->repeated.count + 1;
+ ensure_repeated_alloc (list, newcount);
+ for (i = newcount - 1; i > 0; i--)
+ list->repeated.element[i] = list->repeated.element[i-1];
+ list->repeated.count = newcount;
+ copy_element (&list->repeated.element[0],
+ &list->repeated.element[list->repeated.count-1]);
+ list->repeated.element[0].repcount = moved_repcount;
+ }
+
+ /* Remove the element from the end of list->repeated. */
+ list->repeated.element[list->repeated.count-1].repcount -=
+ moved_repcount;
+ if (list->repeated.element[list->repeated.count-1].repcount == 0)
+ {
+ free_element (&list->repeated.element[list->repeated.count-1]);
+ list->repeated.count--;
+ }
+
+ /* Remove the element from the end of list->initial. */
+ list->initial.element[list->initial.count-1].repcount -=
+ moved_repcount;
+ if (list->initial.element[list->initial.count-1].repcount == 0)
+ {
+ free_element (&list->initial.element[list->initial.count-1]);
+ list->initial.count--;
+ }
+ list->initial.length -= moved_repcount;
+ }
+ }
+ }
+}
+
+/* Normalize an argument list constraint. */
+/* Memory effects: Destructively modifies list. */
+static void
+normalize_list (struct format_arg_list *list)
+{
+ unsigned int n, i;
+
+ VERIFY_LIST (list);
+
+ /* First normalize all elements, recursively. */
+ n = list->initial.count;
+ for (i = 0; i < n; i++)
+ if (list->initial.element[i].type == FAT_LIST)
+ normalize_list (list->initial.element[i].list);
+ n = list->repeated.count;
+ for (i = 0; i < n; i++)
+ if (list->repeated.element[i].type == FAT_LIST)
+ normalize_list (list->repeated.element[i].list);
+
+ /* Then normalize the top level list. */
+ normalize_outermost_list (list);
+
+ VERIFY_LIST (list);
+}
+
+
+/* ===================== Unconstrained and empty lists ===================== */
+
+/* It's easier to allocate these on demand, than to be careful not to
+ accidentally modify statically allocated lists. */
+
+
+/* Create an unconstrained argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+make_unconstrained_list ()
+{
+ struct format_arg_list *list;
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 1;
+ list->repeated.allocated = 1;
+ list->repeated.element = XNMALLOC (1, struct format_arg);
+ list->repeated.element[0].repcount = 1;
+ list->repeated.element[0].presence = FCT_OPTIONAL;
+ list->repeated.element[0].type = FAT_OBJECT;
+ list->repeated.length = 1;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Create an empty argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+make_empty_list ()
+{
+ struct format_arg_list *list;
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 0;
+ list->repeated.allocated = 0;
+ list->repeated.element = NULL;
+ list->repeated.length = 0;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Test for an empty list. */
+/* Memory effects: none. */
+static bool
+is_empty_list (const struct format_arg_list *list)
+{
+ return (list->initial.count == 0 && list->repeated.count == 0);
+}
+
+
+/* ======================== format_arg_list surgery ======================== */
+
+/* Unfold list->repeated m times, where m >= 1.
+ Assumes list->repeated.count > 0. */
+/* Memory effects: list is destructively modified. */
+static void
+unfold_loop (struct format_arg_list *list, unsigned int m)
+{
+ unsigned int i, j, k;
+
+ if (m > 1)
+ {
+ unsigned int newcount = list->repeated.count * m;
+ ensure_repeated_alloc (list, newcount);
+ i = list->repeated.count;
+ for (k = 1; k < m; k++)
+ for (j = 0; j < list->repeated.count; j++, i++)
+ copy_element (&list->repeated.element[i], &list->repeated.element[j]);
+ list->repeated.count = newcount;
+ list->repeated.length = list->repeated.length * m;
+ }
+}
+
+/* Ensure list->initial.length := m, where m >= list->initial.length.
+ Assumes list->repeated.count > 0. */
+/* Memory effects: list is destructively modified. */
+static void
+rotate_loop (struct format_arg_list *list, unsigned int m)
+{
+ if (m == list->initial.length)
+ return;
+
+ if (list->repeated.count == 1)
+ {
+ /* Instead of multiple copies of list->repeated.element[0], a single
+ copy with higher repcount is appended to list->initial. */
+ unsigned int i, newcount;
+
+ newcount = list->initial.count + 1;
+ ensure_initial_alloc (list, newcount);
+ i = list->initial.count;
+ copy_element (&list->initial.element[i], &list->repeated.element[0]);
+ list->initial.element[i].repcount = m - list->initial.length;
+ list->initial.count = newcount;
+ list->initial.length = m;
+ }
+ else
+ {
+ unsigned int n = list->repeated.length;
+
+ /* Write m = list->initial.length + q * n + r with 0 <= r < n. */
+ unsigned int q = (m - list->initial.length) / n;
+ unsigned int r = (m - list->initial.length) % n;
+
+ /* Determine how many entries of list->repeated are needed for
+ length r. */
+ unsigned int s;
+ unsigned int t;
+
+ for (t = r, s = 0;
+ s < list->repeated.count && t >= list->repeated.element[s].repcount;
+ t -= list->repeated.element[s].repcount, s++)
+ ;
+
+ /* s must be < list->repeated.count, otherwise r would have been >= n. */
+ ASSERT (s < list->repeated.count);
+
+ /* So we need to add to list->initial:
+ q full copies of list->repeated,
+ plus the s first elements of list->repeated,
+ plus, if t > 0, a splitoff of list->repeated.element[s]. */
+ {
+ unsigned int i, j, k, newcount;
+
+ i = list->initial.count;
+ newcount = i + q * list->repeated.count + s + (t > 0 ? 1 : 0);
+ ensure_initial_alloc (list, newcount);
+ for (k = 0; k < q; k++)
+ for (j = 0; j < list->repeated.count; j++, i++)
+ copy_element (&list->initial.element[i],
+ &list->repeated.element[j]);
+ for (j = 0; j < s; j++, i++)
+ copy_element (&list->initial.element[i], &list->repeated.element[j]);
+ if (t > 0)
+ {
+ copy_element (&list->initial.element[i],
+ &list->repeated.element[j]);
+ list->initial.element[i].repcount = t;
+ i++;
+ }
+ ASSERT (i == newcount);
+ list->initial.count = newcount;
+ /* The new length of the initial segment is
+ = list->initial.length
+ + q * list->repeated.length
+ + list->repeated[0..s-1].repcount + t
+ = list->initial.length + q * n + r
+ = m.
+ */
+ list->initial.length = m;
+ }
+
+ /* And rotate list->repeated. */
+ if (r > 0)
+ {
+ unsigned int i, j, oldcount, newcount;
+ struct format_arg *newelement;
+
+ oldcount = list->repeated.count;
+ newcount = list->repeated.count + (t > 0 ? 1 : 0);
+ newelement = XNMALLOC (newcount, struct format_arg);
+ i = 0;
+ for (j = s; j < oldcount; j++, i++)
+ newelement[i] = list->repeated.element[j];
+ for (j = 0; j < s; j++, i++)
+ newelement[i] = list->repeated.element[j];
+ if (t > 0)
+ {
+ copy_element (&newelement[oldcount], &newelement[0]);
+ newelement[0].repcount -= t;
+ newelement[oldcount].repcount = t;
+ }
+ free (list->repeated.element);
+ list->repeated.element = newelement;
+ }
+ }
+}
+
+
+/* Ensure index n in the initial segment falls on a split between elements,
+ i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
+ different adjacent elements. */
+/* Memory effects: list is destructively modified. */
+static unsigned int
+initial_splitelement (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s;
+ unsigned int t;
+ unsigned int oldrepcount;
+ unsigned int newcount;
+ unsigned int i;
+
+ VERIFY_LIST (list);
+
+ if (n > list->initial.length)
+ {
+ ASSERT (list->repeated.count > 0);
+ rotate_loop (list, n);
+ ASSERT (n <= list->initial.length);
+ }
+
+ /* Determine how many entries of list->initial need to be skipped. */
+ for (t = n, s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ ;
+
+ if (t == 0)
+ return s;
+
+ ASSERT (s < list->initial.count);
+
+ /* Split the entry into two entries. */
+ oldrepcount = list->initial.element[s].repcount;
+ newcount = list->initial.count + 1;
+ ensure_initial_alloc (list, newcount);
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+1] = list->initial.element[i];
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ list->initial.element[s].repcount = t;
+ list->initial.element[s+1].repcount = oldrepcount - t;
+ list->initial.count = newcount;
+
+ VERIFY_LIST (list);
+
+ return s+1;
+}
+
+
+/* Ensure index n in the initial segment is not shared. Return its index. */
+/* Memory effects: list is destructively modified. */
+static unsigned int
+initial_unshare (struct format_arg_list *list, unsigned int n)
+{
+ /* This does the same side effects as
+ initial_splitelement (list, n);
+ initial_splitelement (list, n + 1);
+ */
+ unsigned int s;
+ unsigned int t;
+
+ VERIFY_LIST (list);
+
+ if (n >= list->initial.length)
+ {
+ ASSERT (list->repeated.count > 0);
+ rotate_loop (list, n + 1);
+ ASSERT (n < list->initial.length);
+ }
+
+ /* Determine how many entries of list->initial need to be skipped. */
+ for (t = n, s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ ;
+
+ /* s must be < list->initial.count. */
+ ASSERT (s < list->initial.count);
+
+ if (list->initial.element[s].repcount > 1)
+ {
+ /* Split the entry into at most three entries: for indices < n,
+ for index n, and for indices > n. */
+ unsigned int oldrepcount = list->initial.element[s].repcount;
+ unsigned int newcount =
+ list->initial.count + (t == 0 || t == oldrepcount - 1 ? 1 : 2);
+ ensure_initial_alloc (list, newcount);
+ if (t == 0 || t == oldrepcount - 1)
+ {
+ unsigned int i;
+
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+1] = list->initial.element[i];
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ if (t == 0)
+ {
+ list->initial.element[s].repcount = 1;
+ list->initial.element[s+1].repcount = oldrepcount - 1;
+ }
+ else
+ {
+ list->initial.element[s].repcount = oldrepcount - 1;
+ list->initial.element[s+1].repcount = 1;
+ }
+ }
+ else
+ {
+ unsigned int i;
+
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+2] = list->initial.element[i];
+ copy_element (&list->initial.element[s+2], &list->initial.element[s]);
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ list->initial.element[s].repcount = t;
+ list->initial.element[s+1].repcount = 1;
+ list->initial.element[s+2].repcount = oldrepcount - 1 - t;
+ }
+ list->initial.count = newcount;
+ if (t > 0)
+ s++;
+ }
+
+ /* Now the entry for index n has repcount 1. */
+ ASSERT (list->initial.element[s].repcount == 1);
+
+ VERIFY_LIST (list);
+
+ return s;
+}
+
+
+/* Add n unconstrained elements at the front of the list. */
+/* Memory effects: list is destructively modified. */
+static void
+shift_list (struct format_arg_list *list, unsigned int n)
+{
+ VERIFY_LIST (list);
+
+ if (n > 0)
+ {
+ unsigned int i;
+
+ grow_initial_alloc (list);
+ for (i = list->initial.count; i > 0; i--)
+ list->initial.element[i] = list->initial.element[i-1];
+ list->initial.element[0].repcount = n;
+ list->initial.element[0].presence = FCT_REQUIRED;
+ list->initial.element[0].type = FAT_OBJECT;
+ list->initial.count++;
+ list->initial.length += n;
+
+ normalize_outermost_list (list);
+ }
+
+ VERIFY_LIST (list);
+}
+
+
+/* ================= Intersection of two format_arg_lists ================= */
+
+/* Create the intersection (i.e. combined constraints) of two argument
+ constraints. Return false if the intersection is empty, i.e. if the
+ two constraints give a contradiction. */
+/* Memory effects: Freshly allocated element's sublist. */
+static bool
+make_intersected_element (struct format_arg *re,
+ const struct format_arg * e1,
+ const struct format_arg * e2)
+{
+ /* Intersect the cdr types. */
+ if (e1->presence == FCT_REQUIRED || e2->presence == FCT_REQUIRED)
+ re->presence = FCT_REQUIRED;
+ else
+ re->presence = FCT_OPTIONAL;
+
+ /* Intersect the arg types. */
+ if (e1->type == FAT_OBJECT)
+ {
+ re->type = e2->type;
+ if (re->type == FAT_LIST)
+ re->list = copy_list (e2->list);
+ }
+ else if (e2->type == FAT_OBJECT)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ re->list = copy_list (e1->list);
+ }
+ else if (e1->type == FAT_LIST
+ && (e2->type == FAT_CHARACTER_INTEGER_NULL
+ || e2->type == FAT_CHARACTER_NULL
+ || e2->type == FAT_INTEGER_NULL))
+ {
+ re->type = e1->type;
+ re->list = make_intersection_with_empty_list (e1->list);
+ if (re->list == NULL)
+ return false;
+ }
+ else if (e2->type == FAT_LIST
+ && (e1->type == FAT_CHARACTER_INTEGER_NULL
+ || e1->type == FAT_CHARACTER_NULL
+ || e1->type == FAT_INTEGER_NULL))
+ {
+ re->type = e2->type;
+ re->list = make_intersection_with_empty_list (e2->list);
+ if (re->list == NULL)
+ return false;
+ }
+ else if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
+ || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
+ || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == e2->type)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ {
+ re->list = make_intersected_list (copy_list (e1->list),
+ copy_list (e2->list));
+ if (re->list == NULL)
+ return false;
+ }
+ }
+ else
+ /* Each of FAT_CHARACTER, FAT_INTEGER, FAT_LIST, FAT_FORMATSTRING,
+ FAT_FUNCTION matches only itself. Contradiction. */
+ return false;
+
+ return true;
+}
+
+/* Append list->repeated to list->initial, and clear list->repeated. */
+/* Memory effects: list is destructively modified. */
+static void
+append_repeated_to_initial (struct format_arg_list *list)
+{
+ if (list->repeated.count > 0)
+ {
+ /* Move list->repeated over to list->initial. */
+ unsigned int i, j, newcount;
+
+ newcount = list->initial.count + list->repeated.count;
+ ensure_initial_alloc (list, newcount);
+ i = list->initial.count;
+ for (j = 0; j < list->repeated.count; j++, i++)
+ list->initial.element[i] = list->repeated.element[j];
+ list->initial.count = newcount;
+ list->initial.length = list->initial.length + list->repeated.length;
+ free (list->repeated.element);
+ list->repeated.element = NULL;
+ list->repeated.allocated = 0;
+ list->repeated.count = 0;
+ list->repeated.length = 0;
+ }
+}
+
+/* Handle a contradiction during building of a format_arg_list.
+ The list consists only of an initial segment. The repeated segment is
+ empty. This function searches the last FCT_OPTIONAL and cuts off the
+ list at this point, or - if none is found - returns NULL. */
+/* Memory effects: list is destructively modified. If NULL is returned,
+ list is freed. */
+static struct format_arg_list *
+backtrack_in_initial (struct format_arg_list *list)
+{
+ ASSERT (list->repeated.count == 0);
+
+ while (list->initial.count > 0)
+ {
+ unsigned int i = list->initial.count - 1;
+ if (list->initial.element[i].presence == FCT_REQUIRED)
+ {
+ /* Throw away this element. */
+ list->initial.length -= list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ list->initial.count = i;
+ }
+ else /* list->initial.element[i].presence == FCT_OPTIONAL */
+ {
+ /* The list must end here. */
+ list->initial.length--;
+ if (list->initial.element[i].repcount > 1)
+ list->initial.element[i].repcount--;
+ else
+ {
+ free_element (&list->initial.element[i]);
+ list->initial.count = i;
+ }
+ VERIFY_LIST (list);
+ return list;
+ }
+ }
+
+ free_list (list);
+ return NULL;
+}
+
+/* Create the intersection (i.e. combined constraints) of two argument list
+ constraints. Free both argument lists when done. Return NULL if the
+ intersection is empty, i.e. if the two constraints give a contradiction. */
+/* Memory effects: list1 and list2 are freed. The result, if non-NULL, is
+ freshly allocated. */
+static struct format_arg_list *
+make_intersected_list (struct format_arg_list *list1,
+ struct format_arg_list *list2)
+{
+ struct format_arg_list *result;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
+ {
+ unsigned int n1 = list1->repeated.length;
+ unsigned int n2 = list2->repeated.length;
+ unsigned int g = gcd (n1, n2);
+ unsigned int m1 = n2 / g; /* = lcm(n1,n2) / n1 */
+ unsigned int m2 = n1 / g; /* = lcm(n1,n2) / n2 */
+
+ unfold_loop (list1, m1);
+ unfold_loop (list2, m2);
+ /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
+ }
+
+ if (list1->repeated.length > 0 || list2->repeated.length > 0)
+ /* Step 2: Ensure the initial segment of the result can be computed
+ from the initial segments of list1 and list2. If both have a
+ repeated segment, this means to ensure
+ list1->initial.length == list2->initial.length. */
+ {
+ unsigned int m = MAX (list1->initial.length, list2->initial.length);
+
+ if (list1->repeated.length > 0)
+ rotate_loop (list1, m);
+ if (list2->repeated.length > 0)
+ rotate_loop (list2, m);
+ }
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ {
+ ASSERT (list1->initial.length == list2->initial.length);
+ ASSERT (list1->repeated.length == list2->repeated.length);
+ }
+
+ /* Step 3: Allocate the result. */
+ result = XMALLOC (struct format_arg_list);
+ result->initial.count = 0;
+ result->initial.allocated = 0;
+ result->initial.element = NULL;
+ result->initial.length = 0;
+ result->repeated.count = 0;
+ result->repeated.allocated = 0;
+ result->repeated.element = NULL;
+ result->repeated.length = 0;
+
+ /* Step 4: Elementwise intersection of list1->initial, list2->initial. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->initial.element; c1 = list1->initial.count;
+ e2 = list2->initial.element; c2 = list2->initial.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Intersect the argument types. */
+ if (!make_intersected_element (re, e1, e2))
+ {
+ /* If re->presence == FCT_OPTIONAL, the result list ends here. */
+ if (re->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+
+ result->initial.count++;
+ result->initial.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ if (list1->repeated.count == 0 && list2->repeated.count == 0)
+ {
+ /* Intersecting two finite lists. */
+ if (c1 > 0)
+ {
+ /* list1 longer than list2. */
+ if (e1->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ }
+ else if (c2 > 0)
+ {
+ /* list2 longer than list1. */
+ if (e2->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ }
+ goto done;
+ }
+ else if (list1->repeated.count == 0)
+ {
+ /* Intersecting a finite and an infinite list. */
+ ASSERT (c1 == 0);
+ if ((c2 > 0 ? e2->presence : list2->repeated.element[0].presence)
+ == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+ else if (list2->repeated.count == 0)
+ {
+ /* Intersecting an infinite and a finite list. */
+ ASSERT (c2 == 0);
+ if ((c1 > 0 ? e1->presence : list1->repeated.element[0].presence)
+ == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+ /* Intersecting two infinite lists. */
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ /* Step 5: Elementwise intersection of list1->repeated, list2->repeated. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->repeated.element; c1 = list1->repeated.count;
+ e2 = list2->repeated.element; c2 = list2->repeated.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->repeated. */
+ grow_repeated_alloc (result);
+ re = &result->repeated.element[result->repeated.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Intersect the argument types. */
+ if (!make_intersected_element (re, e1, e2))
+ {
+ bool re_is_required = re->presence == FCT_REQUIRED;
+
+ append_repeated_to_initial (result);
+
+ /* If re->presence == FCT_OPTIONAL, the result list ends here. */
+ if (re_is_required)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+
+ goto done;
+ }
+
+ result->repeated.count++;
+ result->repeated.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ done:
+ free_list (list1);
+ free_list (list2);
+ if (result != NULL)
+ {
+ /* Undo the loop unfolding and unrolling done above. */
+ normalize_outermost_list (result);
+ VERIFY_LIST (result);
+ }
+ return result;
+}
+
+
+/* Create the intersection of an argument list and the empty list.
+ Return NULL if the intersection is empty. */
+/* Memory effects: The result, if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+make_intersection_with_empty_list (struct format_arg_list *list)
+{
+#if 0 /* equivalent but slower */
+ return make_intersected_list (copy_list (list), make_empty_list ());
+#else
+ if (list->initial.count > 0
+ ? list->initial.element[0].presence == FCT_REQUIRED
+ : list->repeated.count > 0
+ && list->repeated.element[0].presence == FCT_REQUIRED)
+ return NULL;
+ else
+ return make_empty_list ();
+#endif
+}
+
+
+#ifdef unused
+/* Create the intersection of two argument list constraints. NULL stands
+ for an impossible situation, i.e. a contradiction. */
+/* Memory effects: list1 and list2 are freed if non-NULL. The result,
+ if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+intersection (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ if (list1 != NULL)
+ {
+ if (list2 != NULL)
+ return make_intersected_list (list1, list2);
+ else
+ {
+ free_list (list1);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (list2 != NULL)
+ {
+ free_list (list2);
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+}
+#endif
+
+
+/* ===================== Union of two format_arg_lists ===================== */
+
+/* Create the union (i.e. alternative constraints) of two argument
+ constraints. */
+static void
+make_union_element (struct format_arg *re,
+ const struct format_arg * e1,
+ const struct format_arg * e2)
+{
+ /* Union of the cdr types. */
+ if (e1->presence == FCT_REQUIRED && e2->presence == FCT_REQUIRED)
+ re->presence = FCT_REQUIRED;
+ else /* Either one of them is FCT_OPTIONAL. */
+ re->presence = FCT_OPTIONAL;
+
+ /* Union of the arg types. */
+ if (e1->type == e2->type)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ re->list = make_union_list (copy_list (e1->list),
+ copy_list (e2->list));
+ }
+ else if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
+ || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
+ || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_LIST && is_empty_list (e1->list))
+ {
+ if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ || e2->type == FAT_CHARACTER_NULL
+ || e2->type == FAT_INTEGER_NULL)
+ re->type = e2->type;
+ else if (e2->type == FAT_CHARACTER)
+ re->type = FAT_CHARACTER_NULL;
+ else if (e2->type == FAT_INTEGER)
+ re->type = FAT_INTEGER_NULL;
+ else
+ re->type = FAT_OBJECT;
+ }
+ else if (e2->type == FAT_LIST && is_empty_list (e2->list))
+ {
+ if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ || e1->type == FAT_CHARACTER_NULL
+ || e1->type == FAT_INTEGER_NULL)
+ re->type = e1->type;
+ else if (e1->type == FAT_CHARACTER)
+ re->type = FAT_CHARACTER_NULL;
+ else if (e1->type == FAT_INTEGER)
+ re->type = FAT_INTEGER_NULL;
+ else
+ re->type = FAT_OBJECT;
+ }
+ else if ((e1->type == FAT_CHARACTER || e1->type == FAT_CHARACTER_NULL)
+ && (e2->type == FAT_INTEGER || e2->type == FAT_INTEGER_NULL))
+ {
+ re->type = FAT_CHARACTER_INTEGER_NULL;
+ }
+ else if ((e2->type == FAT_CHARACTER || e2->type == FAT_CHARACTER_NULL)
+ && (e1->type == FAT_INTEGER || e1->type == FAT_INTEGER_NULL))
+ {
+ re->type = FAT_CHARACTER_INTEGER_NULL;
+ }
+ else
+ {
+ /* Other union types are too hard to describe precisely. */
+ re->type = FAT_OBJECT;
+ }
+}
+
+/* Create the union (i.e. alternative constraints) of two argument list
+ constraints. Free both argument lists when done. */
+/* Memory effects: list1 and list2 are freed. The result is freshly
+ allocated. */
+static struct format_arg_list *
+make_union_list (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ struct format_arg_list *result;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ {
+ /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
+ {
+ unsigned int n1 = list1->repeated.length;
+ unsigned int n2 = list2->repeated.length;
+ unsigned int g = gcd (n1, n2);
+ unsigned int m1 = n2 / g; /* = lcm(n1,n2) / n1 */
+ unsigned int m2 = n1 / g; /* = lcm(n1,n2) / n2 */
+
+ unfold_loop (list1, m1);
+ unfold_loop (list2, m2);
+ /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
+ }
+
+ /* Step 2: Ensure that list1->initial.length == list2->initial.length. */
+ {
+ unsigned int m = MAX (list1->initial.length, list2->initial.length);
+
+ rotate_loop (list1, m);
+ rotate_loop (list2, m);
+ }
+
+ ASSERT (list1->initial.length == list2->initial.length);
+ ASSERT (list1->repeated.length == list2->repeated.length);
+ }
+ else if (list1->repeated.length > 0)
+ {
+ /* Ensure the initial segment of the result can be computed from the
+ initial segment of list1. */
+ if (list2->initial.length >= list1->initial.length)
+ {
+ rotate_loop (list1, list2->initial.length);
+ if (list1->repeated.element[0].presence == FCT_REQUIRED)
+ rotate_loop (list1, list1->initial.length + 1);
+ }
+ }
+ else if (list2->repeated.length > 0)
+ {
+ /* Ensure the initial segment of the result can be computed from the
+ initial segment of list2. */
+ if (list1->initial.length >= list2->initial.length)
+ {
+ rotate_loop (list2, list1->initial.length);
+ if (list2->repeated.element[0].presence == FCT_REQUIRED)
+ rotate_loop (list2, list2->initial.length + 1);
+ }
+ }
+
+ /* Step 3: Allocate the result. */
+ result = XMALLOC (struct format_arg_list);
+ result->initial.count = 0;
+ result->initial.allocated = 0;
+ result->initial.element = NULL;
+ result->initial.length = 0;
+ result->repeated.count = 0;
+ result->repeated.allocated = 0;
+ result->repeated.element = NULL;
+ result->repeated.length = 0;
+
+ /* Step 4: Elementwise union of list1->initial, list2->initial. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->initial.element; c1 = list1->initial.count;
+ e2 = list2->initial.element; c2 = list2->initial.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Union of the argument types. */
+ make_union_element (re, e1, e2);
+
+ result->initial.count++;
+ result->initial.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ if (c1 > 0)
+ {
+ /* list2 already terminated, but still more elements in list1->initial.
+ Copy them all, but turn the first presence to FCT_OPTIONAL. */
+ ASSERT (list2->repeated.count == 0);
+
+ if (e1->presence == FCT_REQUIRED)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e1);
+ re->presence = FCT_OPTIONAL;
+ re->repcount = 1;
+ result->initial.count++;
+ result->initial.length += 1;
+ e1->repcount -= 1;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ }
+
+ /* Ensure room in result->initial. */
+ ensure_initial_alloc (result, result->initial.count + c1);
+ while (c1 > 0)
+ {
+ struct format_arg *re;
+
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e1);
+ result->initial.count++;
+ result->initial.length += re->repcount;
+ e1++;
+ c1--;
+ }
+ }
+ else if (c2 > 0)
+ {
+ /* list1 already terminated, but still more elements in list2->initial.
+ Copy them all, but turn the first presence to FCT_OPTIONAL. */
+ ASSERT (list1->repeated.count == 0);
+
+ if (e2->presence == FCT_REQUIRED)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e2);
+ re->presence = FCT_OPTIONAL;
+ re->repcount = 1;
+ result->initial.count++;
+ result->initial.length += 1;
+ e2->repcount -= 1;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ /* Ensure room in result->initial. */
+ ensure_initial_alloc (result, result->initial.count + c2);
+ while (c2 > 0)
+ {
+ struct format_arg *re;
+
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e2);
+ result->initial.count++;
+ result->initial.length += re->repcount;
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ /* Step 5: Elementwise union of list1->repeated, list2->repeated. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->repeated.element; c1 = list1->repeated.count;
+ e2 = list2->repeated.element; c2 = list2->repeated.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->repeated. */
+ grow_repeated_alloc (result);
+ re = &result->repeated.element[result->repeated.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Union of the argument types. */
+ make_union_element (re, e1, e2);
+
+ result->repeated.count++;
+ result->repeated.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+ else if (list1->repeated.length > 0)
+ {
+ /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
+ initial segment. Just copy the repeated segment of list1. */
+ unsigned int i;
+
+ result->repeated.count = list1->repeated.count;
+ result->repeated.allocated = result->repeated.count;
+ result->repeated.element =
+ XNMALLOC (result->repeated.allocated, struct format_arg);
+ for (i = 0; i < list1->repeated.count; i++)
+ copy_element (&result->repeated.element[i],
+ &list1->repeated.element[i]);
+ result->repeated.length = list1->repeated.length;
+ }
+ else if (list2->repeated.length > 0)
+ {
+ /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
+ initial segment. Just copy the repeated segment of list2. */
+ unsigned int i;
+
+ result->repeated.count = list2->repeated.count;
+ result->repeated.allocated = result->repeated.count;
+ result->repeated.element =
+ XNMALLOC (result->repeated.allocated, struct format_arg);
+ for (i = 0; i < list2->repeated.count; i++)
+ copy_element (&result->repeated.element[i],
+ &list2->repeated.element[i]);
+ result->repeated.length = list2->repeated.length;
+ }
+
+ free_list (list1);
+ free_list (list2);
+ /* Undo the loop unfolding and unrolling done above. */
+ normalize_outermost_list (result);
+ VERIFY_LIST (result);
+ return result;
+}
+
+
+/* Create the union of an argument list and the empty list. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_union_with_empty_list (struct format_arg_list *list)
+{
+#if 0 /* equivalent but slower */
+ return make_union_list (list, make_empty_list ());
+#else
+ VERIFY_LIST (list);
+
+ if (list->initial.count > 0
+ ? list->initial.element[0].presence == FCT_REQUIRED
+ : list->repeated.count > 0
+ && list->repeated.element[0].presence == FCT_REQUIRED)
+ {
+ initial_splitelement (list, 1);
+ ASSERT (list->initial.count > 0);
+ ASSERT (list->initial.element[0].repcount == 1);
+ ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
+ list->initial.element[0].presence = FCT_OPTIONAL;
+
+ /* We might need to merge list->initial.element[0] and
+ list->initial.element[1]. */
+ normalize_outermost_list (list);
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+#endif
+}
+
+
+/* Create the union of two argument list constraints. NULL stands for an
+ impossible situation, i.e. a contradiction. */
+/* Memory effects: list1 and list2 are freed if non-NULL. The result,
+ if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+union (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ if (list1 != NULL)
+ {
+ if (list2 != NULL)
+ return make_union_list (list1, list2);
+ else
+ return list1;
+ }
+ else
+ {
+ if (list2 != NULL)
+ return list2;
+ else
+ return NULL;
+ }
+}
+
+
+/* =========== Adding specific constraints to a format_arg_list =========== */
+
+
+/* Test whether arguments 0..n are required arguments in a list. */
+static bool
+is_required (const struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s;
+ unsigned int t;
+
+ /* We'll check whether the first n+1 presence flags are FCT_REQUIRED. */
+ t = n + 1;
+
+ /* Walk the list->initial segment. */
+ for (s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ if (list->initial.element[s].presence != FCT_REQUIRED)
+ return false;
+
+ if (t == 0)
+ return true;
+
+ if (s < list->initial.count)
+ {
+ if (list->initial.element[s].presence != FCT_REQUIRED)
+ return false;
+ else
+ return true;
+ }
+
+ /* Walk the list->repeated segment. */
+ if (list->repeated.count == 0)
+ return false;
+
+ for (s = 0;
+ s < list->repeated.count && t >= list->repeated.element[s].repcount;
+ t -= list->repeated.element[s].repcount, s++)
+ if (list->repeated.element[s].presence != FCT_REQUIRED)
+ return false;
+
+ if (t == 0)
+ return true;
+
+ if (s < list->repeated.count)
+ {
+ if (list->repeated.element[s].presence != FCT_REQUIRED)
+ return false;
+ else
+ return true;
+ }
+
+ /* The list->repeated segment consists only of FCT_REQUIRED. So,
+ regardless how many more passes through list->repeated would be
+ needed until t becomes 0, the result is true. */
+ return true;
+}
+
+
+/* Add a constraint to an argument list, namely that the arguments 0...n are
+ present. NULL stands for an impossible situation, i.e. a contradiction. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_required_constraint (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int i, rest;
+
+ if (list == NULL)
+ return NULL;
+
+ VERIFY_LIST (list);
+
+ if (list->repeated.count == 0 && list->initial.length <= n)
+ {
+ /* list is already constrained to have at most length n.
+ Contradiction. */
+ free_list (list);
+ return NULL;
+ }
+
+ initial_splitelement (list, n + 1);
+
+ for (i = 0, rest = n + 1; rest > 0; )
+ {
+ list->initial.element[i].presence = FCT_REQUIRED;
+ rest -= list->initial.element[i].repcount;
+ i++;
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ never present. NULL stands for an impossible situation, i.e. a
+ contradiction. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_end_constraint (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s, i;
+ enum format_cdr_type n_presence;
+
+ if (list == NULL)
+ return NULL;
+
+ VERIFY_LIST (list);
+
+ if (list->repeated.count == 0 && list->initial.length <= n)
+ /* list is already constrained to have at most length n. */
+ return list;
+
+ s = initial_splitelement (list, n);
+ n_presence =
+ (s < list->initial.count
+ ? /* n < list->initial.length */ list->initial.element[s].presence
+ : /* n >= list->initial.length */ list->repeated.element[0].presence);
+
+ for (i = s; i < list->initial.count; i++)
+ {
+ list->initial.length -= list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ }
+ list->initial.count = s;
+
+ for (i = 0; i < list->repeated.count; i++)
+ free_element (&list->repeated.element[i]);
+ if (list->repeated.element != NULL)
+ free (list->repeated.element);
+ list->repeated.element = NULL;
+ list->repeated.allocated = 0;
+ list->repeated.count = 0;
+ list->repeated.length = 0;
+
+ if (n_presence == FCT_REQUIRED)
+ return backtrack_in_initial (list);
+ else
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ of a given type. NULL stands for an impossible situation, i.e. a
+ contradiction. Assumes a preceding add_required_constraint (list, n). */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_type_constraint (struct format_arg_list *list, unsigned int n,
+ enum format_arg_type type)
+{
+ unsigned int s;
+ struct format_arg newconstraint;
+ struct format_arg tmpelement;
+
+ if (list == NULL)
+ return NULL;
+
+ /* Through the previous add_required_constraint, we can assume
+ list->initial.length >= n+1. */
+
+ s = initial_unshare (list, n);
+
+ newconstraint.presence = FCT_OPTIONAL;
+ newconstraint.type = type;
+ if (!make_intersected_element (&tmpelement,
+ &list->initial.element[s], &newconstraint))
+ return add_end_constraint (list, n);
+ free_element (&list->initial.element[s]);
+ list->initial.element[s].type = tmpelement.type;
+ list->initial.element[s].list = tmpelement.list;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ of a given list type. NULL stands for an impossible situation, i.e. a
+ contradiction. Assumes a preceding add_required_constraint (list, n). */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_listtype_constraint (struct format_arg_list *list, unsigned int n,
+ enum format_arg_type type,
+ struct format_arg_list *sublist)
+{
+ unsigned int s;
+ struct format_arg newconstraint;
+ struct format_arg tmpelement;
+
+ if (list == NULL)
+ return NULL;
+
+ /* Through the previous add_required_constraint, we can assume
+ list->initial.length >= n+1. */
+
+ s = initial_unshare (list, n);
+
+ newconstraint.presence = FCT_OPTIONAL;
+ newconstraint.type = type;
+ newconstraint.list = sublist;
+ if (!make_intersected_element (&tmpelement,
+ &list->initial.element[s], &newconstraint))
+ return add_end_constraint (list, n);
+ free_element (&list->initial.element[s]);
+ list->initial.element[s].type = tmpelement.type;
+ list->initial.element[s].list = tmpelement.list;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* ============= Subroutines used by the format string parser ============= */
+
+static void
+add_req_type_constraint (struct format_arg_list **listp,
+ unsigned int position, enum format_arg_type type)
+{
+ *listp = add_required_constraint (*listp, position);
+ *listp = add_type_constraint (*listp, position, type);
+}
+
+
+static void
+add_req_listtype_constraint (struct format_arg_list **listp,
+ unsigned int position, enum format_arg_type type,
+ struct format_arg_list *sublist)
+{
+ *listp = add_required_constraint (*listp, position);
+ *listp = add_listtype_constraint (*listp, position, type, sublist);
+}
+
+
+/* Create an endless repeated list whose elements are lists constrained
+ by sublist. */
+/* Memory effects: sublist is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_repeated_list_of_lists (struct format_arg_list *sublist)
+{
+ if (sublist == NULL)
+ /* The list cannot have a single element. */
+ return make_empty_list ();
+ else
+ {
+ struct format_arg_list *listlist;
+
+ listlist = XMALLOC (struct format_arg_list);
+
+ listlist->initial.count = 0;
+ listlist->initial.allocated = 0;
+ listlist->initial.element = NULL;
+ listlist->initial.length = 0;
+ listlist->repeated.count = 1;
+ listlist->repeated.allocated = 1;
+ listlist->repeated.element = XNMALLOC (1, struct format_arg);
+ listlist->repeated.element[0].repcount = 1;
+ listlist->repeated.element[0].presence = FCT_OPTIONAL;
+ listlist->repeated.element[0].type = FAT_LIST;
+ listlist->repeated.element[0].list = sublist;
+ listlist->repeated.length = 1;
+
+ VERIFY_LIST (listlist);
+
+ return listlist;
+ }
+}
+
+
+/* Create an endless repeated list which represents the union of a finite
+ number of copies of L, each time shifted by period:
+ ()
+ L
+ L and (*^period L)
+ L and (*^period L) and (*^{2 period} L)
+ L and (*^period L) and (*^{2 period} L) and (*^{3 period} L)
+ ...
+ */
+/* Memory effects: sublist is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_repeated_list (struct format_arg_list *sublist, unsigned int period)
+{
+ struct segment tmp;
+ struct segment *srcseg;
+ struct format_arg_list *list;
+ unsigned int p, n, i, si, ti, j, sj, tj, splitindex, newcount;
+ bool ended;
+
+ VERIFY_LIST (sublist);
+
+ ASSERT (period > 0);
+
+ if (sublist->repeated.count == 0)
+ {
+ /* L is a finite list. */
+
+ if (sublist->initial.length < period)
+ /* L and (*^period L) is a contradition, so we need to consider
+ only 1 and 0 iterations. */
+ return make_union_with_empty_list (sublist);
+
+ srcseg = &sublist->initial;
+ p = period;
+ }
+ else
+ {
+ /* L is an infinite list. */
+ /* p := lcm (period, period of L) */
+ unsigned int Lp = sublist->repeated.length;
+ unsigned int m = period / gcd (period, Lp); /* = lcm(period,Lp) / Lp */
+
+ unfold_loop (sublist, m);
+ p = m * Lp;
+
+ /* Concatenate the initial and the repeated segments into a single
+ segment. */
+ tmp.count = sublist->initial.count + sublist->repeated.count;
+ tmp.allocated = tmp.count;
+ tmp.element = XNMALLOC (tmp.allocated, struct format_arg);
+ for (i = 0; i < sublist->initial.count; i++)
+ tmp.element[i] = sublist->initial.element[i];
+ for (j = 0; j < sublist->repeated.count; i++, j++)
+ tmp.element[i] = sublist->initial.element[j];
+ tmp.length = sublist->initial.length + sublist->repeated.length;
+
+ srcseg = &tmp;
+ }
+
+ n = srcseg->length;
+
+ /* Example: n = 7, p = 2
+ Let L = (A B C D E F G).
+
+ L = A B C D E F G
+ L & L<<p = A B C&A D&B E&C F&D G&E
+ L & L<<p & L<<2p = A B C&A D&B E&C&A F&D&B G&E&C
+ ... = A B C&A D&B E&C&A F&D&B G&E&C&A
+
+ Thus the result has an initial segment of length n - p and a period
+ of p, and can be computed by floor(n/p) intersection operations.
+ Or by a single incremental intersection operation, going from left
+ to right. */
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 0;
+ list->repeated.allocated = 0;
+ list->repeated.element = NULL;
+ list->repeated.length = 0;
+
+ /* Sketch:
+ for (i = 0; i < p; i++)
+ list->initial.element[i] = srcseg->element[i];
+ list->initial.element[0].presence = FCT_OPTIONAL; // union with empty list
+ for (i = p, j = 0; i < n; i++, j++)
+ list->initial.element[i] = srcseg->element[i] & list->initial.element[j];
+ */
+
+ ended = false;
+
+ i = 0, ti = 0, si = 0;
+ while (i < p)
+ {
+ unsigned int k = MIN (srcseg->element[si].repcount - ti, p - i);
+
+ /* Ensure room in list->initial. */
+ grow_initial_alloc (list);
+ copy_element (&list->initial.element[list->initial.count],
+ &srcseg->element[si]);
+ list->initial.element[list->initial.count].repcount = k;
+ list->initial.count++;
+ list->initial.length += k;
+
+ i += k;
+ ti += k;
+ if (ti == srcseg->element[si].repcount)
+ {
+ ti = 0;
+ si++;
+ }
+ }
+
+ ASSERT (list->initial.count > 0);
+ if (list->initial.element[0].presence == FCT_REQUIRED)
+ {
+ initial_splitelement (list, 1);
+ ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
+ ASSERT (list->initial.element[0].repcount == 1);
+ list->initial.element[0].presence = FCT_OPTIONAL;
+ }
+
+ j = 0, tj = 0, sj = 0;
+ while (i < n)
+ {
+ unsigned int k =
+ MIN (srcseg->element[si].repcount - ti,
+ list->initial.element[sj].repcount - tj);
+
+ /* Ensure room in list->initial. */
+ grow_initial_alloc (list);
+ if (!make_intersected_element (&list->initial.element[list->initial.count],
+ &srcseg->element[si],
+ &list->initial.element[sj]))
+ {
+ if (list->initial.element[list->initial.count].presence == FCT_REQUIRED)
+ {
+ /* Contradiction. Backtrack. */
+ list = backtrack_in_initial (list);
+ ASSERT (list != NULL); /* at least the empty list is valid */
+ return list;
+ }
+ else
+ {
+ /* The list ends here. */
+ ended = true;
+ break;
+ }
+ }
+ list->initial.element[list->initial.count].repcount = k;
+ list->initial.count++;
+ list->initial.length += k;
+
+ i += k;
+ ti += k;
+ if (ti == srcseg->element[si].repcount)
+ {
+ ti = 0;
+ si++;
+ }
+
+ j += k;
+ tj += k;
+ if (tj == list->initial.element[sj].repcount)
+ {
+ tj = 0;
+ sj++;
+ }
+ }
+ if (!ended)
+ ASSERT (list->initial.length == n);
+
+ /* Add optional exit points at 0, period, 2*period etc.
+ FIXME: Not sure this is correct in all cases. */
+ for (i = 0; i < list->initial.length; i += period)
+ {
+ si = initial_unshare (list, i);
+ list->initial.element[si].presence = FCT_OPTIONAL;
+ }
+
+ if (!ended)
+ {
+ /* Now split off the repeated part. */
+ splitindex = initial_splitelement (list, n - p);
+ newcount = list->initial.count - splitindex;
+ if (newcount > list->repeated.allocated)
+ {
+ list->repeated.allocated = newcount;
+ list->repeated.element = XNMALLOC (newcount, struct format_arg);
+ }
+ for (i = splitindex, j = 0; i < n; i++, j++)
+ list->repeated.element[j] = list->initial.element[i];
+ list->repeated.count = newcount;
+ list->repeated.length = p;
+ list->initial.count = splitindex;
+ list->initial.length = n - p;
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* ================= Handling of format string directives ================= */
+
+/* Possible signatures of format directives. */
+static const enum format_arg_type I [1] = { FAT_INTEGER_NULL };
+static const enum format_arg_type II [2] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL
+};
+static const enum format_arg_type ICCI [4] = {
+ FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_INTEGER_NULL
+};
+static const enum format_arg_type IIIC [4] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL
+};
+static const enum format_arg_type IICCI [5] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL,
+ FAT_INTEGER_NULL
+};
+static const enum format_arg_type IIICC [5] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL,
+ FAT_CHARACTER_NULL
+};
+static const enum format_arg_type IIIICCC [7] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL,
+ FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL
+};
+static const enum format_arg_type THREE [3] = {
+ FAT_CHARACTER_INTEGER_NULL, FAT_CHARACTER_INTEGER_NULL,
+ FAT_CHARACTER_INTEGER_NULL
+};
+
+
+/* Check the parameters. For V params, add the constraint to the argument
+ list. Return false and fill in *invalid_reason if the format string is
+ invalid. */
+static bool
+check_params (struct format_arg_list **listp,
+ unsigned int paramcount, struct param *params,
+ unsigned int t_count, const enum format_arg_type *t_types,
+ unsigned int directives, char **invalid_reason)
+{
+ unsigned int orig_paramcount = paramcount;
+ unsigned int orig_t_count = t_count;
+
+ for (; paramcount > 0 && t_count > 0;
+ params++, paramcount--, t_types++, t_count--)
+ {
+ switch (*t_types)
+ {
+ case FAT_CHARACTER_INTEGER_NULL:
+ break;
+ case FAT_CHARACTER_NULL:
+ switch (params->type)
+ {
+ case PT_NIL: case PT_CHARACTER: case PT_V:
+ break;
+ case PT_INTEGER: case PT_ARGCOUNT:
+ /* wrong param type */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "integer", "character");
+ return false;
+ }
+ break;
+ case FAT_INTEGER_NULL:
+ switch (params->type)
+ {
+ case PT_NIL: case PT_INTEGER: case PT_ARGCOUNT: case PT_V:
+ break;
+ case PT_CHARACTER:
+ /* wrong param type */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "character", "integer");
+ return false;
+ }
+ break;
+ default:
+ abort ();
+ }
+ if (params->type == PT_V)
+ {
+ int position = params->value;
+ if (position >= 0)
+ add_req_type_constraint (listp, position, *t_types);
+ }
+ }
+
+ for (; paramcount > 0; params++, paramcount--)
+ switch (params->type)
+ {
+ case PT_NIL:
+ break;
+ case PT_CHARACTER: case PT_INTEGER: case PT_ARGCOUNT:
+ /* too many params for directive */
+ *invalid_reason =
+ xasprintf (ngettext ("In the directive number %u, too many parameters are given; expected at most %u parameter.",
+ "In the directive number %u, too many parameters are given; expected at most %u parameters.",
+ orig_t_count),
+ directives, orig_t_count);
+ return false;
+ case PT_V:
+ /* Force argument to be NIL. */
+ {
+ int position = params->value;
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (listp, position,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+/* Handle the parameters, without a priori type information.
+ For V params, add the constraint to the argument list.
+ Return false and fill in *invalid_reason if the format string is
+ invalid. */
+static bool
+nocheck_params (struct format_arg_list **listp,
+ unsigned int paramcount, struct param *params,
+ unsigned int directives, char **invalid_reason)
+{
+ (void) directives;
+ (void) invalid_reason;
+
+ for (; paramcount > 0; params++, paramcount--)
+ if (params->type == PT_V)
+ {
+ int position = params->value;
+ add_req_type_constraint (listp, position, FAT_CHARACTER_INTEGER_NULL);
+ }
+
+ return true;
+}
+
+
+/* ======================= The format string parser ======================= */
+
+/* Parse a piece of format string, until the matching terminating format
+ directive is encountered.
+ format is the remainder of the format string.
+ position is the position in this argument list, if known, or -1 if unknown.
+ list represents the argument list constraints at the current parse point.
+ NULL stands for a contradiction.
+ escape represents the union of the argument list constraints at all the
+ currently pending FORMAT-UP-AND-OUT points. NULL stands for a contradiction
+ or an empty union.
+ All four are updated upon valid return.
+ *separatorp is set to true if the parse terminated due to a ~; separator,
+ more precisely to 2 if with colon, or to 1 if without colon.
+ spec is the global struct spec.
+ terminator is the directive that terminates this parse.
+ separator specifies if ~; separators are allowed.
+ fdi is an array to be filled with format directive indicators, or NULL.
+ If the format string is invalid, false is returned and *invalid_reason is
+ set to an error message explaining why. */
+static bool
+parse_upto (const char **formatp,
+ int *positionp, struct format_arg_list **listp,
+ struct format_arg_list **escapep, int *separatorp,
+ struct spec *spec, char terminator, bool separator,
+ char *fdi, char **invalid_reason)
+{
+ const char *format = *formatp;
+ const char *const format_start = format;
+ int position = *positionp;
+ struct format_arg_list *list = *listp;
+ struct format_arg_list *escape = *escapep;
+
+ for (; *format != '\0'; )
+ if (*format++ == '~')
+ {
+ bool colon_p = false;
+ bool atsign_p = false;
+ unsigned int paramcount = 0;
+ struct param *params = NULL;
+
+ FDI_SET (format - 1, FMTDIR_START);
+
+ /* Count number of directives. */
+ spec->directives++;
+
+ /* Parse parameters. */
+ for (;;)
+ {
+ enum param_type type = PT_NIL;
+ int value = 0;
+
+ if (c_isdigit (*format))
+ {
+ type = PT_INTEGER;
+ do
+ {
+ value = 10 * value + (*format - '0');
+ format++;
+ }
+ while (c_isdigit (*format));
+ }
+ else if (*format == '+' || *format == '-')
+ {
+ bool negative = (*format == '-');
+ type = PT_INTEGER;
+ format++;
+ if (!c_isdigit (*format))
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '%c' is not followed by a digit."), spec->directives, format[-1]);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ return false;
+ }
+ do
+ {
+ value = 10 * value + (*format - '0');
+ format++;
+ }
+ while (c_isdigit (*format));
+ if (negative)
+ value = -value;
+ }
+ else if (*format == '\'')
+ {
+ type = PT_CHARACTER;
+ format++;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ format++;
+ }
+ else if (*format == 'V' || *format == 'v')
+ {
+ type = PT_V;
+ format++;
+ value = position;
+ /* Consumes an argument. */
+ if (position >= 0)
+ position++;
+ }
+ else if (*format == '#')
+ {
+ type = PT_ARGCOUNT;
+ format++;
+ }
+
+ params =
+ (struct param *)
+ xrealloc (params, (paramcount + 1) * sizeof (struct param));
+ params[paramcount].type = type;
+ params[paramcount].value = value;
+ paramcount++;
+
+ if (*format == ',')
+ format++;
+ else
+ break;
+ }
+
+ /* Parse modifiers. */
+ for (;;)
+ {
+ if (*format == ':')
+ {
+ format++;
+ colon_p = true;
+ }
+ else if (*format == '@')
+ {
+ format++;
+ atsign_p = true;
+ }
+ else
+ break;
+ }
+
+ /* Parse directive. */
+ switch (*format++)
+ {
+ case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
+ case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
+ if (!check_params (&list, paramcount, params, 4, IIIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case 'W': case 'w': /* 22.3.4.3 FORMAT-WRITE */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case 'D': case 'd': /* 22.3.2.2 FORMAT-DECIMAL */
+ case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
+ case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
+ case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
+ if (!check_params (&list, paramcount, params, 4, ICCI,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_INTEGER);
+ break;
+
+ case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
+ if (!check_params (&list, paramcount, params, 5, IICCI,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_INTEGER);
+ break;
+
+ case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (colon_p)
+ {
+ /* Go back by 1 argument. */
+ if (position > 0)
+ position--;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case 'C': case 'c': /* 22.3.1.1 FORMAT-CHARACTER */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_CHARACTER);
+ break;
+
+ case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
+ if (!check_params (&list, paramcount, params, 5, IIICC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
+ case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
+ if (!check_params (&list, paramcount, params, 7, IIIICCC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
+ if (!check_params (&list, paramcount, params, 4, IIIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case '%': /* 22.3.1.2 FORMAT-TERPRI */
+ case '&': /* 22.3.1.3 FORMAT-FRESH-LINE */
+ case '|': /* 22.3.1.4 FORMAT-PAGE */
+ case '~': /* 22.3.1.5 FORMAT-TILDE */
+ case 'I': case 'i': /* 22.3.5.3 */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case '\n': /* 22.3.9.3 #\Newline */
+ case '_': /* 22.3.5.1 */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case 'T': case 't': /* 22.3.6.1 FORMAT-TABULATE */
+ if (!check_params (&list, paramcount, params, 2, II,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case '*': /* 22.3.7.1 FORMAT-GOTO */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ {
+ int n; /* value of first parameter */
+ if (paramcount == 0
+ || (paramcount >= 1 && params[0].type == PT_NIL))
+ n = (atsign_p ? 0 : 1);
+ else if (paramcount >= 1 && params[0].type == PT_INTEGER)
+ n = params[0].value;
+ else
+ {
+ /* Unknown argument, leads to an unknown position. */
+ position = -1;
+ break;
+ }
+ if (n < 0)
+ {
+ /* invalid argument */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the argument %d is negative."), spec->directives, n);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (atsign_p)
+ {
+ /* Absolute goto. */
+ position = n;
+ }
+ else if (colon_p)
+ {
+ /* Backward goto. */
+ if (n > 0)
+ {
+ if (position >= 0)
+ {
+ if (position >= n)
+ position -= n;
+ else
+ position = 0;
+ }
+ else
+ position = -1;
+ }
+ }
+ else
+ {
+ /* Forward goto. */
+ if (position >= 0)
+ position += n;
+ }
+ }
+ break;
+
+ case '?': /* 22.3.7.6 FORMAT-INDIRECTION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
+ if (atsign_p)
+ position = -1;
+ else
+ if (position >= 0)
+ {
+ struct format_arg_list *sublist = make_unconstrained_list ();
+ add_req_listtype_constraint (&list, position++,
+ FAT_LIST, sublist);
+ free_list (sublist);
+ }
+ break;
+
+ case '/': /* 22.3.5.4 FORMAT-CALL-USER-FUNCTION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ while (*format != '\0' && *format != '/')
+ format++;
+ if (*format == '\0')
+ {
+ *invalid_reason =
+ xstrdup (_("The string ends in the middle of a ~/.../ directive."));
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ format++;
+ break;
+
+ case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ {
+ if (!parse_upto (formatp, positionp, listp, escapep,
+ NULL, spec, ')', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ }
+ format = *formatp;
+ position = *positionp;
+ list = *listp;
+ escape = *escapep;
+ break;
+
+ case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
+ if (terminator != ')')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
+ if (atsign_p && colon_p)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, both the @ and the : modifiers are given."), spec->directives);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ else if (atsign_p)
+ {
+ struct format_arg_list *nil_list;
+ struct format_arg_list *union_list;
+
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ *formatp = format;
+ *escapep = escape;
+
+ /* First alternative: argument is NIL. */
+ nil_list = (list != NULL ? copy_list (list) : NULL);
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (&nil_list, position,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+
+ /* Second alternative: use sub-format. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ NULL, spec, ']', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ {
+ if (position >= 0)
+ {
+ if (sub_position == position + 1)
+ /* new position is branch independent */
+ position = position + 1;
+ else
+ /* new position is branch dependent */
+ position = -1;
+ }
+ }
+ else
+ {
+ if (position >= 0)
+ position = position + 1;
+ }
+ union_list = union (nil_list, sub_list);
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ else if (colon_p)
+ {
+ int union_position;
+ struct format_arg_list *union_list;
+
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+
+ *formatp = format;
+ *escapep = escape;
+ union_position = -2;
+ union_list = NULL;
+
+ /* First alternative. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ int sub_separator = 0;
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (&sub_list, position - 1,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ &sub_separator, spec, ']', true,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (!sub_separator)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '~:[' is not followed by two clauses, separated by '~;'."), spec->directives);
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ union_position = sub_position;
+ union_list = union (union_list, sub_list);
+ }
+
+ /* Second alternative. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ NULL, spec, ']', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ {
+ if (union_position == -2)
+ union_position = sub_position;
+ else if (sub_position < 0
+ || sub_position != union_position)
+ union_position = -1;
+ }
+ union_list = union (union_list, sub_list);
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (union_position != -2)
+ position = union_position;
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ else
+ {
+ int arg_position;
+ int union_position;
+ struct format_arg_list *union_list;
+ bool last_alternative;
+
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ /* If there was no first parameter, an argument is consumed. */
+ arg_position = -1;
+ if (!(paramcount >= 1 && params[0].type != PT_NIL))
+ if (position >= 0)
+ {
+ arg_position = position;
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ }
+
+ *formatp = format;
+ *escapep = escape;
+
+ union_position = -2;
+ union_list = NULL;
+ last_alternative = false;
+ for (;;)
+ {
+ /* Next alternative. */
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ int sub_separator = 0;
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ &sub_separator, spec, ']', !last_alternative,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ /* If this alternative is chosen, the argument arg_position
+ is an integer, namely the index of this alternative. */
+ if (!last_alternative && arg_position >= 0)
+ add_req_type_constraint (&sub_list, arg_position,
+ FAT_INTEGER);
+ if (sub_list != NULL)
+ {
+ if (union_position == -2)
+ union_position = sub_position;
+ else if (sub_position < 0
+ || sub_position != union_position)
+ union_position = -1;
+ }
+ union_list = union (union_list, sub_list);
+ if (sub_separator == 2)
+ last_alternative = true;
+ if (!sub_separator)
+ break;
+ }
+ if (!last_alternative)
+ {
+ /* An implicit default alternative. */
+ if (union_position == -2)
+ union_position = position;
+ else if (position < 0 || position != union_position)
+ union_position = -1;
+ if (list != NULL)
+ union_list = union (union_list, copy_list (list));
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (union_position != -2)
+ position = union_position;
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ break;
+
+ case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
+ if (terminator != ']')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '{': /* 22.3.7.4 FORMAT-ITERATION */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ {
+ int sub_position = 0;
+ struct format_arg_list *sub_list = make_unconstrained_list ();
+ struct format_arg_list *sub_escape = NULL;
+ struct spec sub_spec;
+ sub_spec.directives = 0;
+ sub_spec.list = sub_list;
+ if (!parse_upto (formatp, &sub_position, &sub_list, &sub_escape,
+ NULL, &sub_spec, '}', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ spec->directives += sub_spec.directives;
+
+ /* If the sub-formatstring is empty, except for the terminating
+ ~} directive, a formatstring argument is consumed. */
+ if (*format == '~' && sub_spec.directives == 1)
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
+
+ if (colon_p)
+ {
+ /* Each iteration uses a new sublist. */
+ struct format_arg_list *listlist;
+
+ /* ~{ catches ~^. */
+ sub_list = union (sub_list, sub_escape);
+
+ listlist = make_repeated_list_of_lists (sub_list);
+
+ sub_list = listlist;
+ }
+ else
+ {
+ /* Each iteration's arguments are all concatenated in a
+ single list. */
+ struct format_arg_list *looplist;
+
+ /* FIXME: This is far from correct. Test cases:
+ abc~{~^~}
+ abc~{~S~^~S~}
+ abc~{~D~^~C~}
+ abc~{~D~^~D~}
+ abc~{~D~^~S~}
+ abc~{~D~^~C~}~:*~{~S~^~D~}
+ */
+
+ /* ~{ catches ~^. */
+ sub_list = union (sub_list, sub_escape);
+
+ if (sub_list == NULL)
+ looplist = make_empty_list ();
+ else
+ if (sub_position < 0 || sub_position == 0)
+ /* Too hard to track the possible argument types
+ when the iteration is performed 2 times or more.
+ So be satisfied with the constraints of executing
+ the iteration 1 or 0 times. */
+ looplist = make_union_with_empty_list (sub_list);
+ else
+ looplist = make_repeated_list (sub_list, sub_position);
+
+ sub_list = looplist;
+ }
+
+ if (atsign_p)
+ {
+ /* All remaining arguments are used. */
+ if (list != NULL && position >= 0)
+ {
+ shift_list (sub_list, position);
+ list = make_intersected_list (list, sub_list);
+ }
+ position = -1;
+ }
+ else
+ {
+ /* The argument is a list. */
+ if (position >= 0)
+ add_req_listtype_constraint (&list, position++,
+ FAT_LIST, sub_list);
+ }
+ }
+ format = *formatp;
+ break;
+
+ case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
+ if (terminator != '}')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '<': /* 22.3.6.2, 22.3.5.2 FORMAT-JUSTIFICATION */
+ if (!check_params (&list, paramcount, params, 4, IIIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ {
+ struct format_arg_list *sub_escape = NULL;
+
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+
+ for (;;)
+ {
+ int sub_separator = 0;
+ if (!parse_upto (formatp, positionp, listp, &sub_escape,
+ &sub_separator, spec, '>', true,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (!sub_separator)
+ break;
+ }
+
+ format = *formatp;
+ position = *positionp;
+ list = *listp;
+
+ /* ~< catches ~^. */
+ if (sub_escape != NULL)
+ position = -1;
+ list = union (list, sub_escape);
+ }
+ break;
+
+ case '>': /* 22.3.6.3 FORMAT-JUSTIFICATION-END */
+ if (terminator != '>')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), '>', '<');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
+ if (!check_params (&list, paramcount, params, 3, THREE,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0 && list != NULL && is_required (list, position))
+ /* This ~^ can never be executed. Ignore it. */
+ break;
+ if (list != NULL)
+ {
+ struct format_arg_list *this_escape = copy_list (list);
+ if (position >= 0)
+ this_escape = add_end_constraint (this_escape, position);
+ escape = union (escape, this_escape);
+ }
+ if (position >= 0)
+ list = add_required_constraint (list, position);
+ break;
+
+ case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
+ if (!separator)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '~;' is used in an invalid position."), spec->directives);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (terminator == '>')
+ {
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ *separatorp = (colon_p ? 2 : 1);
+ return true;
+
+ case '!': /* FORMAT-CALL, a CLISP extension */
+ if (!nocheck_params (&list, paramcount, params,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ {
+ add_req_type_constraint (&list, position++, FAT_FUNCTION);
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ }
+ break;
+
+ default:
+ --format;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec->directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ return false;
+ }
+
+ FDI_SET (format - 1, FMTDIR_END);
+
+ free (params);
+ }
+
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ if (terminator != '\0')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), terminator - 1, terminator);
+ return false;
+ }
+ return true;
+}
+
+
+/* ============== Top level format string handling functions ============== */
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ struct spec spec;
+ struct spec *result;
+ int position = 0;
+ struct format_arg_list *escape;
+
+ spec.directives = 0;
+ spec.list = make_unconstrained_list ();
+ escape = NULL;
+
+ if (!parse_upto (&format, &position, &spec.list, &escape,
+ NULL, &spec, '\0', false,
+ fdi, invalid_reason))
+ /* Invalid format string. */
+ return NULL;
+
+ /* Catch ~^ here. */
+ spec.list = union (spec.list, escape);
+
+ if (spec.list == NULL)
+ {
+ /* Contradictory argument type information. */
+ *invalid_reason =
+ xstrdup (_("The string refers to some argument in incompatible ways."));
+ return NULL;
+ }
+
+ /* Normalize the result. */
+ normalize_list (spec.list);
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free_list (spec->list);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (equality)
+ {
+ if (!equal_list (spec1->list, spec2->list))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' are not equivalent"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ }
+ else
+ {
+ struct format_arg_list *intersection =
+ make_intersected_list (copy_list (spec1->list),
+ copy_list (spec2->list));
+
+ if (!(intersection != NULL
+ && (normalize_list (intersection),
+ equal_list (intersection, spec2->list))))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' are not a subset of those in '%s'"),
+ pretty_msgstr, pretty_msgid);
+ err = true;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_lisp =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+/* ============================= Testing code ============================= */
+
+#undef union
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void print_list (struct format_arg_list *list);
+
+static void
+print_element (struct format_arg *element)
+{
+ switch (element->presence)
+ {
+ case FCT_REQUIRED:
+ break;
+ case FCT_OPTIONAL:
+ printf (". ");
+ break;
+ default:
+ abort ();
+ }
+
+ switch (element->type)
+ {
+ case FAT_OBJECT:
+ printf ("*");
+ break;
+ case FAT_CHARACTER_INTEGER_NULL:
+ printf ("ci()");
+ break;
+ case FAT_CHARACTER_NULL:
+ printf ("c()");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_INTEGER_NULL:
+ printf ("i()");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_REAL:
+ printf ("r");
+ break;
+ case FAT_LIST:
+ print_list (element->list);
+ break;
+ case FAT_FORMATSTRING:
+ printf ("~");
+ break;
+ case FAT_FUNCTION:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+}
+
+static void
+print_list (struct format_arg_list *list)
+{
+ unsigned int i, j;
+
+ printf ("(");
+
+ for (i = 0; i < list->initial.count; i++)
+ for (j = 0; j < list->initial.element[i].repcount; j++)
+ {
+ if (i > 0 || j > 0)
+ printf (" ");
+ print_element (&list->initial.element[i]);
+ }
+
+ if (list->repeated.count > 0)
+ {
+ printf (" |");
+ for (i = 0; i < list->repeated.count; i++)
+ for (j = 0; j < list->repeated.element[i].repcount; j++)
+ {
+ printf (" ");
+ print_element (&list->repeated.element[i]);
+ }
+ }
+
+ printf (")");
+}
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ print_list (spec->list);
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-lisp.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-lua.c b/gettext-tools/src/format-lua.c
new file mode 100644
index 0000000..272def4
--- /dev/null
+++ b/gettext-tools/src/format-lua.c
@@ -0,0 +1,348 @@
+/* Lua format strings.
+ Copyright (C) 2012 Free Software Foundation, Inc.
+ Written by Ľubomír Remák <lubomirr@lubomirr.eu>, 2012.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "gettext.h"
+#include "xalloc.h"
+#include "format-invalid.h"
+#include "c-ctype.h"
+#include "xvasprintf.h"
+
+#define _(str) gettext (str)
+
+/* The Lua format strings are described in the Lua manual,
+ which can be found at:
+ http://www.lua.org/manual/5.2/manual.html
+
+ A directive
+ - starts with '%'
+ - is optionally followed by any of the characters '0', '-', ' ', or
+ each of which acts as a flag,
+ - is optionally followed by a width specification: a nonempty digit
+ sequence,
+ - is optionally followed by '.' and a precision specification: a nonempty
+ digit sequence,
+ - is finished by a specifier
+ - 's', 'q', that needs a string argument,
+ - 'd', 'i', 'o', 'u', 'X', 'x', that need an integer argument,
+ - 'A', 'a', 'E', 'e', 'f', 'G', 'g', that need a floating-point argument,
+ - 'c', that needs a character argument.
+ Additionally there is the directive '%%', which takes no argument.
+
+ Note: Lua does not distinguish between integer, floating-point
+ and character arguments, since it has a number data type only.
+ However, we should not allow users to use %d instead of %c.
+ The same applies to %s and %q - we should not allow intermixing them.
+ */
+
+enum format_arg_type
+{
+ FAT_INTEGER,
+ FAT_CHARACTER,
+ FAT_FLOAT,
+ FAT_STRING,
+ FAT_ESCAPED_STRING
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int format_args_count;
+ unsigned int allocated;
+ enum format_arg_type *format_args;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+static void format_free (void *descr);
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+
+ const char *format_start = format;
+ const char *fatstr = format;
+ struct spec *result = NULL;
+ result = XMALLOC (struct spec);
+ result->directives = 0;
+ result->allocated = 0;
+ result->format_args_count = 0;
+ result->format_args = NULL;
+
+
+ for (; *fatstr != '\0';)
+ {
+ if (*fatstr++ == '%')
+ {
+ FDI_SET (fatstr - 1, FMTDIR_START);
+ result->directives++;
+
+ if (*fatstr != '%')
+ {
+ enum format_arg_type type;
+
+ /* Remove width. */
+ while (isdigit (*fatstr))
+ fatstr++;
+
+ if (*fatstr == '.')
+ {
+ fatstr++;
+
+ /* Remove precision. */
+ while (isdigit (*fatstr))
+ fatstr++;
+ }
+
+ switch (*fatstr)
+ {
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'X':
+ case 'x':
+ type = FAT_INTEGER;
+ break;
+ case 'a':
+ case 'A':
+ case 'E':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'G':
+ type = FAT_FLOAT;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case 'q':
+ type = FAT_ESCAPED_STRING;
+ break;
+ default:
+ if (*fatstr == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (fatstr - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (result->
+ format_args_count + 1,
+ *fatstr);
+ FDI_SET (fatstr, FMTDIR_ERROR);
+ }
+ goto fmt_error;
+ }
+
+ if (result->format_args_count == result->allocated)
+ {
+ result->allocated = 2 * result->allocated + 10;
+ result->format_args =
+ xrealloc (result->format_args,
+ result->allocated *
+ sizeof (enum format_arg_type));
+ }
+ result->format_args[result->format_args_count++] = type;
+ }
+ FDI_SET (fatstr, FMTDIR_END);
+ fatstr++;
+ }
+ }
+
+ return result;
+
+fmt_error:
+ format_free (result);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->format_args != NULL)
+ free (spec->format_args);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+
+ if (spec1->format_args_count + spec2->format_args_count > 0)
+ {
+ unsigned int i, n1, n2;
+
+ n1 = spec1->format_args_count;
+ n2 = spec2->format_args_count;
+
+ for (i = 0; i < n1 || i < n2; i++)
+ {
+ if (i >= n1)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ i + 1, pretty_msgstr, pretty_msgid);
+ return true;
+ }
+ else if (i >= n2)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ i + 1, pretty_msgstr);
+ return true;
+ }
+ else if (spec1->format_args[i] != spec2->format_args[i])
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr, i + 1);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+struct formatstring_parser formatstring_lua =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->format_args_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ switch (spec->format_args[i])
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_ESCAPED_STRING:
+ printf ("q");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-lua.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-pascal.c b/gettext-tools/src/format-pascal.c
new file mode 100644
index 0000000..7e0c505
--- /dev/null
+++ b/gettext-tools/src/format-pascal.c
@@ -0,0 +1,548 @@
+/* Object Pascal format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009-2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Object Pascal format strings are usable with the "format" function in the
+ "sysutils" unit. They are described in
+ <http://www.freepascal.org/docs-html/rtl/sysutils/format.html>
+ and are implemented in fpc-2.4.0/rtl/objpas/sysutils/sysformt.inc.
+ Another implementation exists in Borland Delphi. The GNU Pascal's
+ "sysutils" doesn't (yet?) have the "format" function.
+
+ A directive
+ - starts with '%',
+ - either
+ - is finished with '%', or
+ - - is optionally followed by an index specification: '*' (reads an
+ argument, must be of type integer) or a nonempty digit sequence
+ or nothing (equivalent to 0), followed by ':',
+ - is optionally followed by '-', which acts as a flag,
+ - is optionally followed by a width specification: '*' (reads an
+ argument, must be of type integer) or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*'
+ (reads an argument, must be of type integer) or a nonempty digit
+ sequence,
+ - is finished by a case-insensitive specifier. If no index was
+ specified, it reads an argument; otherwise is uses the index-th
+ argument, 0-based.
+ - 'd', 'u', 'x', needs an 'integer' or 'int64' or 'qword' argument,
+ - 'e', 'f', 'g', 'n', 'm', need an 'extended' or 'currency' floating-
+ point argument,
+ - 's', needs a 'string', 'char', 'pchar', 'widestring', 'widechar',
+ 'pwidechar' or 'ansistring' argument,
+ - 'p', needs a 'pointer' argument.
+ Numbered and unnumbered argument specifications can be used in the same
+ string. Numbered argument specifications have no influence on the
+ "current argument index", that is incremented each time an argument is read.
+ */
+
+enum format_arg_type
+{
+ FAT_INTEGER, /* integer, int64, qword */
+ FAT_FLOAT, /* extended, currency */
+ FAT_STRING, /* string, char, pchar, widestring, widechar, pwidechar,
+ ansistring */
+ FAT_POINTER
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ enum arg_index
+ {
+ index_numbered, /* index given by a fixed integer */
+ index_unnumbered, /* index given by unnumbered_arg_count++ */
+ index_unknown /* index is only known at run time */
+ };
+
+ directives = 0;
+ numbered_arg_count = 0;
+ allocated = 0;
+ numbered = NULL;
+ unnumbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ directives++;
+
+ if (*format != '%')
+ {
+ /* A complex directive. */
+ enum arg_index main_arg = index_unnumbered;
+ unsigned int main_number = 0;
+ enum format_arg_type type;
+
+ if (isdigit (*format) || *format == ':')
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ while (isdigit (*f))
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+
+ if (*f == ':')
+ {
+ main_number = m;
+ main_arg = index_numbered;
+ format = ++f;
+ }
+ }
+ else if (*format == '*')
+ {
+ if (format[1] == ':')
+ {
+ main_arg = index_unknown;
+ format += 2;
+ }
+ }
+
+ /* Parse flags. */
+ if (*format == '-')
+ format++;
+
+ /* Parse width. */
+ if (isdigit (*format))
+ {
+ do
+ format++;
+ while (isdigit (*format));
+ }
+ else if (*format == '*')
+ {
+ /* Unnumbered argument of type FAT_INTEGER. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = unnumbered_arg_count;
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ unnumbered_arg_count++;
+
+ format++;
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (isdigit (*format))
+ {
+ do
+ format++;
+ while (isdigit (*format));
+ }
+ else if (*format == '*')
+ {
+ /* Unnumbered argument of type FAT_INTEGER. */
+ if (allocated == unnumbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = unnumbered_arg_count;
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ unnumbered_arg_count++;
+
+ format++;
+ }
+ else
+ --format; /* will jump to bad_format */
+ }
+
+ switch (c_tolower (*format))
+ {
+ case 'd': case 'u': case 'x':
+ type = FAT_INTEGER;
+ break;
+ case 'e': case 'f': case 'g': case 'n': case 'm':
+ type = FAT_FLOAT;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case 'p':
+ type = FAT_POINTER;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ switch (main_arg)
+ {
+ case index_unnumbered:
+ numbered[numbered_arg_count].number = unnumbered_arg_count;
+ numbered[numbered_arg_count].type = type;
+ unnumbered_arg_count++;
+ break;
+ case index_numbered:
+ numbered[numbered_arg_count].number = main_number;
+ numbered[numbered_arg_count].type = type;
+ break;
+ case index_unknown:
+ numbered[numbered_arg_count].number = unnumbered_arg_count;
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ unnumbered_arg_count++;
+ break;
+ default:
+ abort ();
+ }
+ numbered_arg_count++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (numbered, numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < numbered_arg_count; i++)
+ if (j > 0 && numbered[i].number == numbered[j-1].number)
+ {
+ enum format_arg_type type1 = numbered[i].type;
+ enum format_arg_type type2 = numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = type1;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+ err = true;
+ }
+
+ numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ numbered[j].number = numbered[i].number;
+ numbered[j].type = numbered[i].type;
+ }
+ j++;
+ }
+ numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ result->directives = directives;
+ result->numbered_arg_count = numbered_arg_count;
+ result->allocated = allocated;
+ result->numbered = numbered;
+ return result;
+
+ bad_format:
+ if (numbered != NULL)
+ free (numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_pascal =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 0;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_POINTER:
+ printf ("p");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-pascal.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-perl-brace.c b/gettext-tools/src/format-perl-brace.c
new file mode 100644
index 0000000..3ab05ce
--- /dev/null
+++ b/gettext-tools/src/format-perl-brace.c
@@ -0,0 +1,294 @@
+/* Perl brace format strings.
+ Copyright (C) 2004, 2006-2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Perl brace format strings are supported by Guido Flohr's libintl-perl
+ package, more precisely by the __expand and __x functions therein.
+ A format string directive here consists of
+ - an opening brace '{',
+ - an identifier [_A-Za-z][_0-9A-Za-z]*,
+ - a closing brace '}'.
+ */
+
+struct named_arg
+{
+ char *name;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int named_arg_count;
+ unsigned int allocated;
+ struct named_arg *named;
+};
+
+
+static int
+named_arg_compare (const void *p1, const void *p2)
+{
+ return strcmp (((const struct named_arg *) p1)->name,
+ ((const struct named_arg *) p2)->name);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.named_arg_count = 0;
+ spec.allocated = 0;
+ spec.named = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '{')
+ {
+ const char *f = format;
+ char c;
+
+ c = *f;
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+ {
+ do
+ c = *++f;
+ while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'
+ || (c >= '0' && c <= '9'));
+ if (c == '}')
+ {
+ /* A directive. */
+ char *name;
+ const char *name_start = format;
+ const char *name_end = f;
+ size_t n = name_end - name_start;
+
+ FDI_SET (format - 1, FMTDIR_START);
+
+ name = XNMALLOC (n + 1, char);
+ memcpy (name, name_start, n);
+ name[n] = '\0';
+
+ spec.directives++;
+
+ if (spec.allocated == spec.named_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
+ }
+ spec.named[spec.named_arg_count].name = name;
+ spec.named_arg_count++;
+
+ FDI_SET (f, FMTDIR_END);
+
+ format = ++f;
+ }
+ }
+ }
+
+ /* Sort the named argument array, and eliminate duplicates. */
+ if (spec.named_arg_count > 1)
+ {
+ unsigned int i, j;
+
+ qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
+ named_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ for (i = j = 0; i < spec.named_arg_count; i++)
+ if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
+ free (spec.named[i].name);
+ else
+ {
+ if (j < i)
+ spec.named[j].name = spec.named[i].name;
+ j++;
+ }
+ spec.named_arg_count = j;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec->named_arg_count; i++)
+ free (spec->named[i].name);
+ free (spec->named);
+ }
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->named_arg_count + spec2->named_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->named_arg_count;
+ unsigned int n2 = spec2->named_arg_count;
+
+ /* Check the argument names in spec1 are contained in those of spec2.
+ Additional arguments in spec2 are allowed; they expand to themselves
+ (including the surrounding braces) at runtime.
+ Both arrays are sorted. We search for the differences. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ strcmp (spec1->named[i].name, spec2->named[j].name));
+
+ if (cmp > 0)
+ j++;
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
+ spec1->named[i].name, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_perl_brace =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("{");
+ for (i = 0; i < spec->named_arg_count; i++)
+ {
+ if (i > 0)
+ printf (", ");
+ printf ("'%s'", spec->named[i].name);
+ }
+ printf ("}");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-perl-brace.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-perl.c b/gettext-tools/src/format-perl.c
new file mode 100644
index 0000000..730d190
--- /dev/null
+++ b/gettext-tools/src/format-perl.c
@@ -0,0 +1,768 @@
+/* Perl format strings.
+ Copyright (C) 2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Perl format strings are implemented in function Perl_sv_vcatpvfn in
+ perl-5.8.0/sv.c.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer starting with a
+ nonzero digit,
+ - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
+ each of which acts as a flag,
+ - is optionally followed by a vector specification: 'v' or '*v' (reads an
+ argument) or '*m$v' where m is a positive integer starting with a nonzero
+ digit,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or '*m$' where m is a positive integer starting with a nonzero digit or
+ a nonempty digit sequence starting with a nonzero digit,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or '*m$' where m is a positive integer starting with a
+ nonzero digit or a digit sequence,
+ - is optionally followed by a size specifier, one of 'h' 'l' 'll' 'L' 'q'
+ 'V' 'I32' 'I64' 'I',
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that needs a small integer argument,
+ - 's', that needs a string argument,
+ - '_', that needs a scalar vector argument,
+ - 'p', that needs a pointer argument,
+ - 'i', 'd', 'D', that need an integer argument,
+ - 'u', 'U', 'b', 'o', 'O', 'x', 'X', that need an unsigned integer
+ argument,
+ - 'e', 'E', 'f', 'F', 'g', 'G', that need a floating-point argument,
+ - 'n', that needs a pointer to integer.
+ So there can be numbered argument specifications:
+ - '%m$' for the format string,
+ - '*m$v' for the vector,
+ - '*m$' for the width,
+ - '.*m$' for the precision.
+ Numbered and unnumbered argument specifications can be used in the same
+ string. The effect of '%m$' is to take argument number m, without affecting
+ the current argument number. The current argument number is incremented
+ after processing a directive with an unnumbered argument specification.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE = 0,
+ /* Basic types */
+ FAT_INTEGER = 1,
+ FAT_DOUBLE = 2,
+ FAT_CHAR = 3,
+ FAT_STRING = 4,
+ FAT_SCALAR_VECTOR = 5,
+ FAT_POINTER = 6,
+ FAT_COUNT_POINTER = 7,
+ /* Flags */
+ FAT_UNSIGNED = 1 << 3,
+ FAT_SIZE_SHORT = 1 << 4,
+ FAT_SIZE_V = 2 << 4,
+ FAT_SIZE_PTR = 3 << 4,
+ FAT_SIZE_LONG = 4 << 4,
+ FAT_SIZE_LONGLONG = 5 << 4,
+ /* Bitmasks */
+ FAT_SIZE_MASK = (FAT_SIZE_SHORT | FAT_SIZE_V | FAT_SIZE_PTR
+ | FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
+};
+#ifdef __cplusplus
+typedef int format_arg_type_t;
+#else
+typedef enum format_arg_type format_arg_type_t;
+#endif
+
+struct numbered_arg
+{
+ unsigned int number;
+ format_arg_type_t type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+/* Locale independent test for a nonzero decimal digit. */
+#define isnonzerodigit(c) ((unsigned int) ((c) - '1') < 9)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ directives = 0;
+ numbered_arg_count = 0;
+ unnumbered_arg_count = 0;
+ allocated = 0;
+ numbered = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ unsigned int number = 0;
+ bool vectorize = false;
+ format_arg_type_t type;
+ format_arg_type_t size;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ directives++;
+
+ if (isnonzerodigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ number = m;
+ format = ++f;
+ }
+ }
+
+ /* Parse flags. */
+ while (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0')
+ format++;
+
+ /* Parse vector. */
+ if (*format == 'v')
+ {
+ format++;
+ vectorize = true;
+ }
+ else if (*format == '*')
+ {
+ const char *f = format;
+
+ f++;
+ if (*f == 'v')
+ {
+ format = ++f;
+ vectorize = true;
+
+ /* Unnumbered argument. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = ++unnumbered_arg_count;
+ numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR; /* or FAT_STRING? */
+ numbered_arg_count++;
+ }
+ else if (isnonzerodigit (*f))
+ {
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ f++;
+ if (*f == 'v')
+ {
+ unsigned int vector_number = m;
+
+ format = ++f;
+ vectorize = true;
+
+ /* Numbered argument. */
+ /* Note: As of perl-5.8.0, this is not correctly
+ implemented in perl's sv.c. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = vector_number;
+ numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR; /* or FAT_STRING? */
+ numbered_arg_count++;
+ }
+ }
+ }
+ }
+
+ if (vectorize)
+ {
+ /* Numbered or unnumbered argument. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = (number ? number : ++unnumbered_arg_count);
+ numbered[numbered_arg_count].type = FAT_SCALAR_VECTOR;
+ numbered_arg_count++;
+ }
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ unsigned int width_number = 0;
+
+ format++;
+
+ if (isnonzerodigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ width_number = m;
+ format = ++f;
+ }
+ }
+
+ /* Numbered or unnumbered argument. */
+ /* Note: As of perl-5.8.0, this is not correctly
+ implemented in perl's sv.c. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = (width_number ? width_number : ++unnumbered_arg_count);
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ }
+ else if (isnonzerodigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ unsigned int precision_number = 0;
+
+ format++;
+
+ if (isnonzerodigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ precision_number = m;
+ format = ++f;
+ }
+ }
+
+ /* Numbered or unnumbered argument. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = (precision_number ? precision_number : ++unnumbered_arg_count);
+ numbered[numbered_arg_count].type = FAT_INTEGER;
+ numbered_arg_count++;
+ }
+ else
+ {
+ while (isdigit (*format)) format++;
+ }
+ }
+
+ /* Parse size. */
+ size = 0;
+ if (*format == 'h')
+ {
+ size = FAT_SIZE_SHORT;
+ format++;
+ }
+ else if (*format == 'l')
+ {
+ if (format[1] == 'l')
+ {
+ size = FAT_SIZE_LONGLONG;
+ format += 2;
+ }
+ else
+ {
+ size = FAT_SIZE_LONG;
+ format++;
+ }
+ }
+ else if (*format == 'L' || *format == 'q')
+ {
+ size = FAT_SIZE_LONGLONG;
+ format++;
+ }
+ else if (*format == 'V')
+ {
+ size = FAT_SIZE_V;
+ format++;
+ }
+ else if (*format == 'I')
+ {
+ if (format[1] == '6' && format[2] == '4')
+ {
+ size = FAT_SIZE_LONGLONG;
+ format += 3;
+ }
+ else if (format[1] == '3' && format[2] == '2')
+ {
+ size = 0; /* FAT_SIZE_INT */
+ format += 3;
+ }
+ else
+ {
+ size = FAT_SIZE_PTR;
+ format++;
+ }
+ }
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHAR;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case '_':
+ type = FAT_SCALAR_VECTOR;
+ break;
+ case 'D':
+ type = FAT_INTEGER | FAT_SIZE_V;
+ break;
+ case 'i': case 'd':
+ type = FAT_INTEGER | size;
+ break;
+ case 'U': case 'O':
+ type = FAT_INTEGER | FAT_UNSIGNED | FAT_SIZE_V;
+ break;
+ case 'u': case 'b': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER | FAT_UNSIGNED | size;
+ break;
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ if (size == FAT_SIZE_SHORT || size == FAT_SIZE_LONG)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the size specifier is incompatible with the conversion specifier '%c'."), directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ type = FAT_DOUBLE | size;
+ break;
+ case 'p':
+ type = FAT_POINTER;
+ break;
+ case 'n':
+ type = FAT_COUNT_POINTER | size;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (type != FAT_NONE && !vectorize)
+ {
+ /* Numbered or unnumbered argument. */
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = (number ? number : ++unnumbered_arg_count);
+ numbered[numbered_arg_count].type = type;
+ numbered_arg_count++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (numbered, numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < numbered_arg_count; i++)
+ if (j > 0 && numbered[i].number == numbered[j-1].number)
+ {
+ format_arg_type_t type1 = numbered[i].type;
+ format_arg_type_t type2 = numbered[j-1].type;
+ format_arg_type_t type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+ err = true;
+ }
+
+ numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ numbered[j].number = numbered[i].number;
+ numbered[j].type = numbered[i].type;
+ }
+ j++;
+ }
+ numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ result->directives = directives;
+ result->numbered_arg_count = numbered_arg_count;
+ result->allocated = allocated;
+ result->numbered = numbered;
+ return result;
+
+ bad_format:
+ if (numbered != NULL)
+ free (numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_perl =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ if (spec->numbered[i].type & FAT_UNSIGNED)
+ printf ("[unsigned]");
+ switch (spec->numbered[i].type & FAT_SIZE_MASK)
+ {
+ case 0:
+ break;
+ case FAT_SIZE_SHORT:
+ printf ("[short]");
+ break;
+ case FAT_SIZE_V:
+ printf ("[IV]");
+ break;
+ case FAT_SIZE_PTR:
+ printf ("[PTR]");
+ break;
+ case FAT_SIZE_LONG:
+ printf ("[long]");
+ break;
+ case FAT_SIZE_LONGLONG:
+ printf ("[long long]");
+ break;
+ default:
+ abort ();
+ }
+ switch (spec->numbered[i].type & ~(FAT_UNSIGNED | FAT_SIZE_MASK))
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_DOUBLE:
+ printf ("f");
+ break;
+ case FAT_CHAR:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_SCALAR_VECTOR:
+ printf ("sv");
+ break;
+ case FAT_POINTER:
+ printf ("p");
+ break;
+ case FAT_COUNT_POINTER:
+ printf ("n");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-perl.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-php.c b/gettext-tools/src/format-php.c
new file mode 100644
index 0000000..5dd6d7f
--- /dev/null
+++ b/gettext-tools/src/format-php.c
@@ -0,0 +1,501 @@
+/* PHP format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* PHP format strings are described in phpdoc-4.0.6, file
+ phpdoc/manual/function.sprintf.html, and are implemented in
+ php-4.1.0/ext/standard/formatted_print.c.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '0', '-', ' ', or
+ "'<anychar>", each of which acts as a flag,
+ - is optionally followed by a width specification: a nonempty digit
+ sequence,
+ - is optionally followed by '.' and a precision specification: a nonempty
+ digit sequence,
+ - is optionally followed by a size specifier 'l', which is ignored,
+ - is finished by a specifier
+ - 's', that needs a string argument,
+ - 'b', 'd', 'u', 'o', 'x', 'X', that need an integer argument,
+ - 'e', 'f', that need a floating-point argument,
+ - 'c', that needs a character argument.
+ Additionally there is the directive '%%', which takes no argument.
+ Numbered and unnumbered argument specifications can be used in the same
+ string. Numbered argument specifications have no influence on the
+ "current argument index", that is incremented each time an argument is read.
+ */
+
+enum format_arg_type
+{
+ FAT_INTEGER,
+ FAT_FLOAT,
+ FAT_CHARACTER,
+ FAT_STRING
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+ unsigned int unnumbered_arg_count;
+ struct spec *result;
+
+ directives = 0;
+ numbered_arg_count = 0;
+ allocated = 0;
+ numbered = NULL;
+ unnumbered_arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ directives++;
+
+ if (*format != '%')
+ {
+ /* A complex directive. */
+ unsigned int number;
+ enum format_arg_type type;
+
+ number = ++unnumbered_arg_count;
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+ --unnumbered_arg_count;
+ }
+ }
+
+ /* Parse flags. */
+ for (;;)
+ {
+ if (*format == '0' || *format == '-' || *format == ' ')
+ format++;
+ else if (*format == '\'')
+ {
+ format++;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ format++;
+ }
+ else
+ break;
+ }
+
+ /* Parse width. */
+ if (isdigit (*format))
+ {
+ do
+ format++;
+ while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (isdigit (*format))
+ {
+ do
+ format++;
+ while (isdigit (*format));
+ }
+ else
+ --format; /* will jump to bad_format */
+ }
+
+ /* Parse size. */
+ if (*format == 'l')
+ format++;
+
+ switch (*format)
+ {
+ case 'b': case 'd': case 'u': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER;
+ break;
+ case 'e': case 'f':
+ type = FAT_FLOAT;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (allocated == numbered_arg_count)
+ {
+ allocated = 2 * allocated + 1;
+ numbered = (struct numbered_arg *) xrealloc (numbered, allocated * sizeof (struct numbered_arg));
+ }
+ numbered[numbered_arg_count].number = number;
+ numbered[numbered_arg_count].type = type;
+ numbered_arg_count++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (numbered, numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < numbered_arg_count; i++)
+ if (j > 0 && numbered[i].number == numbered[j-1].number)
+ {
+ enum format_arg_type type1 = numbered[i].type;
+ enum format_arg_type type2 = numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = type1;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
+ err = true;
+ }
+
+ numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ numbered[j].number = numbered[i].number;
+ numbered[j].type = numbered[i].type;
+ }
+ j++;
+ }
+ numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ result->directives = directives;
+ result->numbered_arg_count = numbered_arg_count;
+ result->allocated = allocated;
+ result->numbered = numbered;
+ return result;
+
+ bad_format:
+ if (numbered != NULL)
+ free (numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_php =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-php.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-python-brace.c b/gettext-tools/src/format-python-brace.c
new file mode 100644
index 0000000..141d3df
--- /dev/null
+++ b/gettext-tools/src/format-python-brace.c
@@ -0,0 +1,542 @@
+/* Python brace format strings.
+ Copyright (C) 2004, 2006-2007, 2013 Free Software Foundation, Inc.
+ Written by Daiki Ueno <ueno@gnu.org>, 2013.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Python brace format strings are defined by PEP3101 together with
+ 'format' method of string class.
+ A format string directive here consists of
+ - an opening brace '{',
+ - an identifier [_A-Za-z][_0-9A-Za-z]*|[0-9]+,
+ - an optional getattr ('.') or getitem ('['..']') operator with
+ an identifier as argument,
+ - an optional format specifier starting with ':', with a
+ (unnested) format string as argument,
+ - a closing brace '}'.
+ Brace characters '{' and '}' can be escaped by doubles '{{' and '}}'.
+*/
+
+struct named_arg
+{
+ char *name;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int named_arg_count;
+ unsigned int allocated;
+ struct named_arg *named;
+};
+
+
+static bool parse_upto (struct spec *spec, const char **formatp,
+ bool is_toplevel, char terminator,
+ bool translated, char *fdi, char **invalid_reason);
+static void free_named_args (struct spec *spec);
+
+
+/* All the parse_* functions (except parse_upto) follow the same
+ calling convention. FORMATP shall point to the beginning of a token.
+ If parsing succeeds, FORMATP will point to the next character after
+ the token, and true is returned. Otherwise, FORMATP will be
+ unchanged and false is returned. */
+
+static bool
+parse_named_field (struct spec *spec,
+ const char **formatp, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *format = *formatp;
+ char c;
+
+ c = *format;
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
+ {
+ do
+ c = *++format;
+ while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'
+ || (c >= '0' && c <= '9'));
+ *formatp = format;
+ return true;
+ }
+ return false;
+}
+
+static bool
+parse_numeric_field (struct spec *spec,
+ const char **formatp, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *format = *formatp;
+ char c;
+
+ c = *format;
+ if (c >= '0' && c <= '9')
+ {
+ do
+ c = *++format;
+ while (c >= '0' && c <= '9');
+ *formatp = format;
+ return true;
+ }
+ return false;
+}
+
+static bool
+parse_directive (struct spec *spec,
+ const char **formatp, bool is_toplevel,
+ bool translated, char *fdi, char **invalid_reason)
+{
+ const char *format = *formatp;
+ const char *const format_start = format;
+ const char *name_start;
+ char c;
+
+ c = *++format;
+ if (c == '{')
+ {
+ *formatp = ++format;
+ return true;
+ }
+
+ name_start = format;
+ if (!parse_named_field (spec, &format, translated, fdi, invalid_reason)
+ && !parse_numeric_field (spec, &format, translated, fdi, invalid_reason))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '%c' cannot start a field name."), spec->directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+
+ c = *format;
+ if (c == '.')
+ {
+ format++;
+ if (!parse_named_field (spec, &format, translated, fdi,
+ invalid_reason))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '%c' cannot start a getattr argument."), spec->directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+ c = *format;
+ }
+ else if (c == '[')
+ {
+ format++;
+ if (!parse_named_field (spec, &format, translated, fdi,
+ invalid_reason)
+ && !parse_numeric_field (spec, &format, translated, fdi,
+ invalid_reason))
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '%c' cannot start a getitem argument."), spec->directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+
+ c = *format++;
+ if (c != ']')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+ c = *format;
+ }
+
+ if (c == ':')
+ {
+ if (!is_toplevel)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, no more nesting is allowed in a format specifier."), spec->directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+
+ /* Format specifiers. Although a format specifier can be any
+ string in theory, we can only recognize two types of format
+ specifiers below, because otherwise we would need to evaluate
+ Python expressions by ourselves:
+
+ - A nested format directive expanding to the whole string
+ - The Standard Format Specifiers, as described in PEP3101,
+ not including a nested format directive */
+ format++;
+ if (*format == '{')
+ {
+ /* Nested format directive. */
+ if (!parse_directive (spec, &format, false, translated, fdi,
+ invalid_reason))
+ {
+ /* FDI and INVALID_REASON will be set by a recursive call of
+ parse_directive. */
+ return false;
+ }
+
+ if (*format != '}')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ /* Standard format specifiers is in the form:
+ [[fill]align][sign][#][0][minimumwidth][.precision][type] */
+
+ /* Look ahead two characters to skip [[fill]align]. */
+ int c1, c2;
+
+ c1 = format[0];
+ c2 = format[1];
+
+ if (c2 == '<' || c2 == '>' || c2 == '=' || c2 == '^')
+ format += 2;
+ else if (c1 == '<' || c1 == '>' || c1 == '=' || c1 == '^')
+ format++;
+ if (*format == '+' || *format == '-' || *format == ' ')
+ format++;
+ if (*format == '#')
+ format++;
+ if (*format == '0')
+ format++;
+ while (c_isdigit (*format))
+ format++;
+ if (*format == '.')
+ {
+ format++;
+ while (c_isdigit (*format))
+ format++;
+ }
+ switch (*format)
+ {
+ case 'b': case 'c': case 'd': case 'o': case 'x': case 'X':
+ case 'n':
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ case '%':
+ format++;
+ break;
+ default:
+ break;
+ }
+ if (*format != '}')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ c = *format;
+ }
+
+ if (c != '}')
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, there is an unterminated format directive."), spec->directives);
+ FDI_SET (format, FMTDIR_ERROR);
+ return false;
+ }
+
+ if (is_toplevel)
+ {
+ char *name;
+ size_t n = format - name_start;
+
+ FDI_SET (name_start - 1, FMTDIR_START);
+
+ name = XNMALLOC (n + 1, char);
+ memcpy (name, name_start, n);
+ name[n] = '\0';
+
+ spec->directives++;
+
+ if (spec->allocated == spec->named_arg_count)
+ {
+ spec->allocated = 2 * spec->allocated + 1;
+ spec->named = (struct named_arg *) xrealloc (spec->named, spec->allocated * sizeof (struct named_arg));
+ }
+ spec->named[spec->named_arg_count].name = name;
+ spec->named_arg_count++;
+
+ FDI_SET (format, FMTDIR_END);
+ }
+
+ *formatp = ++format;
+ return true;
+}
+
+static bool
+parse_upto (struct spec *spec,
+ const char **formatp, bool is_toplevel, char terminator,
+ bool translated, char *fdi, char **invalid_reason)
+{
+ const char *format = *formatp;
+
+ for (; *format != terminator && *format != '\0';)
+ {
+ if (*format == '{')
+ {
+ if (!parse_directive (spec, &format, is_toplevel, translated, fdi,
+ invalid_reason))
+ return false;
+ }
+ else
+ format++;
+ }
+
+ *formatp = format;
+ return true;
+}
+
+static int
+named_arg_compare (const void *p1, const void *p2)
+{
+ return strcmp (((const struct named_arg *) p1)->name,
+ ((const struct named_arg *) p2)->name);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.named_arg_count = 0;
+ spec.allocated = 0;
+ spec.named = NULL;
+
+ if (!parse_upto (&spec, &format, true, '\0', translated, fdi, invalid_reason))
+ {
+ free_named_args (&spec);
+ return NULL;
+ }
+
+ /* Sort the named argument array, and eliminate duplicates. */
+ if (spec.named_arg_count > 1)
+ {
+ unsigned int i, j;
+
+ qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
+ named_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ for (i = j = 0; i < spec.named_arg_count; i++)
+ if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
+ free (spec.named[i].name);
+ else
+ {
+ if (j < i)
+ spec.named[j].name = spec.named[i].name;
+ j++;
+ }
+ spec.named_arg_count = j;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+free_named_args (struct spec *spec)
+{
+ if (spec->named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec->named_arg_count; i++)
+ free (spec->named[i].name);
+ free (spec->named);
+ }
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free_named_args (spec);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->named_arg_count + spec2->named_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->named_arg_count;
+ unsigned int n2 = spec2->named_arg_count;
+
+ /* Check the argument names in spec1 are contained in those of spec2.
+ Both arrays are sorted. We search for the differences. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ strcmp (spec1->named[i].name, spec2->named[j].name));
+
+ if (cmp > 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
+ spec2->named[i].name, pretty_msgid);
+ err = true;
+ break;
+ }
+ else
+ j++;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
+ spec1->named[i].name, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_python_brace =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("{");
+ for (i = 0; i < spec->named_arg_count; i++)
+ {
+ if (i > 0)
+ printf (", ");
+ printf ("'%s'", spec->named[i].name);
+ }
+ printf ("}");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-python-brace.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-python.c b/gettext-tools/src/format-python.c
new file mode 100644
index 0000000..d6a1338
--- /dev/null
+++ b/gettext-tools/src/format-python.c
@@ -0,0 +1,695 @@
+/* Python format strings.
+ Copyright (C) 2001-2004, 2006-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Python format strings are described in
+ Python Library reference
+ 2. Built-in Types, Exceptions and Functions
+ 2.1. Built-in Types
+ 2.1.5. Sequence Types
+ 2.1.5.2. String Formatting Operations
+ Any string or Unicode string can act as format string via the '%' operator,
+ implemented in stringobject.c and unicodeobject.c.
+ A directive
+ - starts with '%'
+ - is optionally followed by '(ident)' where ident is any sequence of
+ characters with balanced left and right parentheses,
+ - is optionally followed by any of the characters '-' (left justification),
+ '+' (sign), ' ' (blank), '#' (alt), '0' (zero), each of which acts as a
+ flag,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or a nonempty digit sequence,
+ - is optionally followed by a size specifier, one of 'h' 'l' 'L'.
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that needs a character argument,
+ - 's', 'r', that need a string argument (or, when a precision of 0 is
+ given, an argument of any type),
+ - 'i', 'd', 'u', 'o', 'x', 'X', that need an integer argument,
+ - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
+ Use of '(ident)' and use of unnamed argument specifications are exclusive,
+ because the first requires a mapping as argument, while the second requires
+ a tuple as argument. When unnamed arguments are used, the number of
+ arguments in the format string and the number of elements in the argument
+ tuple (to the right of the '%' operator) must be the same.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_ANY,
+ FAT_CHARACTER,
+ FAT_STRING,
+ FAT_INTEGER,
+ FAT_FLOAT
+};
+
+struct named_arg
+{
+ char *name;
+ enum format_arg_type type;
+};
+
+struct unnamed_arg
+{
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int named_arg_count;
+ unsigned int unnamed_arg_count;
+ unsigned int allocated;
+ struct named_arg *named;
+ struct unnamed_arg *unnamed;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+named_arg_compare (const void *p1, const void *p2)
+{
+ return strcmp (((const struct named_arg *) p1)->name,
+ ((const struct named_arg *) p2)->name);
+}
+
+#define INVALID_MIXES_NAMED_UNNAMED() \
+ xstrdup (_("The string refers to arguments both through argument names and through unnamed argument specifications."))
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.named_arg_count = 0;
+ spec.unnamed_arg_count = 0;
+ spec.allocated = 0;
+ spec.named = NULL;
+ spec.unnamed = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ char *name = NULL;
+ bool zero_precision = false;
+ enum format_arg_type type;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format == '(')
+ {
+ unsigned int depth;
+ const char *name_start;
+ const char *name_end;
+ size_t n;
+
+ name_start = ++format;
+ depth = 0;
+ for (; *format != '\0'; format++)
+ {
+ if (*format == '(')
+ depth++;
+ else if (*format == ')')
+ {
+ if (depth == 0)
+ break;
+ else
+ depth--;
+ }
+ }
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ name_end = format++;
+
+ n = name_end - name_start;
+ name = XNMALLOC (n + 1, char);
+ memcpy (name, name_start, n);
+ name[n] = '\0';
+ }
+
+ while (*format == '-' || *format == '+' || *format == ' '
+ || *format == '#' || *format == '0')
+ format++;
+
+ if (*format == '*')
+ {
+ format++;
+
+ /* Named and unnamed specifications are exclusive. */
+ if (spec.named_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnamed_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnamed = (struct unnamed_arg *) xrealloc (spec.unnamed, spec.allocated * sizeof (struct unnamed_arg));
+ }
+ spec.unnamed[spec.unnamed_arg_count].type = FAT_INTEGER;
+ spec.unnamed_arg_count++;
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ format++;
+
+ /* Named and unnamed specifications are exclusive. */
+ if (spec.named_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnamed_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnamed = (struct unnamed_arg *) xrealloc (spec.unnamed, spec.allocated * sizeof (struct unnamed_arg));
+ }
+ spec.unnamed[spec.unnamed_arg_count].type = FAT_INTEGER;
+ spec.unnamed_arg_count++;
+ }
+ else if (isdigit (*format))
+ {
+ zero_precision = true;
+ do
+ {
+ if (*format != '0')
+ zero_precision = false;
+ format++;
+ }
+ while (isdigit (*format));
+ }
+ }
+
+ if (*format == 'h' || *format == 'l' || *format == 'L')
+ format++;
+
+ switch (*format)
+ {
+ case '%':
+ type = FAT_NONE;
+ break;
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 's': case 'r':
+ type = (zero_precision ? FAT_ANY : FAT_STRING);
+ break;
+ case 'i': case 'd': case 'u': case 'o': case 'x': case 'X':
+ type = FAT_INTEGER;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ type = FAT_FLOAT;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (name != NULL)
+ {
+ /* Named argument. */
+
+ /* Named and unnamed specifications are exclusive. */
+ if (spec.unnamed_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.named_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
+ }
+ spec.named[spec.named_arg_count].name = name;
+ spec.named[spec.named_arg_count].type = type;
+ spec.named_arg_count++;
+ }
+ else if (*format != '%')
+ {
+ /* Unnamed argument. */
+
+ /* Named and unnamed specifications are exclusive. */
+ if (spec.named_arg_count > 0)
+ {
+ *invalid_reason = INVALID_MIXES_NAMED_UNNAMED ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.unnamed_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.unnamed = (struct unnamed_arg *) xrealloc (spec.unnamed, spec.allocated * sizeof (struct unnamed_arg));
+ }
+ spec.unnamed[spec.unnamed_arg_count].type = type;
+ spec.unnamed_arg_count++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the named argument array, and eliminate duplicates. */
+ if (spec.named_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
+ named_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.named_arg_count; i++)
+ if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
+ {
+ enum format_arg_type type1 = spec.named[i].type;
+ enum format_arg_type type2 = spec.named[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2 || type2 == FAT_ANY)
+ type_both = type1;
+ else if (type1 == FAT_ANY)
+ type_both = type2;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ xasprintf (_("The string refers to the argument named '%s' in incompatible ways."), spec.named[i].name);
+ err = true;
+ }
+
+ spec.named[j-1].type = type_both;
+ free (spec.named[i].name);
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.named[j].name = spec.named[i].name;
+ spec.named[j].type = spec.named[i].type;
+ }
+ j++;
+ }
+ spec.named_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec.named_arg_count; i++)
+ free (spec.named[i].name);
+ free (spec.named);
+ }
+ if (spec.unnamed != NULL)
+ free (spec.unnamed);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec->named_arg_count; i++)
+ free (spec->named[i].name);
+ free (spec->named);
+ }
+ if (spec->unnamed != NULL)
+ free (spec->unnamed);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->named_arg_count > 0 && spec2->unnamed_arg_count > 0)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' expect a mapping, those in '%s' expect a tuple"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else if (spec1->unnamed_arg_count > 0 && spec2->named_arg_count > 0)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' expect a tuple, those in '%s' expect a mapping"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else
+ {
+ if (spec1->named_arg_count + spec2->named_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->named_arg_count;
+ unsigned int n2 = spec2->named_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ strcmp (spec1->named[i].name, spec2->named[j].name));
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s', as in '%s', doesn't exist in '%s'"),
+ spec2->named[j].name, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
+ spec1->named[i].name, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (strcmp (spec1->named[i].name, spec2->named[j].name) == 0)
+ {
+ if (!(spec1->named[i].type == spec2->named[j].type
+ || (!equality
+ && (spec1->named[i].type == FAT_ANY
+ || spec2->named[j].type == FAT_ANY))))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument '%s' are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->named[j].name);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ if (spec1->unnamed_arg_count + spec2->unnamed_arg_count > 0)
+ {
+ unsigned int i;
+
+ /* Check the argument types are the same. */
+ if (spec1->unnamed_arg_count != spec2->unnamed_arg_count)
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ else
+ for (i = 0; i < spec2->unnamed_arg_count; i++)
+ if (!(spec1->unnamed[i].type == spec2->unnamed[i].type
+ || (!equality
+ && (spec1->unnamed[i].type == FAT_ANY
+ || spec2->unnamed[i].type == FAT_ANY))))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr, i + 1);
+ err = true;
+ }
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_python =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+unsigned int
+get_python_format_unnamed_arg_count (const char *string)
+{
+ /* Parse the format string. */
+ char *invalid_reason = NULL;
+ struct spec *descr =
+ (struct spec *) format_parse (string, false, NULL, &invalid_reason);
+
+ if (descr != NULL)
+ {
+ unsigned int result = descr->unnamed_arg_count;
+
+ format_free (descr);
+ return result;
+ }
+ else
+ {
+ free (invalid_reason);
+ return 0;
+ }
+}
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ if (spec->named_arg_count > 0)
+ {
+ if (spec->unnamed_arg_count > 0)
+ abort ();
+
+ printf ("{");
+ for (i = 0; i < spec->named_arg_count; i++)
+ {
+ if (i > 0)
+ printf (", ");
+ printf ("'%s':", spec->named[i].name);
+ switch (spec->named[i].type)
+ {
+ case FAT_ANY:
+ printf ("*");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf ("}");
+ }
+ else
+ {
+ printf ("(");
+ for (i = 0; i < spec->unnamed_arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ switch (spec->unnamed[i].type)
+ {
+ case FAT_ANY:
+ printf ("*");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+ }
+ printf (")");
+ }
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-python.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-qt-plural.c b/gettext-tools/src/format-qt-plural.c
new file mode 100644
index 0000000..5301d42
--- /dev/null
+++ b/gettext-tools/src/format-qt-plural.c
@@ -0,0 +1,194 @@
+/* Qt plural format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2009.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt plural format strings are processed by QObject::tr and are documented in
+ qt-x11-opensource-src-4.3.1/doc/html/qobject.html#tr.
+ A directive
+ - starts with '%',
+ - is optionally followed by 'L' (a no-op),
+ - is followed by 'n'.
+ Every directive is replaced by the numeric argument N passed to QObject::tr.
+ */
+
+struct spec
+{
+ /* Number of format directives. */
+ unsigned int directives;
+};
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ const char *dir_start = format - 1;
+
+ if (*format == 'L')
+ format++;
+ if (*format == 'n')
+ {
+ /* A directive. */
+ FDI_SET (dir_start, FMTDIR_START);
+ spec.directives++;
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ /* Check the argument is used. */
+ if ((spec1->directives == 0 && spec2->directives > 0)
+ || (equality && spec1->directives > 0 && spec2->directives == 0))
+ {
+ if (error_logger)
+ error_logger (_("number of format specifications in '%s' and '%s' does not match"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_qt_plural =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ if (spec->directives > 0)
+ printf ("*");
+ else
+ printf ("_");
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt-plural.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-qt.c b/gettext-tools/src/format-qt.c
new file mode 100644
index 0000000..3f759a2
--- /dev/null
+++ b/gettext-tools/src/format-qt.c
@@ -0,0 +1,266 @@
+/* Qt format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt format strings are processed by QString::arg and are documented in
+ qt-4.3.0/doc/html/qstring.html.
+ A directive
+ - starts with '%',
+ - is optionally followed by 'L' (indicates locale-dependent processing),
+ - is followed by one or two digits ('0' to '9'). %0n is equivalent to %n.
+ An unterminated directive ('%' or '%L' not followed by a digit or at the
+ end) is not an error.
+ The first .arg() invocation replaces the %n with the lowest numbered n,
+ the next .arg() invocation then replaces the %n with the second-lowest
+ numbered n, and so on.
+ This is inherently buggy because a '%' in the first replacement confuses
+ the second .arg() invocation.
+ To reduce this problem and introduce another one, there are also .arg()
+ methods that take up to 9 strings and perform the replacements in one swoop.
+ But this method works only on strings that contain no 'L' flags and only
+ single-digit argument designators.
+ Although %0 is supported, usually %1 denotes the first argument, %2 the
+ second argument etc. */
+
+struct spec
+{
+ /* Number of format directives. */
+ unsigned int directives;
+
+ /* True if the string supports the multi-argument .arg() methods, i.e. if it
+ contains no 'L' flags and only single-digit argument designators. */
+ bool simple;
+
+ /* Booleans telling which %nn was seen. */
+ unsigned int arg_count;
+ bool args_used[100];
+};
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.simple = true;
+ spec.arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ const char *dir_start = format - 1;
+ bool locale_flag = false;
+
+ if (*format == 'L')
+ {
+ locale_flag = true;
+ format++;
+ }
+ if (*format >= '0' && *format <= '9')
+ {
+ /* A directive. */
+ unsigned int number;
+
+ FDI_SET (dir_start, FMTDIR_START);
+ spec.directives++;
+ if (locale_flag)
+ spec.simple = false;
+
+ number = *format - '0';
+ if (format[1] >= '0' && format[1] <= '9')
+ {
+ number = 10 * number + (format[1] - '0');
+ spec.simple = false;
+ format++;
+ }
+
+ while (spec.arg_count <= number)
+ spec.args_used[spec.arg_count++] = false;
+ spec.args_used[number] = true;
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+ unsigned int i;
+
+ if (spec1->simple && !spec2->simple)
+ {
+ if (error_logger)
+ error_logger (_("'%s' is a simple format string, but '%s' is not: it contains an 'L' flag or a double-digit argument number"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+
+ if (!err)
+ for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
+ {
+ bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
+ bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
+
+ /* The translator cannot omit a %n from the msgstr because that would
+ yield a "Argument missing" warning at runtime. */
+ if (arg_used1 != arg_used2)
+ {
+ if (error_logger)
+ {
+ if (arg_used1)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ i, pretty_msgstr);
+ else
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ i, pretty_msgstr, pretty_msgid);
+ }
+ err = true;
+ break;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_qt =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ if (spec->args_used[i])
+ printf ("*");
+ else
+ printf ("_");
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-qt.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-scheme.c b/gettext-tools/src/format-scheme.c
new file mode 100644
index 0000000..02c6182
--- /dev/null
+++ b/gettext-tools/src/format-scheme.c
@@ -0,0 +1,3581 @@
+/* Scheme format strings.
+ Copyright (C) 2001-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "gcd.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "minmax.h"
+#include "error.h"
+#include "error-progname.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Assertion macros. Could be defined to empty for speed. */
+#define ASSERT(expr) if (!(expr)) abort ();
+#define VERIFY_LIST(list) verify_list (list)
+
+
+/* Scheme format strings are described in the GNU guile documentation,
+ section "Formatted Output". They are implemented in
+ guile-1.6.4/ice-9/format.scm. */
+
+/* Data structure describing format string derived constraints for an
+ argument list. It is a recursive list structure. Structure sharing
+ is not allowed. */
+
+enum format_cdr_type
+{
+ FCT_REQUIRED, /* The format argument list cannot end before this argument. */
+ FCT_OPTIONAL /* The format argument list may end before this argument. */
+};
+
+enum format_arg_type
+{
+ FAT_OBJECT, /* Any object, type T. */
+ FAT_CHARACTER_INTEGER_NULL, /* Type (OR CHARACTER INTEGER NULL). */
+ FAT_CHARACTER_NULL, /* Type (OR CHARACTER NULL). */
+ FAT_CHARACTER, /* Type CHARACTER. */
+ FAT_INTEGER_NULL, /* Type (OR INTEGER NULL). */
+ FAT_INTEGER, /* Meant for objects of type INTEGER. */
+ FAT_REAL, /* Meant for objects of type REAL. */
+ FAT_COMPLEX, /* Meant for objects of type COMPLEX. */
+ FAT_LIST, /* Meant for proper lists. */
+ FAT_FORMATSTRING /* Format strings. */
+};
+
+struct format_arg
+{
+ unsigned int repcount; /* Number of consecutive arguments this constraint
+ applies to. Normally 1, but unconstrained
+ arguments are often repeated. */
+ enum format_cdr_type presence; /* Can the argument list end right before
+ this argument? */
+ enum format_arg_type type; /* Possible values for this argument. */
+ struct format_arg_list *list; /* For FAT_LIST: List elements. */
+};
+
+struct segment
+{
+ unsigned int count; /* Number of format_arg records used. */
+ unsigned int allocated;
+ struct format_arg *element; /* Argument constraints. */
+ unsigned int length; /* Number of arguments represented by this segment.
+ This is the sum of all repcounts in the segment. */
+};
+
+struct format_arg_list
+{
+ /* The constraints for the potentially infinite argument list are assumed
+ to become ultimately periodic. (Too complicated argument lists without
+ a-priori period, like
+ (format t "~@{~:[-~;~S~]~}" nil t 1 t 3 nil t 4)
+ are described by a constraint that ends in a length 1 period of
+ unconstrained arguments.) Such a periodic sequence can be split into
+ an initial segment and an endlessly repeated loop segment.
+ A finite sequence is represented entirely in the initial segment; the
+ loop segment is empty. */
+
+ struct segment initial; /* Initial arguments segment. */
+ struct segment repeated; /* Endlessly repeated segment. */
+};
+
+struct spec
+{
+ unsigned int directives;
+ struct format_arg_list *list;
+};
+
+
+/* Parameter for a directive. */
+enum param_type
+{
+ PT_NIL, /* param not present */
+ PT_CHARACTER, /* character */
+ PT_INTEGER, /* integer */
+ PT_ARGCOUNT, /* number of remaining arguments */
+ PT_V /* variable taken from argument list */
+};
+
+struct param
+{
+ enum param_type type;
+ int value; /* for PT_INTEGER: the value, for PT_V: the position */
+};
+
+
+/* Forward declaration of local functions. */
+#define union make_union
+static void verify_list (const struct format_arg_list *list);
+static void free_list (struct format_arg_list *list);
+static struct format_arg_list * copy_list (const struct format_arg_list *list);
+static bool equal_list (const struct format_arg_list *list1,
+ const struct format_arg_list *list2);
+static struct format_arg_list * make_intersected_list
+ (struct format_arg_list *list1,
+ struct format_arg_list *list2);
+static struct format_arg_list * make_intersection_with_empty_list
+ (struct format_arg_list *list);
+static struct format_arg_list * make_union_list
+ (struct format_arg_list *list1,
+ struct format_arg_list *list2);
+
+
+/* ======================= Verify a format_arg_list ======================= */
+
+/* Verify some invariants. */
+static void
+verify_element (const struct format_arg * e)
+{
+ ASSERT (e->repcount > 0);
+ if (e->type == FAT_LIST)
+ verify_list (e->list);
+}
+
+/* Verify some invariants. */
+/* Memory effects: none. */
+static void
+verify_list (const struct format_arg_list *list)
+{
+ unsigned int i;
+ unsigned int total_repcount;
+
+ ASSERT (list->initial.count <= list->initial.allocated);
+ total_repcount = 0;
+ for (i = 0; i < list->initial.count; i++)
+ {
+ verify_element (&list->initial.element[i]);
+ total_repcount += list->initial.element[i].repcount;
+ }
+ ASSERT (total_repcount == list->initial.length);
+
+ ASSERT (list->repeated.count <= list->repeated.allocated);
+ total_repcount = 0;
+ for (i = 0; i < list->repeated.count; i++)
+ {
+ verify_element (&list->repeated.element[i]);
+ total_repcount += list->repeated.element[i].repcount;
+ }
+ ASSERT (total_repcount == list->repeated.length);
+}
+
+#define VERIFY_LIST(list) verify_list (list)
+
+
+/* ======================== Free a format_arg_list ======================== */
+
+/* Free the data belonging to an argument list element. */
+static inline void
+free_element (struct format_arg *element)
+{
+ if (element->type == FAT_LIST)
+ free_list (element->list);
+}
+
+/* Free an argument list. */
+/* Memory effects: Frees list. */
+static void
+free_list (struct format_arg_list *list)
+{
+ unsigned int i;
+
+ for (i = 0; i < list->initial.count; i++)
+ free_element (&list->initial.element[i]);
+ if (list->initial.element != NULL)
+ free (list->initial.element);
+
+ for (i = 0; i < list->repeated.count; i++)
+ free_element (&list->repeated.element[i]);
+ if (list->repeated.element != NULL)
+ free (list->repeated.element);
+}
+
+
+/* ======================== Copy a format_arg_list ======================== */
+
+/* Copy the data belonging to an argument list element. */
+static inline void
+copy_element (struct format_arg *newelement,
+ const struct format_arg *oldelement)
+{
+ newelement->repcount = oldelement->repcount;
+ newelement->presence = oldelement->presence;
+ newelement->type = oldelement->type;
+ if (oldelement->type == FAT_LIST)
+ newelement->list = copy_list (oldelement->list);
+}
+
+/* Copy an argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+copy_list (const struct format_arg_list *list)
+{
+ struct format_arg_list *newlist;
+ unsigned int length;
+ unsigned int i;
+
+ VERIFY_LIST (list);
+
+ newlist = XMALLOC (struct format_arg_list);
+
+ newlist->initial.count = newlist->initial.allocated = list->initial.count;
+ length = 0;
+ if (list->initial.count == 0)
+ newlist->initial.element = NULL;
+ else
+ {
+ newlist->initial.element =
+ XNMALLOC (newlist->initial.allocated, struct format_arg);
+ for (i = 0; i < list->initial.count; i++)
+ {
+ copy_element (&newlist->initial.element[i],
+ &list->initial.element[i]);
+ length += list->initial.element[i].repcount;
+ }
+ }
+ ASSERT (length == list->initial.length);
+ newlist->initial.length = length;
+
+ newlist->repeated.count = newlist->repeated.allocated = list->repeated.count;
+ length = 0;
+ if (list->repeated.count == 0)
+ newlist->repeated.element = NULL;
+ else
+ {
+ newlist->repeated.element =
+ XNMALLOC (newlist->repeated.allocated, struct format_arg);
+ for (i = 0; i < list->repeated.count; i++)
+ {
+ copy_element (&newlist->repeated.element[i],
+ &list->repeated.element[i]);
+ length += list->repeated.element[i].repcount;
+ }
+ }
+ ASSERT (length == list->repeated.length);
+ newlist->repeated.length = length;
+
+ VERIFY_LIST (newlist);
+
+ return newlist;
+}
+
+
+/* ===================== Compare two format_arg_lists ===================== */
+
+/* Tests whether two normalized argument constraints are equivalent,
+ ignoring the repcount. */
+static bool
+equal_element (const struct format_arg * e1, const struct format_arg * e2)
+{
+ return (e1->presence == e2->presence
+ && e1->type == e2->type
+ && (e1->type == FAT_LIST ? equal_list (e1->list, e2->list) : true));
+}
+
+/* Tests whether two normalized argument list constraints are equivalent. */
+/* Memory effects: none. */
+static bool
+equal_list (const struct format_arg_list *list1,
+ const struct format_arg_list *list2)
+{
+ unsigned int n, i;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ n = list1->initial.count;
+ if (n != list2->initial.count)
+ return false;
+ for (i = 0; i < n; i++)
+ {
+ const struct format_arg * e1 = &list1->initial.element[i];
+ const struct format_arg * e2 = &list2->initial.element[i];
+
+ if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
+ return false;
+ }
+
+ n = list1->repeated.count;
+ if (n != list2->repeated.count)
+ return false;
+ for (i = 0; i < n; i++)
+ {
+ const struct format_arg * e1 = &list1->repeated.element[i];
+ const struct format_arg * e2 = &list2->repeated.element[i];
+
+ if (!(e1->repcount == e2->repcount && equal_element (e1, e2)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* ===================== Incremental memory allocation ===================== */
+
+/* Ensure list->initial.allocated >= newcount. */
+static inline void
+ensure_initial_alloc (struct format_arg_list *list, unsigned int newcount)
+{
+ if (newcount > list->initial.allocated)
+ {
+ list->initial.allocated =
+ MAX (2 * list->initial.allocated + 1, newcount);
+ list->initial.element =
+ (struct format_arg *)
+ xrealloc (list->initial.element,
+ list->initial.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->initial.allocated > list->initial.count. */
+static inline void
+grow_initial_alloc (struct format_arg_list *list)
+{
+ if (list->initial.count >= list->initial.allocated)
+ {
+ list->initial.allocated =
+ MAX (2 * list->initial.allocated + 1, list->initial.count + 1);
+ list->initial.element =
+ (struct format_arg *)
+ xrealloc (list->initial.element,
+ list->initial.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->repeated.allocated >= newcount. */
+static inline void
+ensure_repeated_alloc (struct format_arg_list *list, unsigned int newcount)
+{
+ if (newcount > list->repeated.allocated)
+ {
+ list->repeated.allocated =
+ MAX (2 * list->repeated.allocated + 1, newcount);
+ list->repeated.element =
+ (struct format_arg *)
+ xrealloc (list->repeated.element,
+ list->repeated.allocated * sizeof (struct format_arg));
+ }
+}
+
+/* Ensure list->repeated.allocated > list->repeated.count. */
+static inline void
+grow_repeated_alloc (struct format_arg_list *list)
+{
+ if (list->repeated.count >= list->repeated.allocated)
+ {
+ list->repeated.allocated =
+ MAX (2 * list->repeated.allocated + 1, list->repeated.count + 1);
+ list->repeated.element =
+ (struct format_arg *)
+ xrealloc (list->repeated.element,
+ list->repeated.allocated * sizeof (struct format_arg));
+ }
+}
+
+
+/* ====================== Normalize a format_arg_list ====================== */
+
+/* Normalize an argument list constraint, assuming all sublists are already
+ normalized. */
+/* Memory effects: Destructively modifies list. */
+static void
+normalize_outermost_list (struct format_arg_list *list)
+{
+ unsigned int n, i, j;
+
+ /* Step 1: Combine adjacent elements.
+ Copy from i to j, keeping 0 <= j <= i. */
+
+ n = list->initial.count;
+ for (i = j = 0; i < n; i++)
+ if (j > 0
+ && equal_element (&list->initial.element[i],
+ &list->initial.element[j-1]))
+ {
+ list->initial.element[j-1].repcount +=
+ list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ }
+ else
+ {
+ if (j < i)
+ list->initial.element[j] = list->initial.element[i];
+ j++;
+ }
+ list->initial.count = j;
+
+ n = list->repeated.count;
+ for (i = j = 0; i < n; i++)
+ if (j > 0
+ && equal_element (&list->repeated.element[i],
+ &list->repeated.element[j-1]))
+ {
+ list->repeated.element[j-1].repcount +=
+ list->repeated.element[i].repcount;
+ free_element (&list->repeated.element[i]);
+ }
+ else
+ {
+ if (j < i)
+ list->repeated.element[j] = list->repeated.element[i];
+ j++;
+ }
+ list->repeated.count = j;
+
+ /* Nothing more to be done if the loop segment is empty. */
+ if (list->repeated.count > 0)
+ {
+ unsigned int m, repcount0_extra;
+
+ /* Step 2: Reduce the loop period. */
+ n = list->repeated.count;
+ repcount0_extra = 0;
+ if (n > 1
+ && equal_element (&list->repeated.element[0],
+ &list->repeated.element[n-1]))
+ {
+ repcount0_extra = list->repeated.element[n-1].repcount;
+ n--;
+ }
+ /* Proceed as if the loop period were n, with
+ list->repeated.element[0].repcount incremented by repcount0_extra. */
+ for (m = 2; m <= n / 2; n++)
+ if ((n % m) == 0)
+ {
+ /* m is a divisor of n. Try to reduce the loop period to n. */
+ bool ok = true;
+
+ for (i = 0; i < n - m; i++)
+ if (!((list->repeated.element[i].repcount
+ + (i == 0 ? repcount0_extra : 0)
+ == list->repeated.element[i+m].repcount)
+ && equal_element (&list->repeated.element[i],
+ &list->repeated.element[i+m])))
+ {
+ ok = false;
+ break;
+ }
+ if (ok)
+ {
+ for (i = m; i < n; i++)
+ free_element (&list->repeated.element[i]);
+ if (n < list->repeated.count)
+ list->repeated.element[m] = list->repeated.element[n];
+ list->repeated.count = list->repeated.count - n + m;
+ list->repeated.length /= n / m;
+ break;
+ }
+ }
+
+ /* Step 3: Roll as much as possible of the initial segment's tail
+ into the loop. */
+ if (list->repeated.count == 1)
+ {
+ if (list->initial.count > 0
+ && equal_element (&list->initial.element[list->initial.count-1],
+ &list->repeated.element[0]))
+ {
+ /* Roll the last element of the initial segment into the loop.
+ Its repcount is irrelevant. The second-to-last element is
+ certainly different and doesn't need to be considered. */
+ list->initial.length -=
+ list->initial.element[list->initial.count-1].repcount;
+ list->initial.count--;
+ }
+ }
+ else
+ {
+ while (list->initial.count > 0
+ && equal_element (&list->initial.element[list->initial.count-1],
+ &list->repeated.element[list->repeated.count-1]))
+ {
+ unsigned int moved_repcount =
+ MIN (list->initial.element[list->initial.count-1].repcount,
+ list->repeated.element[list->repeated.count-1].repcount);
+
+ /* Add the element at the start of list->repeated. */
+ if (equal_element (&list->repeated.element[0],
+ &list->repeated.element[list->repeated.count-1]))
+ list->repeated.element[0].repcount += moved_repcount;
+ else
+ {
+ unsigned int newcount = list->repeated.count + 1;
+ ensure_repeated_alloc (list, newcount);
+ for (i = newcount - 1; i > 0; i--)
+ list->repeated.element[i] = list->repeated.element[i-1];
+ list->repeated.count = newcount;
+ copy_element (&list->repeated.element[0],
+ &list->repeated.element[list->repeated.count-1]);
+ list->repeated.element[0].repcount = moved_repcount;
+ }
+
+ /* Remove the element from the end of list->repeated. */
+ list->repeated.element[list->repeated.count-1].repcount -=
+ moved_repcount;
+ if (list->repeated.element[list->repeated.count-1].repcount == 0)
+ {
+ free_element (&list->repeated.element[list->repeated.count-1]);
+ list->repeated.count--;
+ }
+
+ /* Remove the element from the end of list->initial. */
+ list->initial.element[list->initial.count-1].repcount -=
+ moved_repcount;
+ if (list->initial.element[list->initial.count-1].repcount == 0)
+ {
+ free_element (&list->initial.element[list->initial.count-1]);
+ list->initial.count--;
+ }
+ list->initial.length -= moved_repcount;
+ }
+ }
+ }
+}
+
+/* Normalize an argument list constraint. */
+/* Memory effects: Destructively modifies list. */
+static void
+normalize_list (struct format_arg_list *list)
+{
+ unsigned int n, i;
+
+ VERIFY_LIST (list);
+
+ /* First normalize all elements, recursively. */
+ n = list->initial.count;
+ for (i = 0; i < n; i++)
+ if (list->initial.element[i].type == FAT_LIST)
+ normalize_list (list->initial.element[i].list);
+ n = list->repeated.count;
+ for (i = 0; i < n; i++)
+ if (list->repeated.element[i].type == FAT_LIST)
+ normalize_list (list->repeated.element[i].list);
+
+ /* Then normalize the top level list. */
+ normalize_outermost_list (list);
+
+ VERIFY_LIST (list);
+}
+
+
+/* ===================== Unconstrained and empty lists ===================== */
+
+/* It's easier to allocate these on demand, than to be careful not to
+ accidentally modify statically allocated lists. */
+
+
+/* Create an unconstrained argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+make_unconstrained_list ()
+{
+ struct format_arg_list *list;
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 1;
+ list->repeated.allocated = 1;
+ list->repeated.element = XNMALLOC (1, struct format_arg);
+ list->repeated.element[0].repcount = 1;
+ list->repeated.element[0].presence = FCT_OPTIONAL;
+ list->repeated.element[0].type = FAT_OBJECT;
+ list->repeated.length = 1;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Create an empty argument list. */
+/* Memory effects: Freshly allocated result. */
+static struct format_arg_list *
+make_empty_list ()
+{
+ struct format_arg_list *list;
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 0;
+ list->repeated.allocated = 0;
+ list->repeated.element = NULL;
+ list->repeated.length = 0;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Test for an empty list. */
+/* Memory effects: none. */
+static bool
+is_empty_list (const struct format_arg_list *list)
+{
+ return (list->initial.count == 0 && list->repeated.count == 0);
+}
+
+
+/* ======================== format_arg_list surgery ======================== */
+
+/* Unfold list->repeated m times, where m >= 1.
+ Assumes list->repeated.count > 0. */
+/* Memory effects: list is destructively modified. */
+static void
+unfold_loop (struct format_arg_list *list, unsigned int m)
+{
+ unsigned int i, j, k;
+
+ if (m > 1)
+ {
+ unsigned int newcount = list->repeated.count * m;
+ ensure_repeated_alloc (list, newcount);
+ i = list->repeated.count;
+ for (k = 1; k < m; k++)
+ for (j = 0; j < list->repeated.count; j++, i++)
+ copy_element (&list->repeated.element[i], &list->repeated.element[j]);
+ list->repeated.count = newcount;
+ list->repeated.length = list->repeated.length * m;
+ }
+}
+
+/* Ensure list->initial.length := m, where m >= list->initial.length.
+ Assumes list->repeated.count > 0. */
+/* Memory effects: list is destructively modified. */
+static void
+rotate_loop (struct format_arg_list *list, unsigned int m)
+{
+ if (m == list->initial.length)
+ return;
+
+ if (list->repeated.count == 1)
+ {
+ /* Instead of multiple copies of list->repeated.element[0], a single
+ copy with higher repcount is appended to list->initial. */
+ unsigned int i, newcount;
+
+ newcount = list->initial.count + 1;
+ ensure_initial_alloc (list, newcount);
+ i = list->initial.count;
+ copy_element (&list->initial.element[i], &list->repeated.element[0]);
+ list->initial.element[i].repcount = m - list->initial.length;
+ list->initial.count = newcount;
+ list->initial.length = m;
+ }
+ else
+ {
+ unsigned int n = list->repeated.length;
+
+ /* Write m = list->initial.length + q * n + r with 0 <= r < n. */
+ unsigned int q = (m - list->initial.length) / n;
+ unsigned int r = (m - list->initial.length) % n;
+
+ /* Determine how many entries of list->repeated are needed for
+ length r. */
+ unsigned int s;
+ unsigned int t;
+
+ for (t = r, s = 0;
+ s < list->repeated.count && t >= list->repeated.element[s].repcount;
+ t -= list->repeated.element[s].repcount, s++)
+ ;
+
+ /* s must be < list->repeated.count, otherwise r would have been >= n. */
+ ASSERT (s < list->repeated.count);
+
+ /* So we need to add to list->initial:
+ q full copies of list->repeated,
+ plus the s first elements of list->repeated,
+ plus, if t > 0, a splitoff of list->repeated.element[s]. */
+ {
+ unsigned int i, j, k, newcount;
+
+ i = list->initial.count;
+ newcount = i + q * list->repeated.count + s + (t > 0 ? 1 : 0);
+ ensure_initial_alloc (list, newcount);
+ for (k = 0; k < q; k++)
+ for (j = 0; j < list->repeated.count; j++, i++)
+ copy_element (&list->initial.element[i],
+ &list->repeated.element[j]);
+ for (j = 0; j < s; j++, i++)
+ copy_element (&list->initial.element[i], &list->repeated.element[j]);
+ if (t > 0)
+ {
+ copy_element (&list->initial.element[i],
+ &list->repeated.element[j]);
+ list->initial.element[i].repcount = t;
+ i++;
+ }
+ ASSERT (i == newcount);
+ list->initial.count = newcount;
+ /* The new length of the initial segment is
+ = list->initial.length
+ + q * list->repeated.length
+ + list->repeated[0..s-1].repcount + t
+ = list->initial.length + q * n + r
+ = m.
+ */
+ list->initial.length = m;
+ }
+
+ /* And rotate list->repeated. */
+ if (r > 0)
+ {
+ unsigned int i, j, oldcount, newcount;
+ struct format_arg *newelement;
+
+ oldcount = list->repeated.count;
+ newcount = list->repeated.count + (t > 0 ? 1 : 0);
+ newelement = XNMALLOC (newcount, struct format_arg);
+ i = 0;
+ for (j = s; j < oldcount; j++, i++)
+ newelement[i] = list->repeated.element[j];
+ for (j = 0; j < s; j++, i++)
+ newelement[i] = list->repeated.element[j];
+ if (t > 0)
+ {
+ copy_element (&newelement[oldcount], &newelement[0]);
+ newelement[0].repcount -= t;
+ newelement[oldcount].repcount = t;
+ }
+ free (list->repeated.element);
+ list->repeated.element = newelement;
+ }
+ }
+}
+
+
+/* Ensure index n in the initial segment falls on a split between elements,
+ i.e. if 0 < n < list->initial.length, then n-1 and n are covered by two
+ different adjacent elements. */
+/* Memory effects: list is destructively modified. */
+static unsigned int
+initial_splitelement (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s;
+ unsigned int t;
+ unsigned int oldrepcount;
+ unsigned int newcount;
+ unsigned int i;
+
+ VERIFY_LIST (list);
+
+ if (n > list->initial.length)
+ {
+ ASSERT (list->repeated.count > 0);
+ rotate_loop (list, n);
+ ASSERT (n <= list->initial.length);
+ }
+
+ /* Determine how many entries of list->initial need to be skipped. */
+ for (t = n, s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ ;
+
+ if (t == 0)
+ return s;
+
+ ASSERT (s < list->initial.count);
+
+ /* Split the entry into two entries. */
+ oldrepcount = list->initial.element[s].repcount;
+ newcount = list->initial.count + 1;
+ ensure_initial_alloc (list, newcount);
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+1] = list->initial.element[i];
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ list->initial.element[s].repcount = t;
+ list->initial.element[s+1].repcount = oldrepcount - t;
+ list->initial.count = newcount;
+
+ VERIFY_LIST (list);
+
+ return s+1;
+}
+
+
+/* Ensure index n in the initial segment is not shared. Return its index. */
+/* Memory effects: list is destructively modified. */
+static unsigned int
+initial_unshare (struct format_arg_list *list, unsigned int n)
+{
+ /* This does the same side effects as
+ initial_splitelement (list, n);
+ initial_splitelement (list, n + 1);
+ */
+ unsigned int s;
+ unsigned int t;
+
+ VERIFY_LIST (list);
+
+ if (n >= list->initial.length)
+ {
+ ASSERT (list->repeated.count > 0);
+ rotate_loop (list, n + 1);
+ ASSERT (n < list->initial.length);
+ }
+
+ /* Determine how many entries of list->initial need to be skipped. */
+ for (t = n, s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ ;
+
+ /* s must be < list->initial.count. */
+ ASSERT (s < list->initial.count);
+
+ if (list->initial.element[s].repcount > 1)
+ {
+ /* Split the entry into at most three entries: for indices < n,
+ for index n, and for indices > n. */
+ unsigned int oldrepcount = list->initial.element[s].repcount;
+ unsigned int newcount =
+ list->initial.count + (t == 0 || t == oldrepcount - 1 ? 1 : 2);
+ ensure_initial_alloc (list, newcount);
+ if (t == 0 || t == oldrepcount - 1)
+ {
+ unsigned int i;
+
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+1] = list->initial.element[i];
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ if (t == 0)
+ {
+ list->initial.element[s].repcount = 1;
+ list->initial.element[s+1].repcount = oldrepcount - 1;
+ }
+ else
+ {
+ list->initial.element[s].repcount = oldrepcount - 1;
+ list->initial.element[s+1].repcount = 1;
+ }
+ }
+ else
+ {
+ unsigned int i;
+
+ for (i = list->initial.count - 1; i > s; i--)
+ list->initial.element[i+2] = list->initial.element[i];
+ copy_element (&list->initial.element[s+2], &list->initial.element[s]);
+ copy_element (&list->initial.element[s+1], &list->initial.element[s]);
+ list->initial.element[s].repcount = t;
+ list->initial.element[s+1].repcount = 1;
+ list->initial.element[s+2].repcount = oldrepcount - 1 - t;
+ }
+ list->initial.count = newcount;
+ if (t > 0)
+ s++;
+ }
+
+ /* Now the entry for index n has repcount 1. */
+ ASSERT (list->initial.element[s].repcount == 1);
+
+ VERIFY_LIST (list);
+
+ return s;
+}
+
+
+/* Add n unconstrained elements at the front of the list. */
+/* Memory effects: list is destructively modified. */
+static void
+shift_list (struct format_arg_list *list, unsigned int n)
+{
+ VERIFY_LIST (list);
+
+ if (n > 0)
+ {
+ unsigned int i;
+
+ grow_initial_alloc (list);
+ for (i = list->initial.count; i > 0; i--)
+ list->initial.element[i] = list->initial.element[i-1];
+ list->initial.element[0].repcount = n;
+ list->initial.element[0].presence = FCT_REQUIRED;
+ list->initial.element[0].type = FAT_OBJECT;
+ list->initial.count++;
+ list->initial.length += n;
+
+ normalize_outermost_list (list);
+ }
+
+ VERIFY_LIST (list);
+}
+
+
+/* ================= Intersection of two format_arg_lists ================= */
+
+/* Create the intersection (i.e. combined constraints) of two argument
+ constraints. Return false if the intersection is empty, i.e. if the
+ two constraints give a contradiction. */
+/* Memory effects: Freshly allocated element's sublist. */
+static bool
+make_intersected_element (struct format_arg *re,
+ const struct format_arg * e1,
+ const struct format_arg * e2)
+{
+ /* Intersect the cdr types. */
+ if (e1->presence == FCT_REQUIRED || e2->presence == FCT_REQUIRED)
+ re->presence = FCT_REQUIRED;
+ else
+ re->presence = FCT_OPTIONAL;
+
+ /* Intersect the arg types. */
+ if (e1->type == FAT_OBJECT)
+ {
+ re->type = e2->type;
+ if (re->type == FAT_LIST)
+ re->list = copy_list (e2->list);
+ }
+ else if (e2->type == FAT_OBJECT)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ re->list = copy_list (e1->list);
+ }
+ else if (e1->type == FAT_LIST
+ && (e2->type == FAT_CHARACTER_INTEGER_NULL
+ || e2->type == FAT_CHARACTER_NULL
+ || e2->type == FAT_INTEGER_NULL))
+ {
+ re->type = e1->type;
+ re->list = make_intersection_with_empty_list (e1->list);
+ if (re->list == NULL)
+ return false;
+ }
+ else if (e2->type == FAT_LIST
+ && (e1->type == FAT_CHARACTER_INTEGER_NULL
+ || e1->type == FAT_CHARACTER_NULL
+ || e1->type == FAT_INTEGER_NULL))
+ {
+ re->type = e2->type;
+ re->list = make_intersection_with_empty_list (e2->list);
+ if (re->list == NULL)
+ return false;
+ }
+ else if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
+ || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
+ || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == FAT_COMPLEX
+ && (e2->type == FAT_REAL || e2->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e2->type == FAT_COMPLEX
+ && (e1->type == FAT_REAL || e1->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e1->type == e2->type)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ {
+ re->list = make_intersected_list (copy_list (e1->list),
+ copy_list (e2->list));
+ if (re->list == NULL)
+ return false;
+ }
+ }
+ else
+ /* Each of FAT_CHARACTER, FAT_INTEGER, FAT_LIST, FAT_FORMATSTRING
+ matches only itself. Contradiction. */
+ return false;
+
+ return true;
+}
+
+/* Append list->repeated to list->initial, and clear list->repeated. */
+/* Memory effects: list is destructively modified. */
+static void
+append_repeated_to_initial (struct format_arg_list *list)
+{
+ if (list->repeated.count > 0)
+ {
+ /* Move list->repeated over to list->initial. */
+ unsigned int i, j, newcount;
+
+ newcount = list->initial.count + list->repeated.count;
+ ensure_initial_alloc (list, newcount);
+ i = list->initial.count;
+ for (j = 0; j < list->repeated.count; j++, i++)
+ list->initial.element[i] = list->repeated.element[j];
+ list->initial.count = newcount;
+ list->initial.length = list->initial.length + list->repeated.length;
+ free (list->repeated.element);
+ list->repeated.element = NULL;
+ list->repeated.allocated = 0;
+ list->repeated.count = 0;
+ list->repeated.length = 0;
+ }
+}
+
+/* Handle a contradiction during building of a format_arg_list.
+ The list consists only of an initial segment. The repeated segment is
+ empty. This function searches the last FCT_OPTIONAL and cuts off the
+ list at this point, or - if none is found - returns NULL. */
+/* Memory effects: list is destructively modified. If NULL is returned,
+ list is freed. */
+static struct format_arg_list *
+backtrack_in_initial (struct format_arg_list *list)
+{
+ ASSERT (list->repeated.count == 0);
+
+ while (list->initial.count > 0)
+ {
+ unsigned int i = list->initial.count - 1;
+ if (list->initial.element[i].presence == FCT_REQUIRED)
+ {
+ /* Throw away this element. */
+ list->initial.length -= list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ list->initial.count = i;
+ }
+ else /* list->initial.element[i].presence == FCT_OPTIONAL */
+ {
+ /* The list must end here. */
+ list->initial.length--;
+ if (list->initial.element[i].repcount > 1)
+ list->initial.element[i].repcount--;
+ else
+ {
+ free_element (&list->initial.element[i]);
+ list->initial.count = i;
+ }
+ VERIFY_LIST (list);
+ return list;
+ }
+ }
+
+ free_list (list);
+ return NULL;
+}
+
+/* Create the intersection (i.e. combined constraints) of two argument list
+ constraints. Free both argument lists when done. Return NULL if the
+ intersection is empty, i.e. if the two constraints give a contradiction. */
+/* Memory effects: list1 and list2 are freed. The result, if non-NULL, is
+ freshly allocated. */
+static struct format_arg_list *
+make_intersected_list (struct format_arg_list *list1,
+ struct format_arg_list *list2)
+{
+ struct format_arg_list *result;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
+ {
+ unsigned int n1 = list1->repeated.length;
+ unsigned int n2 = list2->repeated.length;
+ unsigned int g = gcd (n1, n2);
+ unsigned int m1 = n2 / g; /* = lcm(n1,n2) / n1 */
+ unsigned int m2 = n1 / g; /* = lcm(n1,n2) / n2 */
+
+ unfold_loop (list1, m1);
+ unfold_loop (list2, m2);
+ /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
+ }
+
+ if (list1->repeated.length > 0 || list2->repeated.length > 0)
+ /* Step 2: Ensure the initial segment of the result can be computed
+ from the initial segments of list1 and list2. If both have a
+ repeated segment, this means to ensure
+ list1->initial.length == list2->initial.length. */
+ {
+ unsigned int m = MAX (list1->initial.length, list2->initial.length);
+
+ if (list1->repeated.length > 0)
+ rotate_loop (list1, m);
+ if (list2->repeated.length > 0)
+ rotate_loop (list2, m);
+ }
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ {
+ ASSERT (list1->initial.length == list2->initial.length);
+ ASSERT (list1->repeated.length == list2->repeated.length);
+ }
+
+ /* Step 3: Allocate the result. */
+ result = XMALLOC (struct format_arg_list);
+ result->initial.count = 0;
+ result->initial.allocated = 0;
+ result->initial.element = NULL;
+ result->initial.length = 0;
+ result->repeated.count = 0;
+ result->repeated.allocated = 0;
+ result->repeated.element = NULL;
+ result->repeated.length = 0;
+
+ /* Step 4: Elementwise intersection of list1->initial, list2->initial. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->initial.element; c1 = list1->initial.count;
+ e2 = list2->initial.element; c2 = list2->initial.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Intersect the argument types. */
+ if (!make_intersected_element (re, e1, e2))
+ {
+ /* If re->presence == FCT_OPTIONAL, the result list ends here. */
+ if (re->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+
+ result->initial.count++;
+ result->initial.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ if (list1->repeated.count == 0 && list2->repeated.count == 0)
+ {
+ /* Intersecting two finite lists. */
+ if (c1 > 0)
+ {
+ /* list1 longer than list2. */
+ if (e1->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ }
+ else if (c2 > 0)
+ {
+ /* list2 longer than list1. */
+ if (e2->presence == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ }
+ goto done;
+ }
+ else if (list1->repeated.count == 0)
+ {
+ /* Intersecting a finite and an infinite list. */
+ ASSERT (c1 == 0);
+ if ((c2 > 0 ? e2->presence : list2->repeated.element[0].presence)
+ == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+ else if (list2->repeated.count == 0)
+ {
+ /* Intersecting an infinite and a finite list. */
+ ASSERT (c2 == 0);
+ if ((c1 > 0 ? e1->presence : list1->repeated.element[0].presence)
+ == FCT_REQUIRED)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+ goto done;
+ }
+ /* Intersecting two infinite lists. */
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ /* Step 5: Elementwise intersection of list1->repeated, list2->repeated. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->repeated.element; c1 = list1->repeated.count;
+ e2 = list2->repeated.element; c2 = list2->repeated.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->repeated. */
+ grow_repeated_alloc (result);
+ re = &result->repeated.element[result->repeated.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Intersect the argument types. */
+ if (!make_intersected_element (re, e1, e2))
+ {
+ bool re_is_required = re->presence == FCT_REQUIRED;
+
+ append_repeated_to_initial (result);
+
+ /* If re->presence == FCT_OPTIONAL, the result list ends here. */
+ if (re_is_required)
+ /* Contradiction. Backtrack. */
+ result = backtrack_in_initial (result);
+
+ goto done;
+ }
+
+ result->repeated.count++;
+ result->repeated.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ done:
+ free_list (list1);
+ free_list (list2);
+ if (result != NULL)
+ {
+ /* Undo the loop unfolding and unrolling done above. */
+ normalize_outermost_list (result);
+ VERIFY_LIST (result);
+ }
+ return result;
+}
+
+
+/* Create the intersection of an argument list and the empty list.
+ Return NULL if the intersection is empty. */
+/* Memory effects: The result, if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+make_intersection_with_empty_list (struct format_arg_list *list)
+{
+#if 0 /* equivalent but slower */
+ return make_intersected_list (copy_list (list), make_empty_list ());
+#else
+ if (list->initial.count > 0
+ ? list->initial.element[0].presence == FCT_REQUIRED
+ : list->repeated.count > 0
+ && list->repeated.element[0].presence == FCT_REQUIRED)
+ return NULL;
+ else
+ return make_empty_list ();
+#endif
+}
+
+
+#ifdef unused
+/* Create the intersection of two argument list constraints. NULL stands
+ for an impossible situation, i.e. a contradiction. */
+/* Memory effects: list1 and list2 are freed if non-NULL. The result,
+ if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+intersection (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ if (list1 != NULL)
+ {
+ if (list2 != NULL)
+ return make_intersected_list (list1, list2);
+ else
+ {
+ free_list (list1);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (list2 != NULL)
+ {
+ free_list (list2);
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+}
+#endif
+
+
+/* ===================== Union of two format_arg_lists ===================== */
+
+/* Create the union (i.e. alternative constraints) of two argument
+ constraints. */
+static void
+make_union_element (struct format_arg *re,
+ const struct format_arg * e1,
+ const struct format_arg * e2)
+{
+ /* Union of the cdr types. */
+ if (e1->presence == FCT_REQUIRED && e2->presence == FCT_REQUIRED)
+ re->presence = FCT_REQUIRED;
+ else /* Either one of them is FCT_OPTIONAL. */
+ re->presence = FCT_OPTIONAL;
+
+ /* Union of the arg types. */
+ if (e1->type == e2->type)
+ {
+ re->type = e1->type;
+ if (re->type == FAT_LIST)
+ re->list = make_union_list (copy_list (e1->list),
+ copy_list (e2->list));
+ }
+ else if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ && (e2->type == FAT_CHARACTER_NULL || e2->type == FAT_CHARACTER
+ || e2->type == FAT_INTEGER_NULL || e2->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ && (e1->type == FAT_CHARACTER_NULL || e1->type == FAT_CHARACTER
+ || e1->type == FAT_INTEGER_NULL || e1->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_CHARACTER_NULL && e2->type == FAT_CHARACTER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_CHARACTER_NULL && e1->type == FAT_CHARACTER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_INTEGER_NULL && e2->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_INTEGER_NULL && e1->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_REAL && e2->type == FAT_INTEGER)
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_REAL && e1->type == FAT_INTEGER)
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_COMPLEX
+ && (e2->type == FAT_REAL || e2->type == FAT_INTEGER))
+ {
+ re->type = e1->type;
+ }
+ else if (e2->type == FAT_COMPLEX
+ && (e1->type == FAT_REAL || e1->type == FAT_INTEGER))
+ {
+ re->type = e2->type;
+ }
+ else if (e1->type == FAT_LIST && is_empty_list (e1->list))
+ {
+ if (e2->type == FAT_CHARACTER_INTEGER_NULL
+ || e2->type == FAT_CHARACTER_NULL
+ || e2->type == FAT_INTEGER_NULL)
+ re->type = e2->type;
+ else if (e2->type == FAT_CHARACTER)
+ re->type = FAT_CHARACTER_NULL;
+ else if (e2->type == FAT_INTEGER)
+ re->type = FAT_INTEGER_NULL;
+ else
+ re->type = FAT_OBJECT;
+ }
+ else if (e2->type == FAT_LIST && is_empty_list (e2->list))
+ {
+ if (e1->type == FAT_CHARACTER_INTEGER_NULL
+ || e1->type == FAT_CHARACTER_NULL
+ || e1->type == FAT_INTEGER_NULL)
+ re->type = e1->type;
+ else if (e1->type == FAT_CHARACTER)
+ re->type = FAT_CHARACTER_NULL;
+ else if (e1->type == FAT_INTEGER)
+ re->type = FAT_INTEGER_NULL;
+ else
+ re->type = FAT_OBJECT;
+ }
+ else if ((e1->type == FAT_CHARACTER || e1->type == FAT_CHARACTER_NULL)
+ && (e2->type == FAT_INTEGER || e2->type == FAT_INTEGER_NULL))
+ {
+ re->type = FAT_CHARACTER_INTEGER_NULL;
+ }
+ else if ((e2->type == FAT_CHARACTER || e2->type == FAT_CHARACTER_NULL)
+ && (e1->type == FAT_INTEGER || e1->type == FAT_INTEGER_NULL))
+ {
+ re->type = FAT_CHARACTER_INTEGER_NULL;
+ }
+ else
+ {
+ /* Other union types are too hard to describe precisely. */
+ re->type = FAT_OBJECT;
+ }
+}
+
+/* Create the union (i.e. alternative constraints) of two argument list
+ constraints. Free both argument lists when done. */
+/* Memory effects: list1 and list2 are freed. The result is freshly
+ allocated. */
+static struct format_arg_list *
+make_union_list (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ struct format_arg_list *result;
+
+ VERIFY_LIST (list1);
+ VERIFY_LIST (list2);
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ {
+ /* Step 1: Ensure list1->repeated.length == list2->repeated.length. */
+ {
+ unsigned int n1 = list1->repeated.length;
+ unsigned int n2 = list2->repeated.length;
+ unsigned int g = gcd (n1, n2);
+ unsigned int m1 = n2 / g; /* = lcm(n1,n2) / n1 */
+ unsigned int m2 = n1 / g; /* = lcm(n1,n2) / n2 */
+
+ unfold_loop (list1, m1);
+ unfold_loop (list2, m2);
+ /* Now list1->repeated.length = list2->repeated.length = lcm(n1,n2). */
+ }
+
+ /* Step 2: Ensure that list1->initial.length == list2->initial.length. */
+ {
+ unsigned int m = MAX (list1->initial.length, list2->initial.length);
+
+ rotate_loop (list1, m);
+ rotate_loop (list2, m);
+ }
+
+ ASSERT (list1->initial.length == list2->initial.length);
+ ASSERT (list1->repeated.length == list2->repeated.length);
+ }
+ else if (list1->repeated.length > 0)
+ {
+ /* Ensure the initial segment of the result can be computed from the
+ initial segment of list1. */
+ if (list2->initial.length >= list1->initial.length)
+ {
+ rotate_loop (list1, list2->initial.length);
+ if (list1->repeated.element[0].presence == FCT_REQUIRED)
+ rotate_loop (list1, list1->initial.length + 1);
+ }
+ }
+ else if (list2->repeated.length > 0)
+ {
+ /* Ensure the initial segment of the result can be computed from the
+ initial segment of list2. */
+ if (list1->initial.length >= list2->initial.length)
+ {
+ rotate_loop (list2, list1->initial.length);
+ if (list2->repeated.element[0].presence == FCT_REQUIRED)
+ rotate_loop (list2, list2->initial.length + 1);
+ }
+ }
+
+ /* Step 3: Allocate the result. */
+ result = XMALLOC (struct format_arg_list);
+ result->initial.count = 0;
+ result->initial.allocated = 0;
+ result->initial.element = NULL;
+ result->initial.length = 0;
+ result->repeated.count = 0;
+ result->repeated.allocated = 0;
+ result->repeated.element = NULL;
+ result->repeated.length = 0;
+
+ /* Step 4: Elementwise union of list1->initial, list2->initial. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->initial.element; c1 = list1->initial.count;
+ e2 = list2->initial.element; c2 = list2->initial.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Union of the argument types. */
+ make_union_element (re, e1, e2);
+
+ result->initial.count++;
+ result->initial.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ if (c1 > 0)
+ {
+ /* list2 already terminated, but still more elements in list1->initial.
+ Copy them all, but turn the first presence to FCT_OPTIONAL. */
+ ASSERT (list2->repeated.count == 0);
+
+ if (e1->presence == FCT_REQUIRED)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e1);
+ re->presence = FCT_OPTIONAL;
+ re->repcount = 1;
+ result->initial.count++;
+ result->initial.length += 1;
+ e1->repcount -= 1;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ }
+
+ /* Ensure room in result->initial. */
+ ensure_initial_alloc (result, result->initial.count + c1);
+ while (c1 > 0)
+ {
+ struct format_arg *re;
+
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e1);
+ result->initial.count++;
+ result->initial.length += re->repcount;
+ e1++;
+ c1--;
+ }
+ }
+ else if (c2 > 0)
+ {
+ /* list1 already terminated, but still more elements in list2->initial.
+ Copy them all, but turn the first presence to FCT_OPTIONAL. */
+ ASSERT (list1->repeated.count == 0);
+
+ if (e2->presence == FCT_REQUIRED)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->initial. */
+ grow_initial_alloc (result);
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e2);
+ re->presence = FCT_OPTIONAL;
+ re->repcount = 1;
+ result->initial.count++;
+ result->initial.length += 1;
+ e2->repcount -= 1;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+
+ /* Ensure room in result->initial. */
+ ensure_initial_alloc (result, result->initial.count + c2);
+ while (c2 > 0)
+ {
+ struct format_arg *re;
+
+ re = &result->initial.element[result->initial.count];
+ copy_element (re, e2);
+ result->initial.count++;
+ result->initial.length += re->repcount;
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+
+ if (list1->repeated.length > 0 && list2->repeated.length > 0)
+ /* Step 5: Elementwise union of list1->repeated, list2->repeated. */
+ {
+ struct format_arg *e1;
+ struct format_arg *e2;
+ unsigned int c1;
+ unsigned int c2;
+
+ e1 = list1->repeated.element; c1 = list1->repeated.count;
+ e2 = list2->repeated.element; c2 = list2->repeated.count;
+ while (c1 > 0 && c2 > 0)
+ {
+ struct format_arg *re;
+
+ /* Ensure room in result->repeated. */
+ grow_repeated_alloc (result);
+ re = &result->repeated.element[result->repeated.count];
+ re->repcount = MIN (e1->repcount, e2->repcount);
+
+ /* Union of the argument types. */
+ make_union_element (re, e1, e2);
+
+ result->repeated.count++;
+ result->repeated.length += re->repcount;
+
+ e1->repcount -= re->repcount;
+ if (e1->repcount == 0)
+ {
+ e1++;
+ c1--;
+ }
+ e2->repcount -= re->repcount;
+ if (e2->repcount == 0)
+ {
+ e2++;
+ c2--;
+ }
+ }
+ ASSERT (c1 == 0 && c2 == 0);
+ }
+ else if (list1->repeated.length > 0)
+ {
+ /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
+ initial segment. Just copy the repeated segment of list1. */
+ unsigned int i;
+
+ result->repeated.count = list1->repeated.count;
+ result->repeated.allocated = result->repeated.count;
+ result->repeated.element =
+ XNMALLOC (result->repeated.allocated, struct format_arg);
+ for (i = 0; i < list1->repeated.count; i++)
+ copy_element (&result->repeated.element[i],
+ &list1->repeated.element[i]);
+ result->repeated.length = list1->repeated.length;
+ }
+ else if (list2->repeated.length > 0)
+ {
+ /* Turning FCT_REQUIRED into FCT_OPTIONAL was already handled in the
+ initial segment. Just copy the repeated segment of list2. */
+ unsigned int i;
+
+ result->repeated.count = list2->repeated.count;
+ result->repeated.allocated = result->repeated.count;
+ result->repeated.element =
+ XNMALLOC (result->repeated.allocated, struct format_arg);
+ for (i = 0; i < list2->repeated.count; i++)
+ copy_element (&result->repeated.element[i],
+ &list2->repeated.element[i]);
+ result->repeated.length = list2->repeated.length;
+ }
+
+ free_list (list1);
+ free_list (list2);
+ /* Undo the loop unfolding and unrolling done above. */
+ normalize_outermost_list (result);
+ VERIFY_LIST (result);
+ return result;
+}
+
+
+/* Create the union of an argument list and the empty list. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_union_with_empty_list (struct format_arg_list *list)
+{
+#if 0 /* equivalent but slower */
+ return make_union_list (list, make_empty_list ());
+#else
+ VERIFY_LIST (list);
+
+ if (list->initial.count > 0
+ ? list->initial.element[0].presence == FCT_REQUIRED
+ : list->repeated.count > 0
+ && list->repeated.element[0].presence == FCT_REQUIRED)
+ {
+ initial_splitelement (list, 1);
+ ASSERT (list->initial.count > 0);
+ ASSERT (list->initial.element[0].repcount == 1);
+ ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
+ list->initial.element[0].presence = FCT_OPTIONAL;
+
+ /* We might need to merge list->initial.element[0] and
+ list->initial.element[1]. */
+ normalize_outermost_list (list);
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+#endif
+}
+
+
+/* Create the union of two argument list constraints. NULL stands for an
+ impossible situation, i.e. a contradiction. */
+/* Memory effects: list1 and list2 are freed if non-NULL. The result,
+ if non-NULL, is freshly allocated. */
+static struct format_arg_list *
+union (struct format_arg_list *list1, struct format_arg_list *list2)
+{
+ if (list1 != NULL)
+ {
+ if (list2 != NULL)
+ return make_union_list (list1, list2);
+ else
+ return list1;
+ }
+ else
+ {
+ if (list2 != NULL)
+ return list2;
+ else
+ return NULL;
+ }
+}
+
+
+/* =========== Adding specific constraints to a format_arg_list =========== */
+
+
+/* Test whether arguments 0..n are required arguments in a list. */
+static bool
+is_required (const struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s;
+ unsigned int t;
+
+ /* We'll check whether the first n+1 presence flags are FCT_REQUIRED. */
+ t = n + 1;
+
+ /* Walk the list->initial segment. */
+ for (s = 0;
+ s < list->initial.count && t >= list->initial.element[s].repcount;
+ t -= list->initial.element[s].repcount, s++)
+ if (list->initial.element[s].presence != FCT_REQUIRED)
+ return false;
+
+ if (t == 0)
+ return true;
+
+ if (s < list->initial.count)
+ {
+ if (list->initial.element[s].presence != FCT_REQUIRED)
+ return false;
+ else
+ return true;
+ }
+
+ /* Walk the list->repeated segment. */
+ if (list->repeated.count == 0)
+ return false;
+
+ for (s = 0;
+ s < list->repeated.count && t >= list->repeated.element[s].repcount;
+ t -= list->repeated.element[s].repcount, s++)
+ if (list->repeated.element[s].presence != FCT_REQUIRED)
+ return false;
+
+ if (t == 0)
+ return true;
+
+ if (s < list->repeated.count)
+ {
+ if (list->repeated.element[s].presence != FCT_REQUIRED)
+ return false;
+ else
+ return true;
+ }
+
+ /* The list->repeated segment consists only of FCT_REQUIRED. So,
+ regardless how many more passes through list->repeated would be
+ needed until t becomes 0, the result is true. */
+ return true;
+}
+
+
+/* Add a constraint to an argument list, namely that the arguments 0...n are
+ present. NULL stands for an impossible situation, i.e. a contradiction. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_required_constraint (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int i, rest;
+
+ if (list == NULL)
+ return NULL;
+
+ VERIFY_LIST (list);
+
+ if (list->repeated.count == 0 && list->initial.length <= n)
+ {
+ /* list is already constrained to have at most length n.
+ Contradiction. */
+ free_list (list);
+ return NULL;
+ }
+
+ initial_splitelement (list, n + 1);
+
+ for (i = 0, rest = n + 1; rest > 0; )
+ {
+ list->initial.element[i].presence = FCT_REQUIRED;
+ rest -= list->initial.element[i].repcount;
+ i++;
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ never present. NULL stands for an impossible situation, i.e. a
+ contradiction. */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_end_constraint (struct format_arg_list *list, unsigned int n)
+{
+ unsigned int s, i;
+ enum format_cdr_type n_presence;
+
+ if (list == NULL)
+ return NULL;
+
+ VERIFY_LIST (list);
+
+ if (list->repeated.count == 0 && list->initial.length <= n)
+ /* list is already constrained to have at most length n. */
+ return list;
+
+ s = initial_splitelement (list, n);
+ n_presence =
+ (s < list->initial.count
+ ? /* n < list->initial.length */ list->initial.element[s].presence
+ : /* n >= list->initial.length */ list->repeated.element[0].presence);
+
+ for (i = s; i < list->initial.count; i++)
+ {
+ list->initial.length -= list->initial.element[i].repcount;
+ free_element (&list->initial.element[i]);
+ }
+ list->initial.count = s;
+
+ for (i = 0; i < list->repeated.count; i++)
+ free_element (&list->repeated.element[i]);
+ if (list->repeated.element != NULL)
+ free (list->repeated.element);
+ list->repeated.element = NULL;
+ list->repeated.allocated = 0;
+ list->repeated.count = 0;
+ list->repeated.length = 0;
+
+ if (n_presence == FCT_REQUIRED)
+ return backtrack_in_initial (list);
+ else
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ of a given type. NULL stands for an impossible situation, i.e. a
+ contradiction. Assumes a preceding add_required_constraint (list, n). */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_type_constraint (struct format_arg_list *list, unsigned int n,
+ enum format_arg_type type)
+{
+ unsigned int s;
+ struct format_arg newconstraint;
+ struct format_arg tmpelement;
+
+ if (list == NULL)
+ return NULL;
+
+ /* Through the previous add_required_constraint, we can assume
+ list->initial.length >= n+1. */
+
+ s = initial_unshare (list, n);
+
+ newconstraint.presence = FCT_OPTIONAL;
+ newconstraint.type = type;
+ if (!make_intersected_element (&tmpelement,
+ &list->initial.element[s], &newconstraint))
+ return add_end_constraint (list, n);
+ free_element (&list->initial.element[s]);
+ list->initial.element[s].type = tmpelement.type;
+ list->initial.element[s].list = tmpelement.list;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* Add a constraint to an argument list, namely that the argument n is
+ of a given list type. NULL stands for an impossible situation, i.e. a
+ contradiction. Assumes a preceding add_required_constraint (list, n). */
+/* Memory effects: list is freed. The result is freshly allocated. */
+static struct format_arg_list *
+add_listtype_constraint (struct format_arg_list *list, unsigned int n,
+ enum format_arg_type type,
+ struct format_arg_list *sublist)
+{
+ unsigned int s;
+ struct format_arg newconstraint;
+ struct format_arg tmpelement;
+
+ if (list == NULL)
+ return NULL;
+
+ /* Through the previous add_required_constraint, we can assume
+ list->initial.length >= n+1. */
+
+ s = initial_unshare (list, n);
+
+ newconstraint.presence = FCT_OPTIONAL;
+ newconstraint.type = type;
+ newconstraint.list = sublist;
+ if (!make_intersected_element (&tmpelement,
+ &list->initial.element[s], &newconstraint))
+ return add_end_constraint (list, n);
+ free_element (&list->initial.element[s]);
+ list->initial.element[s].type = tmpelement.type;
+ list->initial.element[s].list = tmpelement.list;
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* ============= Subroutines used by the format string parser ============= */
+
+static void
+add_req_type_constraint (struct format_arg_list **listp,
+ unsigned int position, enum format_arg_type type)
+{
+ *listp = add_required_constraint (*listp, position);
+ *listp = add_type_constraint (*listp, position, type);
+}
+
+
+static void
+add_req_listtype_constraint (struct format_arg_list **listp,
+ unsigned int position, enum format_arg_type type,
+ struct format_arg_list *sublist)
+{
+ *listp = add_required_constraint (*listp, position);
+ *listp = add_listtype_constraint (*listp, position, type, sublist);
+}
+
+
+/* Create an endless repeated list whose elements are lists constrained
+ by sublist. */
+/* Memory effects: sublist is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_repeated_list_of_lists (struct format_arg_list *sublist)
+{
+ if (sublist == NULL)
+ /* The list cannot have a single element. */
+ return make_empty_list ();
+ else
+ {
+ struct format_arg_list *listlist;
+
+ listlist = XMALLOC (struct format_arg_list);
+
+ listlist->initial.count = 0;
+ listlist->initial.allocated = 0;
+ listlist->initial.element = NULL;
+ listlist->initial.length = 0;
+ listlist->repeated.count = 1;
+ listlist->repeated.allocated = 1;
+ listlist->repeated.element = XNMALLOC (1, struct format_arg);
+ listlist->repeated.element[0].repcount = 1;
+ listlist->repeated.element[0].presence = FCT_OPTIONAL;
+ listlist->repeated.element[0].type = FAT_LIST;
+ listlist->repeated.element[0].list = sublist;
+ listlist->repeated.length = 1;
+
+ VERIFY_LIST (listlist);
+
+ return listlist;
+ }
+}
+
+
+/* Create an endless repeated list which represents the union of a finite
+ number of copies of L, each time shifted by period:
+ ()
+ L
+ L and (*^period L)
+ L and (*^period L) and (*^{2 period} L)
+ L and (*^period L) and (*^{2 period} L) and (*^{3 period} L)
+ ...
+ */
+/* Memory effects: sublist is freed. The result is freshly allocated. */
+static struct format_arg_list *
+make_repeated_list (struct format_arg_list *sublist, unsigned int period)
+{
+ struct segment tmp;
+ struct segment *srcseg;
+ struct format_arg_list *list;
+ unsigned int p, n, i, si, ti, j, sj, tj, splitindex, newcount;
+ bool ended;
+
+ VERIFY_LIST (sublist);
+
+ ASSERT (period > 0);
+
+ if (sublist->repeated.count == 0)
+ {
+ /* L is a finite list. */
+
+ if (sublist->initial.length < period)
+ /* L and (*^period L) is a contradition, so we need to consider
+ only 1 and 0 iterations. */
+ return make_union_with_empty_list (sublist);
+
+ srcseg = &sublist->initial;
+ p = period;
+ }
+ else
+ {
+ /* L is an infinite list. */
+ /* p := lcm (period, period of L) */
+ unsigned int Lp = sublist->repeated.length;
+ unsigned int m = period / gcd (period, Lp); /* = lcm(period,Lp) / Lp */
+
+ unfold_loop (sublist, m);
+ p = m * Lp;
+
+ /* Concatenate the initial and the repeated segments into a single
+ segment. */
+ tmp.count = sublist->initial.count + sublist->repeated.count;
+ tmp.allocated = tmp.count;
+ tmp.element = XNMALLOC (tmp.allocated, struct format_arg);
+ for (i = 0; i < sublist->initial.count; i++)
+ tmp.element[i] = sublist->initial.element[i];
+ for (j = 0; j < sublist->repeated.count; i++, j++)
+ tmp.element[i] = sublist->initial.element[j];
+ tmp.length = sublist->initial.length + sublist->repeated.length;
+
+ srcseg = &tmp;
+ }
+
+ n = srcseg->length;
+
+ /* Example: n = 7, p = 2
+ Let L = (A B C D E F G).
+
+ L = A B C D E F G
+ L & L<<p = A B C&A D&B E&C F&D G&E
+ L & L<<p & L<<2p = A B C&A D&B E&C&A F&D&B G&E&C
+ ... = A B C&A D&B E&C&A F&D&B G&E&C&A
+
+ Thus the result has an initial segment of length n - p and a period
+ of p, and can be computed by floor(n/p) intersection operations.
+ Or by a single incremental intersection operation, going from left
+ to right. */
+
+ list = XMALLOC (struct format_arg_list);
+ list->initial.count = 0;
+ list->initial.allocated = 0;
+ list->initial.element = NULL;
+ list->initial.length = 0;
+ list->repeated.count = 0;
+ list->repeated.allocated = 0;
+ list->repeated.element = NULL;
+ list->repeated.length = 0;
+
+ /* Sketch:
+ for (i = 0; i < p; i++)
+ list->initial.element[i] = srcseg->element[i];
+ list->initial.element[0].presence = FCT_OPTIONAL; // union with empty list
+ for (i = p, j = 0; i < n; i++, j++)
+ list->initial.element[i] = srcseg->element[i] & list->initial.element[j];
+ */
+
+ ended = false;
+
+ i = 0, ti = 0, si = 0;
+ while (i < p)
+ {
+ unsigned int k = MIN (srcseg->element[si].repcount - ti, p - i);
+
+ /* Ensure room in list->initial. */
+ grow_initial_alloc (list);
+ copy_element (&list->initial.element[list->initial.count],
+ &srcseg->element[si]);
+ list->initial.element[list->initial.count].repcount = k;
+ list->initial.count++;
+ list->initial.length += k;
+
+ i += k;
+ ti += k;
+ if (ti == srcseg->element[si].repcount)
+ {
+ ti = 0;
+ si++;
+ }
+ }
+
+ ASSERT (list->initial.count > 0);
+ if (list->initial.element[0].presence == FCT_REQUIRED)
+ {
+ initial_splitelement (list, 1);
+ ASSERT (list->initial.element[0].presence == FCT_REQUIRED);
+ ASSERT (list->initial.element[0].repcount == 1);
+ list->initial.element[0].presence = FCT_OPTIONAL;
+ }
+
+ j = 0, tj = 0, sj = 0;
+ while (i < n)
+ {
+ unsigned int k =
+ MIN (srcseg->element[si].repcount - ti,
+ list->initial.element[sj].repcount - tj);
+
+ /* Ensure room in list->initial. */
+ grow_initial_alloc (list);
+ if (!make_intersected_element (&list->initial.element[list->initial.count],
+ &srcseg->element[si],
+ &list->initial.element[sj]))
+ {
+ if (list->initial.element[list->initial.count].presence == FCT_REQUIRED)
+ {
+ /* Contradiction. Backtrack. */
+ list = backtrack_in_initial (list);
+ ASSERT (list != NULL); /* at least the empty list is valid */
+ return list;
+ }
+ else
+ {
+ /* The list ends here. */
+ ended = true;
+ break;
+ }
+ }
+ list->initial.element[list->initial.count].repcount = k;
+ list->initial.count++;
+ list->initial.length += k;
+
+ i += k;
+ ti += k;
+ if (ti == srcseg->element[si].repcount)
+ {
+ ti = 0;
+ si++;
+ }
+
+ j += k;
+ tj += k;
+ if (tj == list->initial.element[sj].repcount)
+ {
+ tj = 0;
+ sj++;
+ }
+ }
+ if (!ended)
+ ASSERT (list->initial.length == n);
+
+ /* Add optional exit points at 0, period, 2*period etc.
+ FIXME: Not sure this is correct in all cases. */
+ for (i = 0; i < list->initial.length; i += period)
+ {
+ si = initial_unshare (list, i);
+ list->initial.element[si].presence = FCT_OPTIONAL;
+ }
+
+ if (!ended)
+ {
+ /* Now split off the repeated part. */
+ splitindex = initial_splitelement (list, n - p);
+ newcount = list->initial.count - splitindex;
+ if (newcount > list->repeated.allocated)
+ {
+ list->repeated.allocated = newcount;
+ list->repeated.element = XNMALLOC (newcount, struct format_arg);
+ }
+ for (i = splitindex, j = 0; i < n; i++, j++)
+ list->repeated.element[j] = list->initial.element[i];
+ list->repeated.count = newcount;
+ list->repeated.length = p;
+ list->initial.count = splitindex;
+ list->initial.length = n - p;
+ }
+
+ VERIFY_LIST (list);
+
+ return list;
+}
+
+
+/* ================= Handling of format string directives ================= */
+
+/* Possible signatures of format directives. */
+static const enum format_arg_type I [1] = { FAT_INTEGER_NULL };
+static const enum format_arg_type II [2] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL
+};
+static const enum format_arg_type IIC [3] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL
+};
+static const enum format_arg_type ICCI [4] = {
+ FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_INTEGER_NULL
+};
+static const enum format_arg_type IIIC [4] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL
+};
+static const enum format_arg_type IICCI [5] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL,
+ FAT_INTEGER_NULL
+};
+static const enum format_arg_type IIICC [5] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_CHARACTER_NULL,
+ FAT_CHARACTER_NULL
+};
+static const enum format_arg_type IIIICCC [7] = {
+ FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL, FAT_INTEGER_NULL,
+ FAT_CHARACTER_NULL, FAT_CHARACTER_NULL, FAT_CHARACTER_NULL
+};
+static const enum format_arg_type THREE [3] = {
+ FAT_CHARACTER_INTEGER_NULL, FAT_CHARACTER_INTEGER_NULL,
+ FAT_CHARACTER_INTEGER_NULL
+};
+
+
+/* Check the parameters. For V params, add the constraint to the argument
+ list. Return false and fill in *invalid_reason if the format string is
+ invalid. */
+static bool
+check_params (struct format_arg_list **listp,
+ unsigned int paramcount, struct param *params,
+ unsigned int t_count, const enum format_arg_type *t_types,
+ unsigned int directives, char **invalid_reason)
+{
+ unsigned int orig_paramcount = paramcount;
+ unsigned int orig_t_count = t_count;
+
+ for (; paramcount > 0 && t_count > 0;
+ params++, paramcount--, t_types++, t_count--)
+ {
+ switch (*t_types)
+ {
+ case FAT_CHARACTER_INTEGER_NULL:
+ break;
+ case FAT_CHARACTER_NULL:
+ switch (params->type)
+ {
+ case PT_NIL: case PT_CHARACTER: case PT_V:
+ break;
+ case PT_INTEGER: case PT_ARGCOUNT:
+ /* wrong param type */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "integer", "character");
+ return false;
+ }
+ break;
+ case FAT_INTEGER_NULL:
+ switch (params->type)
+ {
+ case PT_NIL: case PT_INTEGER: case PT_ARGCOUNT: case PT_V:
+ break;
+ case PT_CHARACTER:
+ /* wrong param type */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, parameter %u is of type '%s' but a parameter of type '%s' is expected."), directives, orig_paramcount - paramcount + 1, "character", "integer");
+ return false;
+ }
+ break;
+ default:
+ abort ();
+ }
+ if (params->type == PT_V)
+ {
+ int position = params->value;
+ if (position >= 0)
+ add_req_type_constraint (listp, position, *t_types);
+ }
+ }
+
+ for (; paramcount > 0; params++, paramcount--)
+ switch (params->type)
+ {
+ case PT_NIL:
+ break;
+ case PT_CHARACTER: case PT_INTEGER: case PT_ARGCOUNT:
+ /* too many params for directive */
+ *invalid_reason =
+ xasprintf (ngettext ("In the directive number %u, too many parameters are given; expected at most %u parameter.",
+ "In the directive number %u, too many parameters are given; expected at most %u parameters.",
+ orig_t_count),
+ directives, orig_t_count);
+ return false;
+ case PT_V:
+ /* Force argument to be NIL. */
+ {
+ int position = params->value;
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (listp, position,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+/* ======================= The format string parser ======================= */
+
+/* Parse a piece of format string, until the matching terminating format
+ directive is encountered.
+ format is the remainder of the format string.
+ position is the position in this argument list, if known, or -1 if unknown.
+ list represents the argument list constraints at the current parse point.
+ NULL stands for a contradiction.
+ escape represents the union of the argument list constraints at all the
+ currently pending FORMAT-UP-AND-OUT points. NULL stands for a contradiction
+ or an empty union.
+ All four are updated upon valid return.
+ *separatorp is set to true if the parse terminated due to a ~; separator,
+ more precisely to 2 if with colon, or to 1 if without colon.
+ spec is the global struct spec.
+ terminator is the directive that terminates this parse.
+ separator specifies if ~; separators are allowed.
+ fdi is an array to be filled with format directive indicators, or NULL.
+ If the format string is invalid, false is returned and *invalid_reason is
+ set to an error message explaining why. */
+static bool
+parse_upto (const char **formatp,
+ int *positionp, struct format_arg_list **listp,
+ struct format_arg_list **escapep, int *separatorp,
+ struct spec *spec, char terminator, bool separator,
+ char *fdi, char **invalid_reason)
+{
+ const char *format = *formatp;
+ const char *const format_start = format;
+ int position = *positionp;
+ struct format_arg_list *list = *listp;
+ struct format_arg_list *escape = *escapep;
+
+ for (; *format != '\0'; )
+ if (*format++ == '~')
+ {
+ bool colon_p = false;
+ bool atsign_p = false;
+ unsigned int paramcount = 0;
+ struct param *params = NULL;
+
+ FDI_SET (format - 1, FMTDIR_START);
+
+ /* Count number of directives. */
+ spec->directives++;
+
+ /* Parse parameters. */
+ for (;;)
+ {
+ enum param_type type = PT_NIL;
+ int value = 0;
+
+ if (c_isdigit (*format))
+ {
+ type = PT_INTEGER;
+ do
+ {
+ value = 10 * value + (*format - '0');
+ format++;
+ }
+ while (c_isdigit (*format));
+ }
+ else if (*format == '+' || *format == '-')
+ {
+ bool negative = (*format == '-');
+ type = PT_INTEGER;
+ format++;
+ if (!c_isdigit (*format))
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '%c' is not followed by a digit."), spec->directives, format[-1]);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ return false;
+ }
+ do
+ {
+ value = 10 * value + (*format - '0');
+ format++;
+ }
+ while (c_isdigit (*format));
+ if (negative)
+ value = -value;
+ }
+ else if (*format == '\'')
+ {
+ type = PT_CHARACTER;
+ format++;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ format++;
+ }
+ else if (*format == 'V' || *format == 'v')
+ {
+ type = PT_V;
+ format++;
+ value = position;
+ /* Consumes an argument. */
+ if (position >= 0)
+ position++;
+ }
+ else if (*format == '#')
+ {
+ type = PT_ARGCOUNT;
+ format++;
+ }
+
+ params =
+ (struct param *)
+ xrealloc (params, (paramcount + 1) * sizeof (struct param));
+ params[paramcount].type = type;
+ params[paramcount].value = value;
+ paramcount++;
+
+ if (*format == ',')
+ format++;
+ else
+ break;
+ }
+
+ /* Parse modifiers. */
+ for (;;)
+ {
+ if (*format == ':')
+ {
+ format++;
+ colon_p = true;
+ }
+ else if (*format == '@')
+ {
+ format++;
+ atsign_p = true;
+ }
+ else
+ break;
+ }
+
+ /* Parse directive. */
+ switch (*format++)
+ {
+ case 'A': case 'a': /* 22.3.4.1 FORMAT-ASCII */
+ case 'S': case 's': /* 22.3.4.2 FORMAT-S-EXPRESSION */
+ if (!check_params (&list, paramcount, params, 4, IIIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case 'C': case 'c': /* FORMAT-CHARACTER */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (paramcount == 0
+ || (paramcount == 1 && params[0].type == PT_NIL))
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_CHARACTER);
+ break;
+
+ case 'D': case 'd': /* 22.3.2.2 FORMAT-DECIMAL */
+ case 'B': case 'b': /* 22.3.2.3 FORMAT-BINARY */
+ case 'O': case 'o': /* 22.3.2.4 FORMAT-OCTAL */
+ case 'X': case 'x': /* 22.3.2.5 FORMAT-HEXADECIMAL */
+ if (!check_params (&list, paramcount, params, 4, ICCI,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_INTEGER);
+ break;
+
+ case 'R': case 'r': /* 22.3.2.1 FORMAT-RADIX */
+ if (!check_params (&list, paramcount, params, 5, IICCI,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_INTEGER);
+ break;
+
+ case 'P': case 'p': /* 22.3.8.3 FORMAT-PLURAL */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (colon_p)
+ {
+ /* Go back by 1 argument. */
+ if (position > 0)
+ position--;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case 'F': case 'f': /* 22.3.3.1 FORMAT-FIXED-FLOAT */
+ if (!check_params (&list, paramcount, params, 5, IIICC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case 'E': case 'e': /* 22.3.3.2 FORMAT-EXPONENTIAL-FLOAT */
+ case 'G': case 'g': /* 22.3.3.3 FORMAT-GENERAL-FLOAT */
+ if (!check_params (&list, paramcount, params, 7, IIIICCC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case '$': /* 22.3.3.4 FORMAT-DOLLARS-FLOAT */
+ if (!check_params (&list, paramcount, params, 4, IIIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_REAL);
+ break;
+
+ case 'I': case 'i': /* FORMAT-FIXED-FLOAT-COMPLEX */
+ if (!check_params (&list, paramcount, params, 5, IIICC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_COMPLEX);
+ break;
+
+ case 'Y': case 'y': /* FORMAT-PRETTY */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ break;
+
+ case '%': /* 22.3.1.2 FORMAT-TERPRI */
+ case '&': /* 22.3.1.3 FORMAT-FRESH-LINE */
+ case '_': /* FORMAT-SPACE */
+ case '/': /* FORMAT-TAB */
+ case '|': /* 22.3.1.4 FORMAT-PAGE */
+ case '~': /* 22.3.1.5 FORMAT-TILDE */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case '!': /* FORMAT-FORCE-OUTPUT */
+ case '\n': /* 22.3.9.3 #\Newline */
+ case 'Q': case 'q': /* FORMAT-IMPLEMENTATION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case 'T': case 't': /* FORMAT-TABULATE */
+ if (!check_params (&list, paramcount, params, 3, IIC,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ break;
+
+ case '*': /* 22.3.7.1 FORMAT-GOTO */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ {
+ int n; /* value of first parameter */
+ if (paramcount == 0
+ || (paramcount >= 1 && params[0].type == PT_NIL))
+ n = (atsign_p ? 0 : 1);
+ else if (paramcount >= 1 && params[0].type == PT_INTEGER)
+ n = params[0].value;
+ else
+ {
+ /* Unknown argument, leads to an unknown position. */
+ position = -1;
+ break;
+ }
+ if (n < 0)
+ {
+ /* invalid argument */
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, the argument %d is negative."), spec->directives, n);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (atsign_p)
+ {
+ /* Absolute goto. */
+ position = n;
+ }
+ else if (colon_p)
+ {
+ /* Backward goto. */
+ if (n > 0)
+ {
+ if (position >= 0)
+ {
+ if (position >= n)
+ position -= n;
+ else
+ position = 0;
+ }
+ else
+ position = -1;
+ }
+ }
+ else
+ {
+ /* Forward goto. */
+ if (position >= 0)
+ position += n;
+ }
+ }
+ break;
+
+ case '?': case 'K': case 'k': /* 22.3.7.6 FORMAT-INDIRECTION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
+ if (atsign_p)
+ position = -1;
+ else
+ if (position >= 0)
+ {
+ struct format_arg_list *sublist = make_unconstrained_list ();
+ add_req_listtype_constraint (&list, position++,
+ FAT_LIST, sublist);
+ free_list (sublist);
+ }
+ break;
+
+ case '(': /* 22.3.8.1 FORMAT-CASE-CONVERSION */
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ {
+ if (!parse_upto (formatp, positionp, listp, escapep,
+ NULL, spec, ')', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ }
+ format = *formatp;
+ position = *positionp;
+ list = *listp;
+ escape = *escapep;
+ break;
+
+ case ')': /* 22.3.8.2 FORMAT-CASE-CONVERSION-END */
+ if (terminator != ')')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), ')', '(');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '[': /* 22.3.7.2 FORMAT-CONDITIONAL */
+ if (atsign_p && colon_p)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, both the @ and the : modifiers are given."), spec->directives);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ else if (atsign_p)
+ {
+ struct format_arg_list *nil_list;
+ struct format_arg_list *union_list;
+
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ *formatp = format;
+ *escapep = escape;
+
+ /* First alternative: argument is NIL. */
+ nil_list = (list != NULL ? copy_list (list) : NULL);
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (&nil_list, position,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+
+ /* Second alternative: use sub-format. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ NULL, spec, ']', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ {
+ if (position >= 0)
+ {
+ if (sub_position == position + 1)
+ /* new position is branch independent */
+ position = position + 1;
+ else
+ /* new position is branch dependent */
+ position = -1;
+ }
+ }
+ else
+ {
+ if (position >= 0)
+ position = position + 1;
+ }
+ union_list = union (nil_list, sub_list);
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ else if (colon_p)
+ {
+ int union_position;
+ struct format_arg_list *union_list;
+
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+
+ *formatp = format;
+ *escapep = escape;
+ union_position = -2;
+ union_list = NULL;
+
+ /* First alternative. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ int sub_separator = 0;
+ if (position >= 0)
+ {
+ struct format_arg_list *empty_list = make_empty_list ();
+ add_req_listtype_constraint (&sub_list, position - 1,
+ FAT_LIST, empty_list);
+ free_list (empty_list);
+ }
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ &sub_separator, spec, ']', true,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (!sub_separator)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '~:[' is not followed by two clauses, separated by '~;'."), spec->directives);
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ union_position = sub_position;
+ union_list = union (union_list, sub_list);
+ }
+
+ /* Second alternative. */
+ {
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ NULL, spec, ']', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ if (sub_list != NULL)
+ {
+ if (union_position == -2)
+ union_position = sub_position;
+ else if (sub_position < 0
+ || sub_position != union_position)
+ union_position = -1;
+ }
+ union_list = union (union_list, sub_list);
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (union_position != -2)
+ position = union_position;
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ else
+ {
+ int arg_position;
+ int union_position;
+ struct format_arg_list *union_list;
+ bool last_alternative;
+
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+
+ /* If there was no first parameter, an argument is consumed. */
+ arg_position = -1;
+ if (!(paramcount >= 1 && params[0].type != PT_NIL))
+ if (position >= 0)
+ {
+ arg_position = position;
+ add_req_type_constraint (&list, position++, FAT_OBJECT);
+ }
+
+ *formatp = format;
+ *escapep = escape;
+
+ union_position = -2;
+ union_list = NULL;
+ last_alternative = false;
+ for (;;)
+ {
+ /* Next alternative. */
+ int sub_position = position;
+ struct format_arg_list *sub_list =
+ (list != NULL ? copy_list (list) : NULL);
+ int sub_separator = 0;
+ if (!parse_upto (formatp, &sub_position, &sub_list, escapep,
+ &sub_separator, spec, ']', !last_alternative,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ /* If this alternative is chosen, the argument arg_position
+ is an integer, namely the index of this alternative. */
+ if (!last_alternative && arg_position >= 0)
+ add_req_type_constraint (&sub_list, arg_position,
+ FAT_INTEGER);
+ if (sub_list != NULL)
+ {
+ if (union_position == -2)
+ union_position = sub_position;
+ else if (sub_position < 0
+ || sub_position != union_position)
+ union_position = -1;
+ }
+ union_list = union (union_list, sub_list);
+ if (sub_separator == 2)
+ last_alternative = true;
+ if (!sub_separator)
+ break;
+ }
+ if (!last_alternative)
+ {
+ /* An implicit default alternative. */
+ if (union_position == -2)
+ union_position = position;
+ else if (position < 0 || position != union_position)
+ union_position = -1;
+ if (list != NULL)
+ union_list = union (union_list, copy_list (list));
+ }
+
+ format = *formatp;
+ escape = *escapep;
+
+ if (union_position != -2)
+ position = union_position;
+ if (list != NULL)
+ free_list (list);
+ list = union_list;
+ }
+ break;
+
+ case ']': /* 22.3.7.3 FORMAT-CONDITIONAL-END */
+ if (terminator != ']')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), ']', '[');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '{': /* 22.3.7.4 FORMAT-ITERATION */
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ {
+ int sub_position = 0;
+ struct format_arg_list *sub_list = make_unconstrained_list ();
+ struct format_arg_list *sub_escape = NULL;
+ struct spec sub_spec;
+ sub_spec.directives = 0;
+ sub_spec.list = sub_list;
+ if (!parse_upto (formatp, &sub_position, &sub_list, &sub_escape,
+ NULL, &sub_spec, '}', false,
+ NULL, invalid_reason))
+ {
+ FDI_SET (**formatp == '\0' ? *formatp - 1 : *formatp,
+ FMTDIR_ERROR);
+ return false;
+ }
+ spec->directives += sub_spec.directives;
+
+ /* If the sub-formatstring is empty, except for the terminating
+ ~} directive, a formatstring argument is consumed. */
+ if (*format == '~' && sub_spec.directives == 1)
+ if (position >= 0)
+ add_req_type_constraint (&list, position++, FAT_FORMATSTRING);
+
+ if (colon_p)
+ {
+ /* Each iteration uses a new sublist. */
+ struct format_arg_list *listlist;
+
+ /* ~{ catches ~^. */
+ sub_list = union (sub_list, sub_escape);
+
+ listlist = make_repeated_list_of_lists (sub_list);
+
+ sub_list = listlist;
+ }
+ else
+ {
+ /* Each iteration's arguments are all concatenated in a
+ single list. */
+ struct format_arg_list *looplist;
+
+ /* FIXME: This is far from correct. Test cases:
+ abc~{~^~}
+ abc~{~S~^~S~}
+ abc~{~D~^~C~}
+ abc~{~D~^~D~}
+ abc~{~D~^~S~}
+ abc~{~D~^~C~}~:*~{~S~^~D~}
+ */
+
+ /* ~{ catches ~^. */
+ sub_list = union (sub_list, sub_escape);
+
+ if (sub_list == NULL)
+ looplist = make_empty_list ();
+ else
+ if (sub_position < 0 || sub_position == 0)
+ /* Too hard to track the possible argument types
+ when the iteration is performed 2 times or more.
+ So be satisfied with the constraints of executing
+ the iteration 1 or 0 times. */
+ looplist = make_union_with_empty_list (sub_list);
+ else
+ looplist = make_repeated_list (sub_list, sub_position);
+
+ sub_list = looplist;
+ }
+
+ if (atsign_p)
+ {
+ /* All remaining arguments are used. */
+ if (list != NULL && position >= 0)
+ {
+ shift_list (sub_list, position);
+ list = make_intersected_list (list, sub_list);
+ }
+ position = -1;
+ }
+ else
+ {
+ /* The argument is a list. */
+ if (position >= 0)
+ add_req_listtype_constraint (&list, position++,
+ FAT_LIST, sub_list);
+ }
+ }
+ format = *formatp;
+ break;
+
+ case '}': /* 22.3.7.5 FORMAT-ITERATION-END */
+ if (terminator != '}')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), '}', '{');
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ return true;
+
+ case '^': /* 22.3.9.2 FORMAT-UP-AND-OUT */
+ if (!check_params (&list, paramcount, params, 3, THREE,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (position >= 0 && list != NULL && is_required (list, position))
+ /* This ~^ can never be executed. Ignore it. */
+ break;
+ if (list != NULL)
+ {
+ struct format_arg_list *this_escape = copy_list (list);
+ if (position >= 0)
+ this_escape = add_end_constraint (this_escape, position);
+ escape = union (escape, this_escape);
+ }
+ if (position >= 0)
+ list = add_required_constraint (list, position);
+ break;
+
+ case ';': /* 22.3.9.1 FORMAT-SEPARATOR */
+ if (!separator)
+ {
+ *invalid_reason =
+ xasprintf (_("In the directive number %u, '~;' is used in an invalid position."), spec->directives);
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ if (terminator == '>')
+ {
+ if (!check_params (&list, paramcount, params, 1, I,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ if (!check_params (&list, paramcount, params, 0, NULL,
+ spec->directives, invalid_reason))
+ {
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ return false;
+ }
+ }
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ *separatorp = (colon_p ? 2 : 1);
+ return true;
+
+ default:
+ --format;
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec->directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ return false;
+ }
+
+ FDI_SET (format - 1, FMTDIR_END);
+
+ free (params);
+ }
+
+ *formatp = format;
+ *positionp = position;
+ *listp = list;
+ *escapep = escape;
+ if (terminator != '\0')
+ {
+ *invalid_reason =
+ xasprintf (_("Found '~%c' without matching '~%c'."), terminator - 1, terminator);
+ return false;
+ }
+ return true;
+}
+
+
+/* ============== Top level format string handling functions ============== */
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ struct spec spec;
+ struct spec *result;
+ int position = 0;
+ struct format_arg_list *escape;
+
+ spec.directives = 0;
+ spec.list = make_unconstrained_list ();
+ escape = NULL;
+
+ if (!parse_upto (&format, &position, &spec.list, &escape,
+ NULL, &spec, '\0', false,
+ fdi, invalid_reason))
+ /* Invalid format string. */
+ return NULL;
+
+ /* Catch ~^ here. */
+ spec.list = union (spec.list, escape);
+
+ if (spec.list == NULL)
+ {
+ /* Contradictory argument type information. */
+ *invalid_reason =
+ xstrdup (_("The string refers to some argument in incompatible ways."));
+ return NULL;
+ }
+
+ /* Normalize the result. */
+ normalize_list (spec.list);
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free_list (spec->list);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (equality)
+ {
+ if (!equal_list (spec1->list, spec2->list))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' are not equivalent"),
+ pretty_msgid, pretty_msgstr);
+ err = true;
+ }
+ }
+ else
+ {
+ struct format_arg_list *intersection =
+ make_intersected_list (copy_list (spec1->list),
+ copy_list (spec2->list));
+
+ if (!(intersection != NULL
+ && (normalize_list (intersection),
+ equal_list (intersection, spec2->list))))
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' are not a subset of those in '%s'"),
+ pretty_msgstr, pretty_msgid);
+ err = true;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_scheme =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+/* ============================= Testing code ============================= */
+
+#undef union
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void print_list (struct format_arg_list *list);
+
+static void
+print_element (struct format_arg *element)
+{
+ switch (element->presence)
+ {
+ case FCT_REQUIRED:
+ break;
+ case FCT_OPTIONAL:
+ printf (". ");
+ break;
+ default:
+ abort ();
+ }
+
+ switch (element->type)
+ {
+ case FAT_OBJECT:
+ printf ("*");
+ break;
+ case FAT_CHARACTER_INTEGER_NULL:
+ printf ("ci()");
+ break;
+ case FAT_CHARACTER_NULL:
+ printf ("c()");
+ break;
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_INTEGER_NULL:
+ printf ("i()");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_REAL:
+ printf ("r");
+ break;
+ case FAT_COMPLEX:
+ printf ("C");
+ break;
+ case FAT_LIST:
+ print_list (element->list);
+ break;
+ case FAT_FORMATSTRING:
+ printf ("~");
+ break;
+ default:
+ abort ();
+ }
+}
+
+static void
+print_list (struct format_arg_list *list)
+{
+ unsigned int i, j;
+
+ printf ("(");
+
+ for (i = 0; i < list->initial.count; i++)
+ for (j = 0; j < list->initial.element[i].repcount; j++)
+ {
+ if (i > 0 || j > 0)
+ printf (" ");
+ print_element (&list->initial.element[i]);
+ }
+
+ if (list->repeated.count > 0)
+ {
+ printf (" |");
+ for (i = 0; i < list->repeated.count; i++)
+ for (j = 0; j < list->repeated.element[i].repcount; j++)
+ {
+ printf (" ");
+ print_element (&list->repeated.element[i]);
+ }
+ }
+
+ printf (")");
+}
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ print_list (spec->list);
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-scheme.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-sh.c b/gettext-tools/src/format-sh.c
new file mode 100644
index 0000000..b796fcd
--- /dev/null
+++ b/gettext-tools/src/format-sh.c
@@ -0,0 +1,403 @@
+/* Shell format strings.
+ Copyright (C) 2003-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Shell format strings are simply strings subjects to variable substitution.
+ A variable substitution starts with '$' and is finished by either
+ - a nonempty sequence of alphanumeric ASCII characters, the first being
+ not a digit, or
+ - an opening brace '{', a nonempty sequence of alphanumeric ASCII
+ characters, the first being not a digit, and a closing brace '}'.
+ We don't support variable references like $1, $$ or $? since they make
+ no sense when 'envsubst' is invoked.
+ We don't support non-ASCII variable names, to avoid dependencies w.r.t. the
+ current encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
+ encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
+ SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
+ encodings.
+ We don't support the POSIX syntax for default or alternate values:
+ ${variable-default} ${variable:-default}
+ ${variable=default} ${variable:=default}
+ ${variable+replacement} ${variable:+replacement}
+ ${variable?ignored} ${variable:?ignored}
+ because the translator might be tempted to change the default value; if
+ we allow it we have a security problem; if we don't allow it the translator
+ will be surprised.
+ */
+
+struct named_arg
+{
+ char *name;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int named_arg_count;
+ unsigned int allocated;
+ struct named_arg *named;
+};
+
+
+static int
+named_arg_compare (const void *p1, const void *p2)
+{
+ return strcmp (((const struct named_arg *) p1)->name,
+ ((const struct named_arg *) p2)->name);
+}
+
+#define INVALID_NON_ASCII_VARIABLE() \
+ xstrdup (_("The string refers to a shell variable with a non-ASCII name."))
+#define INVALID_SHELL_SYNTAX() \
+ xstrdup (_("The string refers to a shell variable with complex shell brace syntax. This syntax is unsupported here due to security reasons."))
+#define INVALID_CONTEXT_DEPENDENT_VARIABLE() \
+ xstrdup (_("The string refers to a shell variable whose value may be different inside shell functions."))
+#define INVALID_EMPTY_VARIABLE() \
+ xstrdup (_("The string refers to a shell variable with an empty name."))
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.named_arg_count = 0;
+ spec.allocated = 0;
+ spec.named = NULL;
+
+ for (; *format != '\0';)
+ if (*format++ == '$')
+ {
+ /* A variable substitution. */
+ char *name;
+
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format == '{')
+ {
+ const char *name_start;
+ const char *name_end;
+ size_t n;
+
+ name_start = ++format;
+ for (; *format != '\0'; format++)
+ {
+ if (*format == '}')
+ break;
+ if (!c_isascii (*format))
+ {
+ *invalid_reason = INVALID_NON_ASCII_VARIABLE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ if (format > name_start
+ && (*format == '-' || *format == '=' || *format == '+'
+ || *format == '?' || *format == ':'))
+ {
+ *invalid_reason = INVALID_SHELL_SYNTAX ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ if (!(c_isalnum (*format) || *format == '_')
+ || (format == name_start && c_isdigit (*format)))
+ {
+ *invalid_reason = INVALID_CONTEXT_DEPENDENT_VARIABLE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ name_end = format++;
+
+ n = name_end - name_start;
+ if (n == 0)
+ {
+ *invalid_reason = INVALID_EMPTY_VARIABLE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ name = XNMALLOC (n + 1, char);
+ memcpy (name, name_start, n);
+ name[n] = '\0';
+ }
+ else if (c_isalpha (*format) || *format == '_')
+ {
+ const char *name_start;
+ const char *name_end;
+ size_t n;
+
+ name_start = format;
+ do
+ format++;
+ while (*format != '\0' && (c_isalnum (*format) || *format == '_'));
+ name_end = format;
+
+ n = name_end - name_start;
+ name = XNMALLOC (n + 1, char);
+ memcpy (name, name_start, n);
+ name[n] = '\0';
+ }
+ else if (*format != '\0')
+ {
+ if (!c_isascii (*format))
+ {
+ *invalid_reason = INVALID_NON_ASCII_VARIABLE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ else
+ {
+ *invalid_reason = INVALID_CONTEXT_DEPENDENT_VARIABLE ();
+ FDI_SET (format, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ }
+ else
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+
+ /* Named argument. */
+ if (spec.allocated == spec.named_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.named = (struct named_arg *) xrealloc (spec.named, spec.allocated * sizeof (struct named_arg));
+ }
+ spec.named[spec.named_arg_count].name = name;
+ spec.named_arg_count++;
+
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+
+ /* Sort the named argument array, and eliminate duplicates. */
+ if (spec.named_arg_count > 1)
+ {
+ unsigned int i, j;
+
+ qsort (spec.named, spec.named_arg_count, sizeof (struct named_arg),
+ named_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ for (i = j = 0; i < spec.named_arg_count; i++)
+ if (j > 0 && strcmp (spec.named[i].name, spec.named[j-1].name) == 0)
+ free (spec.named[i].name);
+ else
+ {
+ if (j < i)
+ spec.named[j].name = spec.named[i].name;
+ j++;
+ }
+ spec.named_arg_count = j;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec.named_arg_count; i++)
+ free (spec.named[i].name);
+ free (spec.named);
+ }
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->named != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < spec->named_arg_count; i++)
+ free (spec->named[i].name);
+ free (spec->named);
+ }
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->named_arg_count + spec2->named_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->named_arg_count;
+ unsigned int n2 = spec2->named_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ strcmp (spec1->named[i].name, spec2->named[j].name));
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s', as in '%s', doesn't exist in '%s'"),
+ spec2->named[j].name, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument '%s' doesn't exist in '%s'"),
+ spec1->named[i].name, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_sh =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("{");
+ for (i = 0; i < spec->named_arg_count; i++)
+ {
+ if (i > 0)
+ printf (", ");
+ printf ("'%s'", spec->named[i].name);
+ }
+ printf ("}");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-sh.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-tcl.c b/gettext-tools/src/format-tcl.c
new file mode 100644
index 0000000..e338c6d
--- /dev/null
+++ b/gettext-tools/src/format-tcl.c
@@ -0,0 +1,550 @@
+/* Tcl format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Tcl format strings are described in the tcl8.3.3/doc/format.n manual
+ page and implemented in the function Tcl_FormatObjCmd in
+ tcl8.3.3/generic/tclCmdAH.c.
+ A directive
+ - starts with '%' or '%m$' where m is a positive integer,
+ - is optionally followed by any of the characters '#', '0', '-', ' ', '+',
+ each of which acts as a flag,
+ - is optionally followed by a width specification: '*' (reads an argument)
+ or a nonempty digit sequence,
+ - is optionally followed by '.' and a precision specification: '*' (reads
+ an argument) or a nonempty digit sequence,
+ - is optionally followed by a size specifier, 'h' or 'l'. 'l' is ignored.
+ - is finished by a specifier
+ - '%', that needs no argument,
+ - 'c', that needs a character argument,
+ - 's', that needs a string argument,
+ - 'i', 'd', that need a signed integer argument,
+ - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
+ - 'e', 'E', 'f', 'g', 'G', that need a floating-point argument.
+ Numbered ('%m$') and unnumbered argument specifications cannot be used
+ in the same string.
+ */
+
+enum format_arg_type
+{
+ FAT_NONE,
+ FAT_CHARACTER,
+ FAT_STRING,
+ FAT_INTEGER,
+ FAT_UNSIGNED_INTEGER,
+ FAT_SHORT_INTEGER,
+ FAT_SHORT_UNSIGNED_INTEGER,
+ FAT_FLOAT
+};
+
+struct numbered_arg
+{
+ unsigned int number;
+ enum format_arg_type type;
+};
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int numbered_arg_count;
+ unsigned int allocated;
+ struct numbered_arg *numbered;
+};
+
+/* Locale independent test for a decimal digit.
+ Argument can be 'char' or 'unsigned char'. (Whereas the argument of
+ <ctype.h> isdigit must be an 'unsigned char'.) */
+#undef isdigit
+#define isdigit(c) ((unsigned int) ((c) - '0') < 10)
+
+
+static int
+numbered_arg_compare (const void *p1, const void *p2)
+{
+ unsigned int n1 = ((const struct numbered_arg *) p1)->number;
+ unsigned int n2 = ((const struct numbered_arg *) p2)->number;
+
+ return (n1 > n2 ? 1 : n1 < n2 ? -1 : 0);
+}
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+ bool seen_numbered_arg;
+ bool seen_unnumbered_arg;
+ unsigned int number;
+
+ spec.directives = 0;
+ spec.numbered_arg_count = 0;
+ spec.allocated = 0;
+ spec.numbered = NULL;
+ seen_numbered_arg = false;
+ seen_unnumbered_arg = false;
+ number = 1;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format != '%')
+ {
+ bool is_numbered_arg;
+ bool short_flag;
+ enum format_arg_type type;
+
+ is_numbered_arg = false;
+ if (isdigit (*format))
+ {
+ const char *f = format;
+ unsigned int m = 0;
+
+ do
+ {
+ m = 10 * m + (*f - '0');
+ f++;
+ }
+ while (isdigit (*f));
+
+ if (*f == '$')
+ {
+ if (m == 0)
+ {
+ *invalid_reason = INVALID_ARGNO_0 (spec.directives);
+ FDI_SET (f, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ number = m;
+ format = ++f;
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (seen_unnumbered_arg)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ is_numbered_arg = true;
+ seen_numbered_arg = true;
+ }
+ }
+
+ /* Numbered and unnumbered specifications are exclusive. */
+ if (!is_numbered_arg)
+ {
+ if (seen_numbered_arg)
+ {
+ *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ goto bad_format;
+ }
+ seen_unnumbered_arg = true;
+ }
+
+ /* Parse flags. */
+ while (*format == ' ' || *format == '+' || *format == '-'
+ || *format == '#' || *format == '0')
+ format++;
+
+ /* Parse width. */
+ if (*format == '*')
+ {
+ format++;
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+
+ /* Parse precision. */
+ if (*format == '.')
+ {
+ format++;
+
+ if (*format == '*')
+ {
+ format++;
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+ else if (isdigit (*format))
+ {
+ do format++; while (isdigit (*format));
+ }
+ }
+
+ /* Parse optional size specification. */
+ short_flag = false;
+ if (*format == 'h')
+ short_flag = true, format++;
+ else if (*format == 'l')
+ format++;
+
+ switch (*format)
+ {
+ case 'c':
+ type = FAT_CHARACTER;
+ break;
+ case 's':
+ type = FAT_STRING;
+ break;
+ case 'i': case 'd':
+ type = (short_flag ? FAT_SHORT_INTEGER : FAT_INTEGER);
+ break;
+ case 'u': case 'o': case 'x': case 'X':
+ type = (short_flag ? FAT_SHORT_UNSIGNED_INTEGER : FAT_UNSIGNED_INTEGER);
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ type = FAT_FLOAT;
+ break;
+ default:
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ if (spec.allocated == spec.numbered_arg_count)
+ {
+ spec.allocated = 2 * spec.allocated + 1;
+ spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
+ }
+ spec.numbered[spec.numbered_arg_count].number = number;
+ spec.numbered[spec.numbered_arg_count].type = type;
+ spec.numbered_arg_count++;
+
+ number++;
+ }
+
+ FDI_SET (format, FMTDIR_END);
+
+ format++;
+ }
+
+ /* Sort the numbered argument array, and eliminate duplicates. */
+ if (spec.numbered_arg_count > 1)
+ {
+ unsigned int i, j;
+ bool err;
+
+ qsort (spec.numbered, spec.numbered_arg_count,
+ sizeof (struct numbered_arg), numbered_arg_compare);
+
+ /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i. */
+ err = false;
+ for (i = j = 0; i < spec.numbered_arg_count; i++)
+ if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
+ {
+ enum format_arg_type type1 = spec.numbered[i].type;
+ enum format_arg_type type2 = spec.numbered[j-1].type;
+ enum format_arg_type type_both;
+
+ if (type1 == type2)
+ type_both = type1;
+ else
+ {
+ /* Incompatible types. */
+ type_both = FAT_NONE;
+ if (!err)
+ *invalid_reason =
+ INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
+ err = true;
+ }
+
+ spec.numbered[j-1].type = type_both;
+ }
+ else
+ {
+ if (j < i)
+ {
+ spec.numbered[j].number = spec.numbered[i].number;
+ spec.numbered[j].type = spec.numbered[i].type;
+ }
+ j++;
+ }
+ spec.numbered_arg_count = j;
+ if (err)
+ /* *invalid_reason has already been set above. */
+ goto bad_format;
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ if (spec.numbered != NULL)
+ free (spec.numbered);
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ if (spec->numbered != NULL)
+ free (spec->numbered);
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+
+ if (spec1->numbered_arg_count + spec2->numbered_arg_count > 0)
+ {
+ unsigned int i, j;
+ unsigned int n1 = spec1->numbered_arg_count;
+ unsigned int n2 = spec2->numbered_arg_count;
+
+ /* Check the argument names are the same.
+ Both arrays are sorted. We search for the first difference. */
+ for (i = 0, j = 0; i < n1 || j < n2; )
+ {
+ int cmp = (i >= n1 ? 1 :
+ j >= n2 ? -1 :
+ spec1->numbered[i].number > spec2->numbered[j].number ? 1 :
+ spec1->numbered[i].number < spec2->numbered[j].number ? -1 :
+ 0);
+
+ if (cmp > 0)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ spec2->numbered[j].number, pretty_msgstr,
+ pretty_msgid);
+ err = true;
+ break;
+ }
+ else if (cmp < 0)
+ {
+ if (equality)
+ {
+ if (error_logger)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ spec1->numbered[i].number, pretty_msgstr);
+ err = true;
+ break;
+ }
+ else
+ i++;
+ }
+ else
+ j++, i++;
+ }
+ /* Check the argument types are the same. */
+ if (!err)
+ for (i = 0, j = 0; j < n2; )
+ {
+ if (spec1->numbered[i].number == spec2->numbered[j].number)
+ {
+ if (spec1->numbered[i].type != spec2->numbered[j].type)
+ {
+ if (error_logger)
+ error_logger (_("format specifications in '%s' and '%s' for argument %u are not the same"),
+ pretty_msgid, pretty_msgstr,
+ spec2->numbered[j].number);
+ err = true;
+ break;
+ }
+ j++, i++;
+ }
+ else
+ i++;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_tcl =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int last;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ last = 1;
+ for (i = 0; i < spec->numbered_arg_count; i++)
+ {
+ unsigned int number = spec->numbered[i].number;
+
+ if (i > 0)
+ printf (" ");
+ if (number < last)
+ abort ();
+ for (; last < number; last++)
+ printf ("_ ");
+ switch (spec->numbered[i].type)
+ {
+ case FAT_CHARACTER:
+ printf ("c");
+ break;
+ case FAT_STRING:
+ printf ("s");
+ break;
+ case FAT_INTEGER:
+ printf ("i");
+ break;
+ case FAT_UNSIGNED_INTEGER:
+ printf ("[unsigned]i");
+ break;
+ case FAT_SHORT_INTEGER:
+ printf ("hi");
+ break;
+ case FAT_SHORT_UNSIGNED_INTEGER:
+ printf ("[unsigned]hi");
+ break;
+ case FAT_FLOAT:
+ printf ("f");
+ break;
+ default:
+ abort ();
+ }
+ last = number + 1;
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-tcl.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format-ycp.c b/gettext-tools/src/format-ycp.c
new file mode 100644
index 0000000..762a5d7
--- /dev/null
+++ b/gettext-tools/src/format-ycp.c
@@ -0,0 +1,250 @@
+/* YCP and Smalltalk format strings.
+ Copyright (C) 2001-2004, 2006-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "format.h"
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "format-invalid.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* YCP sformat strings are described in libycp documentation YCP-builtins.html.
+ A directive starts with '%' and is followed by '%' or a nonzero digit ('1'
+ to '9').
+ GNU Smalltalk format strings are described in the CharArray documentation,
+ methods 'bindWith:' and 'bindWithArguments:'. They have the same syntax.
+ */
+
+struct spec
+{
+ unsigned int directives;
+ unsigned int arg_count;
+ bool args_used[9];
+};
+
+
+static void *
+format_parse (const char *format, bool translated, char *fdi,
+ char **invalid_reason)
+{
+ const char *const format_start = format;
+ struct spec spec;
+ struct spec *result;
+
+ spec.directives = 0;
+ spec.arg_count = 0;
+
+ for (; *format != '\0';)
+ if (*format++ == '%')
+ {
+ /* A directive. */
+ FDI_SET (format - 1, FMTDIR_START);
+ spec.directives++;
+
+ if (*format == '%')
+ format++;
+ else if (*format >= '1' && *format <= '9')
+ {
+ unsigned int number = *format - '1';
+
+ while (spec.arg_count <= number)
+ spec.args_used[spec.arg_count++] = false;
+ spec.args_used[number] = true;
+
+ format++;
+ }
+ else
+ {
+ if (*format == '\0')
+ {
+ *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
+ FDI_SET (format - 1, FMTDIR_ERROR);
+ }
+ else
+ {
+ *invalid_reason =
+ (c_isprint (*format)
+ ? xasprintf (_("In the directive number %u, the character '%c' is not a digit between 1 and 9."), spec.directives, *format)
+ : xasprintf (_("The character that terminates the directive number %u is not a digit between 1 and 9."), spec.directives));
+ FDI_SET (format, FMTDIR_ERROR);
+ }
+ goto bad_format;
+ }
+
+ FDI_SET (format - 1, FMTDIR_END);
+ }
+
+ result = XMALLOC (struct spec);
+ *result = spec;
+ return result;
+
+ bad_format:
+ return NULL;
+}
+
+static void
+format_free (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ free (spec);
+}
+
+static int
+format_get_number_of_directives (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+
+ return spec->directives;
+}
+
+static bool
+format_check (void *msgid_descr, void *msgstr_descr, bool equality,
+ formatstring_error_logger_t error_logger,
+ const char *pretty_msgid, const char *pretty_msgstr)
+{
+ struct spec *spec1 = (struct spec *) msgid_descr;
+ struct spec *spec2 = (struct spec *) msgstr_descr;
+ bool err = false;
+ unsigned int i;
+
+ for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++)
+ {
+ bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]);
+ bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]);
+
+ if (equality ? (arg_used1 != arg_used2) : (!arg_used1 && arg_used2))
+ {
+ if (error_logger)
+ {
+ if (arg_used1)
+ error_logger (_("a format specification for argument %u doesn't exist in '%s'"),
+ i + 1, pretty_msgstr);
+ else
+ error_logger (_("a format specification for argument %u, as in '%s', doesn't exist in '%s'"),
+ i + 1, pretty_msgstr, pretty_msgid);
+ }
+ err = true;
+ break;
+ }
+ }
+
+ return err;
+}
+
+
+struct formatstring_parser formatstring_ycp =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+struct formatstring_parser formatstring_smalltalk =
+{
+ format_parse,
+ format_free,
+ format_get_number_of_directives,
+ NULL,
+ format_check
+};
+
+
+#ifdef TEST
+
+/* Test program: Print the argument list specification returned by
+ format_parse for strings read from standard input. */
+
+#include <stdio.h>
+
+static void
+format_print (void *descr)
+{
+ struct spec *spec = (struct spec *) descr;
+ unsigned int i;
+
+ if (spec == NULL)
+ {
+ printf ("INVALID");
+ return;
+ }
+
+ printf ("(");
+ for (i = 0; i < spec->arg_count; i++)
+ {
+ if (i > 0)
+ printf (" ");
+ if (spec->args_used[i])
+ printf ("*");
+ else
+ printf ("_");
+ }
+ printf (")");
+}
+
+int
+main ()
+{
+ for (;;)
+ {
+ char *line = NULL;
+ size_t line_size = 0;
+ int line_len;
+ char *invalid_reason;
+ void *descr;
+
+ line_len = getline (&line, &line_size, stdin);
+ if (line_len < 0)
+ break;
+ if (line_len > 0 && line[line_len - 1] == '\n')
+ line[--line_len] = '\0';
+
+ invalid_reason = NULL;
+ descr = format_parse (line, false, NULL, &invalid_reason);
+
+ format_print (descr);
+ printf ("\n");
+ if (descr == NULL)
+ printf ("%s\n", invalid_reason);
+
+ free (invalid_reason);
+ free (line);
+ }
+
+ return 0;
+}
+
+/*
+ * For Emacs M-x compile
+ * Local Variables:
+ * compile-command: "/bin/sh ../libtool --tag=CC --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../gnulib-lib -I../intl -DHAVE_CONFIG_H -DTEST format-ycp.c ../gnulib-lib/libgettextlib.la"
+ * End:
+ */
+
+#endif /* TEST */
diff --git a/gettext-tools/src/format.c b/gettext-tools/src/format.c
new file mode 100644
index 0000000..c73ad7d
--- /dev/null
+++ b/gettext-tools/src/format.c
@@ -0,0 +1,197 @@
+/* Format strings.
+ Copyright (C) 2001-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "format.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Table of all format string parsers. */
+struct formatstring_parser *formatstring_parsers[NFORMATS] =
+{
+ /* format_c */ &formatstring_c,
+ /* format_objc */ &formatstring_objc,
+ /* format_sh */ &formatstring_sh,
+ /* format_python */ &formatstring_python,
+ /* format_python_brace */ &formatstring_python_brace,
+ /* format_lisp */ &formatstring_lisp,
+ /* format_elisp */ &formatstring_elisp,
+ /* format_librep */ &formatstring_librep,
+ /* format_scheme */ &formatstring_scheme,
+ /* format_smalltalk */ &formatstring_smalltalk,
+ /* format_java */ &formatstring_java,
+ /* format_csharp */ &formatstring_csharp,
+ /* format_awk */ &formatstring_awk,
+ /* format_pascal */ &formatstring_pascal,
+ /* format_ycp */ &formatstring_ycp,
+ /* format_tcl */ &formatstring_tcl,
+ /* format_perl */ &formatstring_perl,
+ /* format_perl_brace */ &formatstring_perl_brace,
+ /* format_php */ &formatstring_php,
+ /* format_gcc_internal */ &formatstring_gcc_internal,
+ /* format_gfc_internal */ &formatstring_gfc_internal,
+ /* format_qt */ &formatstring_qt,
+ /* format_qt_plural */ &formatstring_qt_plural,
+ /* format_kde */ &formatstring_kde,
+ /* format_boost */ &formatstring_boost,
+ /* format_lua */ &formatstring_lua,
+ /* format_javascript */ &formatstring_javascript
+};
+
+/* Check whether both formats strings contain compatible format
+ specifications for format type i (0 <= i < NFORMATS). */
+int
+check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ size_t i,
+ struct argument_range range,
+ const struct plural_distribution *distribution,
+ formatstring_error_logger_t error_logger)
+{
+ int seen_errors = 0;
+
+ /* At runtime, we can assume the program passes arguments that fit well for
+ msgid. We must signal an error if msgstr wants more arguments that msgid
+ accepts.
+ If msgstr wants fewer arguments than msgid, it wouldn't lead to a crash
+ at runtime, but we nevertheless give an error because
+ 1) this situation occurs typically after the programmer has added some
+ arguments to msgid, so we must make the translator specially aware
+ of it (more than just "fuzzy"),
+ 2) it is generally wrong if a translation wants to ignore arguments that
+ are used by other translations. */
+
+ struct formatstring_parser *parser = formatstring_parsers[i];
+ char *invalid_reason = NULL;
+ void *msgid_descr =
+ parser->parse (msgid_plural != NULL ? msgid_plural : msgid, false, NULL,
+ &invalid_reason);
+
+ if (msgid_descr != NULL)
+ {
+ const char *pretty_msgid =
+ (msgid_plural != NULL ? "msgid_plural" : "msgid");
+ char buf[18+1];
+ const char *pretty_msgstr = "msgstr";
+ bool has_plural_translations = (strlen (msgstr) + 1 < msgstr_len);
+ const char *p_end = msgstr + msgstr_len;
+ const char *p;
+ unsigned int j;
+
+ for (p = msgstr, j = 0; p < p_end; p += strlen (p) + 1, j++)
+ {
+ void *msgstr_descr;
+
+ if (msgid_plural != NULL)
+ {
+ sprintf (buf, "msgstr[%u]", j);
+ pretty_msgstr = buf;
+ }
+
+ msgstr_descr = parser->parse (p, true, NULL, &invalid_reason);
+
+ if (msgstr_descr != NULL)
+ {
+ /* Use strict checking (require same number of format
+ directives on both sides) if the message has no plurals,
+ or if msgid_plural exists but on the msgstr[] side
+ there is only msgstr[0], or if distribution->often[j]
+ indicates that the variant applies to infinitely many
+ values of N and the N range is not restricted in a way
+ that the variant applies to only one N.
+ Use relaxed checking when there are at least two
+ msgstr[] forms and the distribution does not give more
+ precise information. */
+ bool strict_checking =
+ (msgid_plural == NULL
+ || !has_plural_translations
+ || (distribution != NULL
+ && distribution->often != NULL
+ && j < distribution->often_length
+ && distribution->often[j]
+ && !(has_range_p (range)
+ && distribution->histogram (distribution,
+ range.min, range.max, j)
+ <= 1)));
+
+ if (parser->check (msgid_descr, msgstr_descr,
+ strict_checking,
+ error_logger, pretty_msgid, pretty_msgstr))
+ seen_errors++;
+
+ parser->free (msgstr_descr);
+ }
+ else
+ {
+ error_logger (_("\
+'%s' is not a valid %s format string, unlike '%s'. Reason: %s"),
+ pretty_msgstr, format_language_pretty[i],
+ pretty_msgid, invalid_reason);
+ seen_errors++;
+ free (invalid_reason);
+ }
+ }
+
+ parser->free (msgid_descr);
+ }
+ else
+ free (invalid_reason);
+
+ return seen_errors;
+}
+
+/* Check whether both formats strings contain compatible format
+ specifications.
+ Return the number of errors that were seen. */
+int
+check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ const enum is_format is_format[NFORMATS],
+ struct argument_range range,
+ const struct plural_distribution *distribution,
+ formatstring_error_logger_t error_logger)
+{
+ int seen_errors = 0;
+ size_t i;
+
+ /* We check only those messages for which the msgid's is_format flag
+ is one of 'yes' or 'possible'. We don't check msgids with is_format
+ 'no' or 'impossible', to obey the programmer's order. We don't check
+ msgids with is_format 'undecided' because that would introduce too
+ many checks, thus forcing the programmer to add "xgettext: no-c-format"
+ anywhere where a translator wishes to use a percent sign. */
+ for (i = 0; i < NFORMATS; i++)
+ if (possible_format_p (is_format[i]))
+ seen_errors += check_msgid_msgstr_format_i (msgid, msgid_plural,
+ msgstr, msgstr_len, i,
+ range,
+ distribution,
+ error_logger);
+
+ return seen_errors;
+}
diff --git a/gettext-tools/src/format.h b/gettext-tools/src/format.h
new file mode 100644
index 0000000..d92532d
--- /dev/null
+++ b/gettext-tools/src/format.h
@@ -0,0 +1,175 @@
+/* Format strings.
+ Copyright (C) 2001-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _FORMAT_H
+#define _FORMAT_H
+
+#include <stdbool.h>
+
+#include "pos.h" /* Get lex_pos_ty. */
+#include "message.h" /* Get NFORMATS. */
+#include "plural-distrib.h" /* Get struct plural_distribution. */
+#include "error.h" /* Get fallback definition of __attribute__. */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* These indicators are set by the parse function at the appropriate
+ positions. */
+enum
+{
+ /* Set on the first byte of a format directive. */
+ FMTDIR_START = 1 << 0,
+ /* Set on the last byte of a format directive. */
+ FMTDIR_END = 1 << 1,
+ /* Set on the last byte of an invalid format directive, where a parse error
+ was recognized. */
+ FMTDIR_ERROR = 1 << 2
+};
+
+/* Macro for use inside a parser:
+ Sets an indicator at the position corresponding to PTR.
+ Assumes local variables 'fdi' and 'format_start' are defined. */
+#define FDI_SET(ptr, flag) \
+ if (fdi != NULL) \
+ fdi[(ptr) - format_start] |= (flag)/*;*/
+
+/* This type of callback is responsible for showing an error. */
+typedef void (*formatstring_error_logger_t) (const char *format, ...)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+ __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+;
+
+/* This structure describes a format string parser for a language. */
+struct formatstring_parser
+{
+ /* Parse the given string as a format string.
+ If translated is true, some extensions available only to msgstr but not
+ to msgid strings are recognized.
+ If fdi is non-NULL, it must be a an array of strlen (string) zero bytes.
+ Return a freshly allocated structure describing
+ 1. the argument types/names needed for the format string,
+ 2. the total number of format directives.
+ Return NULL if the string is not a valid format string. In this case,
+ also set *invalid_reason to an error message explaining why.
+ In both cases, set FMTDIR_* bits at the appropriate positions in fdi. */
+ void * (*parse) (const char *string, bool translated, char *fdi, char **invalid_reason);
+
+ /* Free a format string descriptor, returned by parse(). */
+ void (*free) (void *descr);
+
+ /* Return the number of format directives.
+ A string that can be output literally has 0 format directives. */
+ int (*get_number_of_directives) (void *descr);
+
+ /* Return true if the format string, although valid, contains directives that
+ make it appear unlikely that the string was meant as a format string.
+ A NULL function is equivalent to a function that always returns false. */
+ bool (*is_unlikely_intentional) (void *descr);
+
+ /* Verify that the argument types/names in msgid_descr and those in
+ msgstr_descr are the same (if equality=true), or (if equality=false)
+ that those of msgid_descr extend those of msgstr_descr (i.e.
+ msgstr_descr may omit some of the arguments of msgid_descr).
+ If not, signal an error using error_logger (only if error_logger != NULL)
+ and return true. Otherwise return false. */
+ bool (*check) (void *msgid_descr, void *msgstr_descr, bool equality, formatstring_error_logger_t error_logger, const char *pretty_msgid, const char *pretty_msgstr);
+};
+
+/* Format string parsers, each defined in its own file. */
+extern DLL_VARIABLE struct formatstring_parser formatstring_c;
+extern DLL_VARIABLE struct formatstring_parser formatstring_objc;
+extern DLL_VARIABLE struct formatstring_parser formatstring_sh;
+extern DLL_VARIABLE struct formatstring_parser formatstring_python;
+extern DLL_VARIABLE struct formatstring_parser formatstring_python_brace;
+extern DLL_VARIABLE struct formatstring_parser formatstring_lisp;
+extern DLL_VARIABLE struct formatstring_parser formatstring_elisp;
+extern DLL_VARIABLE struct formatstring_parser formatstring_librep;
+extern DLL_VARIABLE struct formatstring_parser formatstring_scheme;
+extern DLL_VARIABLE struct formatstring_parser formatstring_smalltalk;
+extern DLL_VARIABLE struct formatstring_parser formatstring_java;
+extern DLL_VARIABLE struct formatstring_parser formatstring_csharp;
+extern DLL_VARIABLE struct formatstring_parser formatstring_awk;
+extern DLL_VARIABLE struct formatstring_parser formatstring_pascal;
+extern DLL_VARIABLE struct formatstring_parser formatstring_ycp;
+extern DLL_VARIABLE struct formatstring_parser formatstring_tcl;
+extern DLL_VARIABLE struct formatstring_parser formatstring_perl;
+extern DLL_VARIABLE struct formatstring_parser formatstring_perl_brace;
+extern DLL_VARIABLE struct formatstring_parser formatstring_php;
+extern DLL_VARIABLE struct formatstring_parser formatstring_gcc_internal;
+extern DLL_VARIABLE struct formatstring_parser formatstring_gfc_internal;
+extern DLL_VARIABLE struct formatstring_parser formatstring_qt;
+extern DLL_VARIABLE struct formatstring_parser formatstring_qt_plural;
+extern DLL_VARIABLE struct formatstring_parser formatstring_kde;
+extern DLL_VARIABLE struct formatstring_parser formatstring_boost;
+extern DLL_VARIABLE struct formatstring_parser formatstring_lua;
+extern DLL_VARIABLE struct formatstring_parser formatstring_javascript;
+
+/* Table of all format string parsers. */
+extern DLL_VARIABLE struct formatstring_parser *formatstring_parsers[NFORMATS];
+
+/* Returns an array of the ISO C 99 <inttypes.h> format directives and other
+ format flags or directives with a system dependent expansion contained in
+ the argument string. *intervalsp is assigned to a freshly allocated array
+ of intervals (startpos pointing to '<', endpos to the character after '>'),
+ and *lengthp is assigned to the number of intervals in this array. */
+struct interval
+{
+ size_t startpos;
+ size_t endpos;
+};
+extern void
+ get_sysdep_c_format_directives (const char *string, bool translated,
+ struct interval **intervalsp, size_t *lengthp);
+
+/* Returns the number of unnamed arguments consumed by a Python format
+ string. */
+extern unsigned int get_python_format_unnamed_arg_count (const char *string);
+
+/* Check whether both formats strings contain compatible format
+ specifications for format type i (0 <= i < NFORMATS).
+ Return the number of errors that were seen. */
+extern int
+ check_msgid_msgstr_format_i (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ size_t i,
+ struct argument_range range,
+ const struct plural_distribution *distribution,
+ formatstring_error_logger_t error_logger);
+
+/* Check whether both formats strings contain compatible format
+ specifications.
+ Return the number of errors that were seen. */
+extern int
+ check_msgid_msgstr_format (const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ const enum is_format is_format[NFORMATS],
+ struct argument_range range,
+ const struct plural_distribution *distribution,
+ formatstring_error_logger_t error_logger);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _FORMAT_H */
diff --git a/gettext-tools/src/gnu/gettext/DumpResource.java b/gettext-tools/src/gnu/gettext/DumpResource.java
new file mode 100644
index 0000000..77c6e01
--- /dev/null
+++ b/gettext-tools/src/gnu/gettext/DumpResource.java
@@ -0,0 +1,236 @@
+/* GNU gettext for Java
+ * Copyright (C) 2001-2003, 2007 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 <http://www.gnu.org/licenses/>.
+ */
+
+package gnu.gettext;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * This programs dumps a resource as a PO file. The resource must be
+ * accessible through the CLASSPATH.
+ *
+ * @author Bruno Haible
+ */
+public class DumpResource {
+ private Writer out;
+ private void dumpString (String str) throws IOException {
+ int n = str.length();
+ out.write('"');
+ for (int i = 0; i < n; i++) {
+ char c = str.charAt(i);
+ if (c == 0x0008) {
+ out.write('\\'); out.write('b');
+ } else if (c == 0x000c) {
+ out.write('\\'); out.write('f');
+ } else if (c == 0x000a) {
+ out.write('\\'); out.write('n');
+ } else if (c == 0x000d) {
+ out.write('\\'); out.write('r');
+ } else if (c == 0x0009) {
+ out.write('\\'); out.write('t');
+ } else if (c == '\\' || c == '"') {
+ out.write('\\'); out.write(c);
+ } else
+ out.write(c);
+ }
+ out.write('"');
+ }
+ private void dumpMessage (String msgid, String msgid_plural, Object msgstr) throws IOException {
+ int separatorPos = msgid.indexOf('\u0004');
+ if (separatorPos >= 0) {
+ String msgctxt = msgid.substring(0,separatorPos);
+ msgid = msgid.substring(separatorPos+1);
+ out.write("msgctxt "); dumpString(msgctxt);
+ }
+ out.write("msgid "); dumpString(msgid); out.write('\n');
+ if (msgid_plural != null) {
+ out.write("msgid_plural "); dumpString(msgid_plural); out.write('\n');
+ for (int i = 0; i < ((String[])msgstr).length; i++) {
+ out.write("msgstr[" + i + "] ");
+ dumpString(((String[])msgstr)[i]);
+ out.write('\n');
+ }
+ } else {
+ out.write("msgstr "); dumpString((String)msgstr); out.write('\n');
+ }
+ out.write('\n');
+ }
+ private ResourceBundle catalog;
+ private Method lookupMethod;
+ // Lookup the value corresponding to a key found in catalog.getKeys().
+ // Here we assume that the catalog returns a non-inherited value for
+ // these keys. FIXME: Not true. Better see whether handleGetObject is
+ // public - it is in ListResourceBundle and PropertyResourceBundle.
+ private Object lookup (String key) {
+ Object value = null;
+ if (lookupMethod != null) {
+ try {
+ value = lookupMethod.invoke(catalog, new Object[] { key });
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.getTargetException().printStackTrace();
+ }
+ } else {
+ try {
+ value = catalog.getObject(key);
+ } catch (MissingResourceException e) {
+ }
+ }
+ return value;
+ }
+ private void dump () throws IOException {
+ lookupMethod = null;
+ try {
+ lookupMethod = catalog.getClass().getMethod("lookup", new Class[] { java.lang.String.class });
+ } catch (NoSuchMethodException e) {
+ } catch (SecurityException e) {
+ }
+ Method pluralMethod = null;
+ try {
+ pluralMethod = catalog.getClass().getMethod("get_msgid_plural_table", new Class[0]);
+ } catch (NoSuchMethodException e) {
+ } catch (SecurityException e) {
+ }
+ Field pluralField = null;
+ try {
+ pluralField = catalog.getClass().getField("plural");
+ } catch (NoSuchFieldException e) {
+ } catch (SecurityException e) {
+ }
+ // Search for the header entry.
+ {
+ Object header_entry = null;
+ Enumeration keys = catalog.getKeys();
+ while (keys.hasMoreElements())
+ if ("".equals(keys.nextElement())) {
+ header_entry = lookup("");
+ break;
+ }
+ // If there is no header entry, fake one.
+ // FIXME: This is not needed; right after po_lex_charset_init set
+ // the PO charset to UTF-8.
+ if (header_entry == null)
+ header_entry = "Content-Type: text/plain; charset=UTF-8\n";
+ dumpMessage("",null,header_entry);
+ }
+ // Now the other messages.
+ {
+ Enumeration keys = catalog.getKeys();
+ Object plural = null;
+ if (pluralMethod != null) {
+ // msgfmt versions > 0.13.1 create a static get_msgid_plural_table()
+ // method.
+ try {
+ plural = pluralMethod.invoke(catalog, new Object[0]);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.getTargetException().printStackTrace();
+ }
+ } else if (pluralField != null) {
+ // msgfmt versions <= 0.13.1 create a static plural field.
+ try {
+ plural = pluralField.get(catalog);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ if (plural instanceof String[]) {
+ // A GNU gettext created class with plural handling, Java2 format.
+ int i = 0;
+ while (keys.hasMoreElements()) {
+ String key = (String)keys.nextElement();
+ Object value = lookup(key);
+ String key_plural = (value instanceof String[] ? ((String[])plural)[i++] : null);
+ if (!"".equals(key))
+ dumpMessage(key,key_plural,value);
+ }
+ if (i != ((String[])plural).length)
+ throw new RuntimeException("wrong plural field length");
+ } else if (plural instanceof Hashtable) {
+ // A GNU gettext created class with plural handling, Java format.
+ while (keys.hasMoreElements()) {
+ String key = (String)keys.nextElement();
+ if (!"".equals(key)) {
+ Object value = lookup(key);
+ String key_plural = (value instanceof String[] ? (String)((Hashtable)plural).get(key) : null);
+ dumpMessage(key,key_plural,value);
+ }
+ }
+ } else if (plural == null) {
+ // No plural handling.
+ while (keys.hasMoreElements()) {
+ String key = (String)keys.nextElement();
+ if (!"".equals(key))
+ dumpMessage(key,null,lookup(key));
+ }
+ } else
+ throw new RuntimeException("wrong plural field value");
+ }
+ }
+
+ public DumpResource (String resource_name, String locale_name) {
+ // Split locale_name into language_country_variant.
+ String language;
+ String country;
+ String variant;
+ language = locale_name;
+ {
+ int i = language.indexOf('_');
+ if (i >= 0) {
+ country = language.substring(i+1);
+ language = language.substring(0,i);
+ } else
+ country = "";
+ }
+ {
+ int j = country.indexOf('_');
+ if (j >= 0) {
+ variant = country.substring(j+1);
+ country = country.substring(0,j);
+ } else
+ variant = "";
+ }
+ Locale locale = new Locale(language,country,variant);
+ // Get the resource.
+ ResourceBundle catalog = ResourceBundle.getBundle(resource_name,locale);
+ // We are only interested in the messsages belonging to the locale
+ // itself, not in the inherited messages. But catalog.getLocale() exists
+ // only in Java2 and sometimes differs from the given locale.
+ try {
+ Writer w1 = new OutputStreamWriter(System.out,"UTF8");
+ Writer w2 = new BufferedWriter(w1);
+ this.out = w2;
+ this.catalog = catalog;
+ dump();
+ w2.close();
+ w1.close();
+ System.out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ public static void main (String[] args) {
+ new DumpResource(args[0], args.length > 1 ? args[1] : "");
+ System.exit(0);
+ }
+}
diff --git a/gettext-tools/src/gnu/gettext/GetURL.java b/gettext-tools/src/gnu/gettext/GetURL.java
new file mode 100644
index 0000000..f42ccc4
--- /dev/null
+++ b/gettext-tools/src/gnu/gettext/GetURL.java
@@ -0,0 +1,81 @@
+/* Fetch an URL's contents.
+ * Copyright (C) 2001, 2008 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 <http://www.gnu.org/licenses/>.
+ */
+
+package gnu.gettext;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Fetch an URL's contents and emit it to standard output.
+ * Exit code: 0 = success
+ * 1 = failure
+ * 2 = timeout
+ * @author Bruno Haible
+ */
+public class GetURL {
+ // Use a separate thread to signal a timeout error if the URL cannot
+ // be accessed and completely read within a given amount of time.
+ private static long timeout = 30*1000; // 30 seconds
+ private boolean done;
+ private Thread timeoutThread;
+ public void fetch (String s) {
+ URL url;
+ try {
+ url = new URL(s);
+ } catch (MalformedURLException e) {
+ System.exit(1);
+ return;
+ }
+ done = false;
+ timeoutThread =
+ new Thread() {
+ public void run () {
+ try {
+ sleep(timeout);
+ if (!done) {
+ System.exit(2);
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ };
+ timeoutThread.start();
+ try {
+ InputStream istream = new BufferedInputStream(url.openStream());
+ OutputStream ostream = new BufferedOutputStream(System.out);
+ for (;;) {
+ int b = istream.read();
+ if (b < 0) break;
+ ostream.write(b);
+ }
+ ostream.close();
+ System.out.flush();
+ istream.close();
+ } catch (IOException e) {
+ //e.printStackTrace();
+ System.exit(1);
+ }
+ done = true;
+ }
+ public static void main (String[] args) {
+ if (args.length != 1)
+ System.exit(1);
+ (new GetURL()).fetch(args[0]);
+ System.exit(0);
+ }
+}
diff --git a/gettext-tools/src/hostname.c b/gettext-tools/src/hostname.c
new file mode 100644
index 0000000..a7032b3
--- /dev/null
+++ b/gettext-tools/src/hostname.c
@@ -0,0 +1,389 @@
+/* Display hostname in various forms.
+ Copyright (C) 2001-2003, 2006-2007, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#if defined _WIN32 || defined __WIN32__
+# define WIN32_NATIVE
+#endif
+
+/* Get gethostname(). */
+#include <unistd.h>
+
+#ifdef WIN32_NATIVE
+/* Native Woe32 API lacks gethostname() but has GetComputerName() instead. */
+# include <windows.h>
+#else
+/* Some systems, like early Solaris versions, lack gethostname() but
+ have uname() instead. */
+# if !HAVE_GETHOSTNAME
+# include <sys/utsname.h>
+# endif
+#endif
+
+/* Get MAXHOSTNAMELEN. */
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 64
+#endif
+
+/* Support for using gethostbyname(). */
+#if HAVE_GETHOSTBYNAME
+# include <sys/types.h>
+# include <sys/socket.h> /* defines AF_INET, AF_INET6 */
+# include <netinet/in.h> /* declares ntohs(), defines struct sockaddr_in */
+# if HAVE_ARPA_INET_H
+# include <arpa/inet.h> /* declares inet_ntoa(), inet_ntop() */
+# endif
+# if HAVE_IPV6
+# if !defined(__CYGWIN__) /* Cygwin has only s6_addr, no s6_addr16 */
+# if defined(__APPLE__) && defined(__MACH__) /* MacOS X */
+# define in6_u __u6_addr
+# define u6_addr16 __u6_addr16
+# endif
+ /* Use s6_addr16 for portability. See RFC 2553. */
+# ifndef s6_addr16
+# define s6_addr16 in6_u.u6_addr16
+# endif
+# define HAVE_IN6_S6_ADDR16 1
+# endif
+# endif
+# include <netdb.h> /* defines struct hostent, declares gethostbyname() */
+#endif
+
+/* Include this after <sys/socket.h>, to avoid a syntax error on BeOS. */
+#include <stdbool.h>
+
+#include "closeout.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "xalloc.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Output format. */
+static enum { default_format, short_format, long_format, ip_format } format;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "fqdn", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "ip-address", no_argument, NULL, 'i' },
+ { "long", no_argument, NULL, 'f' },
+ { "short", no_argument, NULL, 's' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void print_hostname (void);
+
+int
+main (int argc, char *argv[])
+{
+ int optchar;
+ bool do_help;
+ bool do_version;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ format = default_format;
+
+ /* Parse command line options. */
+ while ((optchar = getopt_long (argc, argv, "fhisV", long_options, NULL))
+ != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+ case 'f':
+ format = long_format;
+ break;
+ case 's':
+ format = short_format;
+ break;
+ case 'i':
+ format = ip_format;
+ break;
+ case 'h':
+ do_help = true;
+ break;
+ case 'V':
+ do_version = true;
+ break;
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2003, 2006-2007");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test for extraneous arguments. */
+ if (optind != argc)
+ error (EXIT_FAILURE, 0, _("too many arguments"));
+
+ /* Get and print the hostname. */
+ print_hostname ();
+
+ exit (EXIT_SUCCESS);
+}
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION]\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Print the machine's hostname.\n"));
+ printf ("\n");
+ printf (_("\
+Output format:\n"));
+ printf (_("\
+ -s, --short short host name\n"));
+ printf (_("\
+ -f, --fqdn, --long long host name, includes fully qualified domain\n\
+ name, and aliases\n"));
+ printf (_("\
+ -i, --ip-address addresses for the hostname\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+/* Returns an xmalloc()ed string containing the machine's host name. */
+static char *
+xgethostname ()
+{
+#ifdef WIN32_NATIVE
+ char hostname[MAX_COMPUTERNAME_LENGTH+1];
+ DWORD size = sizeof (hostname);
+
+ if (!GetComputerName (hostname, &size))
+ error (EXIT_FAILURE, 0, _("could not get host name"));
+ return xstrdup (hostname);
+#elif HAVE_GETHOSTNAME
+ char hostname[MAXHOSTNAMELEN+1];
+
+ if (gethostname (hostname, MAXHOSTNAMELEN) < 0)
+ error (EXIT_FAILURE, errno, _("could not get host name"));
+ hostname[MAXHOSTNAMELEN] = '\0';
+ return xstrdup (hostname);
+#else
+ struct utsname utsname;
+
+ if (uname (&utsname) < 0)
+ error (EXIT_FAILURE, errno, _("could not get host name"));
+ return xstrdup (utsname.nodename);
+#endif
+}
+
+/* Converts an AF_INET address to a printable, presentable format.
+ BUFFER is an array with at least 15+1 bytes. ADDR is 'struct in_addr'. */
+#if HAVE_INET_NTOP
+# define ipv4_ntop(buffer,addr) \
+ inet_ntop (AF_INET, &addr, buffer, 15+1)
+#else
+# define ipv4_ntop(buffer,addr) \
+ strcpy (buffer, inet_ntoa (addr))
+#endif
+
+#if HAVE_IPV6
+/* Converts an AF_INET6 address to a printable, presentable format.
+ BUFFER is an array with at least 45+1 bytes. ADDR is 'struct in6_addr'. */
+# if HAVE_INET_NTOP
+# define ipv6_ntop(buffer,addr) \
+ inet_ntop (AF_INET6, &addr, buffer, 45+1)
+# elif HAVE_IN6_S6_ADDR16
+# define ipv6_ntop(buffer,addr) \
+ sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
+ ntohs ((addr).s6_addr16[0]), \
+ ntohs ((addr).s6_addr16[1]), \
+ ntohs ((addr).s6_addr16[2]), \
+ ntohs ((addr).s6_addr16[3]), \
+ ntohs ((addr).s6_addr16[4]), \
+ ntohs ((addr).s6_addr16[5]), \
+ ntohs ((addr).s6_addr16[6]), \
+ ntohs ((addr).s6_addr16[7]))
+# else
+# define ipv6_ntop(buffer,addr) \
+ sprintf (buffer, "%x:%x:%x:%x:%x:%x:%x:%x", \
+ ((addr).s6_addr[0] << 8) | (addr).s6_addr[1], \
+ ((addr).s6_addr[2] << 8) | (addr).s6_addr[3], \
+ ((addr).s6_addr[4] << 8) | (addr).s6_addr[5], \
+ ((addr).s6_addr[6] << 8) | (addr).s6_addr[7], \
+ ((addr).s6_addr[8] << 8) | (addr).s6_addr[9], \
+ ((addr).s6_addr[10] << 8) | (addr).s6_addr[11], \
+ ((addr).s6_addr[12] << 8) | (addr).s6_addr[13], \
+ ((addr).s6_addr[14] << 8) | (addr).s6_addr[15])
+# endif
+#endif
+
+/* Print the hostname according to the specified format. */
+static void
+print_hostname ()
+{
+ char *hostname;
+ char *dot;
+#if HAVE_GETHOSTBYNAME
+ struct hostent *h;
+ size_t i;
+#endif
+
+ hostname = xgethostname ();
+
+ switch (format)
+ {
+ case default_format:
+ /* Print the hostname, as returned by the system call. */
+ printf ("%s\n", hostname);
+ break;
+
+ case short_format:
+ /* Print only the part before the first dot. */
+ dot = strchr (hostname, '.');
+ if (dot != NULL)
+ *dot = '\0';
+ printf ("%s\n", hostname);
+ break;
+
+ case long_format:
+ /* Look for netwide usable hostname and aliases using gethostbyname(). */
+#if HAVE_GETHOSTBYNAME
+ h = gethostbyname (hostname);
+ if (h != NULL)
+ {
+ printf ("%s\n", h->h_name);
+ if (h->h_aliases != NULL)
+ for (i = 0; h->h_aliases[i] != NULL; i++)
+ printf ("%s\n", h->h_aliases[i]);
+ }
+ else
+#endif
+ printf ("%s\n", hostname);
+ break;
+
+ case ip_format:
+ /* Look for netwide usable IP addresses using gethostbyname(). */
+#if HAVE_GETHOSTBYNAME
+ h = gethostbyname (hostname);
+ if (h != NULL && h->h_addr_list != NULL)
+ for (i = 0; h->h_addr_list[i] != NULL; i++)
+ {
+#if HAVE_IPV6
+ if (h->h_addrtype == AF_INET6)
+ {
+ char buffer[45+1];
+ ipv6_ntop (buffer, *(const struct in6_addr*) h->h_addr_list[i]);
+ printf("[%s]\n", buffer);
+ }
+ else
+#endif
+ if (h->h_addrtype == AF_INET)
+ {
+ char buffer[15+1];
+ ipv4_ntop (buffer, *(const struct in_addr*) h->h_addr_list[i]);
+ printf("[%s]\n", buffer);
+ }
+ }
+#endif
+ break;
+
+ default:
+ abort ();
+ }
+}
diff --git a/gettext-tools/src/lang-table.c b/gettext-tools/src/lang-table.c
new file mode 100644
index 0000000..9cc1f55
--- /dev/null
+++ b/gettext-tools/src/lang-table.c
@@ -0,0 +1,315 @@
+/* Table of languages.
+ Copyright (C) 2001-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "lang-table.h"
+
+/* Derived from ISO 639. */
+struct language_table_entry language_table[] =
+ {
+ { "aa", "Afar" },
+ { "ab", "Abkhazian" },
+ { "ace", "Achinese" },
+ { "ae", "Avestan" },
+ { "af", "Afrikaans" },
+ { "ak", "Akan" },
+ { "am", "Amharic" },
+ { "an", "Aragonese" },
+ { "ang", "Old English" },
+ { "ar", "Arabic" },
+ { "arn", "Mapudungun" },
+ { "as", "Assamese" },
+ { "ast", "Asturian" },
+ { "av", "Avaric" },
+ { "awa", "Awadhi" },
+ { "ay", "Aymara" },
+ { "az", "Azerbaijani" },
+ { "ba", "Bashkir" },
+ { "bal", "Baluchi" },
+ { "ban", "Balinese" },
+ { "be", "Belarusian" },
+ { "bej", "Beja" },
+ { "bem", "Bemba" },
+ { "bg", "Bulgarian" },
+ { "bh", "Bihari" },
+ { "bho", "Bhojpuri" },
+ { "bi", "Bislama" },
+ { "bik", "Bikol" },
+ { "bin", "Bini" },
+ { "bm", "Bambara" },
+ { "bn", "Bengali" },
+ { "bo", "Tibetan" },
+ { "br", "Breton" },
+ { "bs", "Bosnian" },
+ { "bug", "Buginese" },
+ { "ca", "Catalan" },
+ { "ce", "Chechen" },
+ { "ceb", "Cebuano" },
+ { "ch", "Chamorro" },
+ { "co", "Corsican" },
+ { "cr", "Cree" },
+ { "crh", "Crimean Tatar" },
+ { "cs", "Czech" },
+ { "csb", "Kashubian" },
+ { "cu", "Church Slavic" },
+ { "cv", "Chuvash" },
+ { "cy", "Welsh" },
+ { "da", "Danish" },
+ { "de", "German" },
+ { "din", "Dinka" },
+ { "doi", "Dogri" },
+ { "dsb", "Lower Sorbian" },
+ { "dv", "Divehi" },
+ { "dz", "Dzongkha" },
+ { "ee", "Ewe" },
+ { "el", "Greek" },
+ { "en", "English" },
+ { "eo", "Esperanto" },
+ { "es", "Spanish" },
+ { "et", "Estonian" },
+ { "eu", "Basque" },
+ { "fa", "Persian" },
+ { "ff", "Fulah" },
+ { "fi", "Finnish" },
+ { "fil", "Filipino" },
+ { "fj", "Fijian" },
+ { "fo", "Faroese" },
+ { "fon", "Fon" },
+ { "fr", "French" },
+ { "fur", "Friulian" },
+ { "fy", "Western Frisian" },
+ { "ga", "Irish" },
+ { "gd", "Scottish Gaelic" },
+ { "gl", "Galician" },
+ { "gn", "Guarani" },
+ { "gon", "Gondi" },
+ { "gsw", "Swiss German" }, /* can also be "Alsatian" */
+ { "gu", "Gujarati" },
+ { "gv", "Manx" },
+ { "ha", "Hausa" },
+ { "he", "Hebrew" },
+ { "hi", "Hindi" },
+ { "hil", "Hiligaynon" },
+ { "hmn", "Hmong" },
+ { "ho", "Hiri Motu" },
+ { "hr", "Croatian" },
+ { "hsb", "Upper Sorbian" },
+ { "ht", "Haitian" },
+ { "hu", "Hungarian" },
+ { "hy", "Armenian" },
+ { "hz", "Herero" },
+ { "ia", "Interlingua" },
+ { "id", "Indonesian" },
+ { "ie", "Interlingue" },
+ { "ig", "Igbo" },
+ { "ii", "Sichuan Yi" },
+ { "ik", "Inupiak" },
+ { "ilo", "Iloko" },
+ { "is", "Icelandic" },
+ { "it", "Italian" },
+ { "iu", "Inuktitut" },
+ { "ja", "Japanese" },
+ { "jab", "Hyam" },
+ { "jv", "Javanese" },
+ { "ka", "Georgian" },
+ { "kab", "Kabyle" },
+ { "kaj", "Jju" },
+ { "kam", "Kamba" },
+ { "kbd", "Kabardian" },
+ { "kcg", "Tyap" },
+ { "kdm", "Kagoma" },
+ { "kg", "Kongo" },
+ { "ki", "Kikuyu" },
+ { "kj", "Kuanyama" },
+ { "kk", "Kazakh" },
+ { "kl", "Kalaallisut" },
+ { "km", "Central Khmer" },
+ { "kmb", "Kimbundu" },
+ { "kn", "Kannada" },
+ { "ko", "Korean" },
+ { "kr", "Kanuri" },
+ { "kru", "Kurukh" },
+ { "ks", "Kashmiri" },
+ { "ku", "Kurdish" },
+ { "kv", "Komi" },
+ { "kw", "Cornish" },
+ { "ky", "Kirghiz" },
+ { "kok", "Konkani" },
+ { "la", "Latin" },
+ { "lb", "Letzeburgesch" },
+ { "lg", "Ganda" },
+ { "li", "Limburgish" },
+ { "ln", "Lingala" },
+ { "lo", "Laotian" },
+ { "lt", "Lithuanian" },
+ { "lu", "Luba-Katanga" },
+ { "lua", "Luba-Lulua" },
+ { "luo", "Luo" },
+ { "lv", "Latvian" },
+ { "mad", "Madurese" },
+ { "mag", "Magahi" },
+ { "mai", "Maithili" },
+ { "mak", "Makasar" },
+ { "man", "Mandingo" },
+ { "men", "Mende" },
+ { "mg", "Malagasy" },
+ { "mh", "Marshallese" },
+ { "mi", "Maori" },
+ { "min", "Minangkabau" },
+ { "mk", "Macedonian" },
+ { "ml", "Malayalam" },
+ { "mn", "Mongolian" },
+ { "mni", "Manipuri" },
+ { "mo", "Moldavian" },
+ { "moh", "Mohawk" },
+ { "mos", "Mossi" },
+ { "mr", "Marathi" },
+ { "ms", "Malay" },
+ { "mt", "Maltese" },
+ { "mwr", "Marwari" },
+ { "my", "Burmese" },
+ { "myn", "Mayan" },
+ { "na", "Nauru" },
+ { "nap", "Neapolitan" },
+ { "nah", "Nahuatl" },
+ { "nb", "Norwegian Bokmal" },
+ { "nd", "North Ndebele" },
+ { "nds", "Low Saxon" },
+ { "ne", "Nepali" },
+ { "ng", "Ndonga" },
+ { "nl", "Dutch" },
+ { "nn", "Norwegian Nynorsk" },
+ { "no", "Norwegian" },
+ { "nr", "South Ndebele" },
+ { "nso", "Northern Sotho" },
+ { "nv", "Navajo" },
+ { "ny", "Nyanja" },
+ { "nym", "Nyamwezi" },
+ { "nyn", "Nyankole" },
+ { "oc", "Occitan" },
+ { "oj", "Ojibwa" },
+ { "om", "(Afan) Oromo" },
+ { "or", "Oriya" },
+ { "os", "Ossetian" },
+ { "pa", "Punjabi" },
+ { "pag", "Pangasinan" },
+ { "pam", "Pampanga" },
+ { "pap", "Papiamento" },
+ { "pbb", "Páez" },
+ { "pi", "Pali" },
+ { "pl", "Polish" },
+ { "ps", "Pashto" },
+ { "pt", "Portuguese" },
+ { "qu", "Quechua" },
+ { "raj", "Rajasthani" },
+ { "rm", "Romansh" },
+ { "rn", "Kirundi" },
+ { "ro", "Romanian" },
+ { "ru", "Russian" },
+ { "rw", "Kinyarwanda" },
+ { "sa", "Sanskrit" },
+ { "sah", "Yakut" },
+ { "sas", "Sasak" },
+ { "sat", "Santali" },
+ { "sc", "Sardinian" },
+ { "scn", "Sicilian" },
+ { "sd", "Sindhi" },
+ { "se", "Northern Sami" },
+ { "sg", "Sango" },
+ { "shn", "Shan" },
+ { "si", "Sinhala" },
+ { "sid", "Sidamo" },
+ { "sk", "Slovak" },
+ { "sl", "Slovenian" },
+ { "sm", "Samoan" },
+ { "sma", "Southern Sami" },
+ { "smj", "Lule Sami" },
+ { "smn", "Inari Sami" },
+ { "sms", "Skolt Sami" },
+ { "sn", "Shona" },
+ { "so", "Somali" },
+ { "sq", "Albanian" },
+ { "sr", "Serbian" },
+ { "srr", "Serer" },
+ { "ss", "Siswati" },
+ { "st", "Sesotho" },
+ { "su", "Sundanese" },
+ { "suk", "Sukuma" },
+ { "sus", "Susu" },
+ { "sv", "Swedish" },
+ { "sw", "Swahili" },
+ { "ta", "Tamil" },
+ { "te", "Telugu" },
+ { "tem", "Timne" },
+ { "tet", "Tetum" },
+ { "tg", "Tajik" },
+ { "th", "Thai" },
+ { "ti", "Tigrinya" },
+ { "tiv", "Tiv" },
+ { "tk", "Turkmen" },
+ { "tl", "Tagalog" },
+ { "tn", "Setswana" },
+ { "to", "Tonga" },
+ { "tr", "Turkish" },
+ { "ts", "Tsonga" },
+ { "tt", "Tatar" },
+ { "tum", "Tumbuka" },
+ { "tw", "Twi" },
+ { "ty", "Tahitian" },
+ { "ug", "Uighur" },
+ { "uk", "Ukrainian" },
+ { "umb", "Umbundu" },
+ { "ur", "Urdu" },
+ { "uz", "Uzbek" },
+ { "ve", "Venda" },
+ { "vi", "Vietnamese" },
+ { "vo", "Volapuk" },
+ { "wal", "Walamo" },
+ { "war", "Waray" },
+ { "wen", "Sorbian" },
+ { "wo", "Wolof" },
+ { "xh", "Xhosa" },
+ { "yao", "Yao" },
+ { "yi", "Yiddish" },
+ { "yo", "Yoruba" },
+ { "za", "Zhuang" },
+ { "zh", "Chinese" },
+ { "zu", "Zulu" },
+ { "zap", "Zapotec" }
+ };
+const size_t language_table_size = sizeof (language_table) / sizeof (language_table[0]);
+
+/* The language names for variants of languages, according to the catalog name
+ (usually built from the language code and territory code).
+ Should be consistent with the list of languages found on the TP site, see
+ the URL contained in gettext-tools/projects/TP/teams.url. */
+struct language_table_entry language_variant_table[] =
+ {
+ { "de_AT", "Austrian" },
+ { "en_GB", "English (British)" },
+ { "es_AR", "Argentinian" },
+ { "es_IC", "Spanish (Canary Islands)" },
+ { "pt_BR", "Brazilian Portuguese" },
+ { "zh_CN", "Chinese (simplified)" },
+ { "zh_HK", "Chinese (Hong Kong)" },
+ { "zh_TW", "Chinese (traditional)" }
+ };
+const size_t language_variant_table_size = sizeof (language_variant_table) / sizeof (language_variant_table[0]);
diff --git a/gettext-tools/src/lang-table.h b/gettext-tools/src/lang-table.h
new file mode 100644
index 0000000..e72e4c4
--- /dev/null
+++ b/gettext-tools/src/lang-table.h
@@ -0,0 +1,35 @@
+/* Table of languages.
+ Copyright (C) 2001-2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _LANG_TABLE_H
+#define _LANG_TABLE_H
+
+#include <stddef.h>
+
+struct language_table_entry
+{
+ const char *code;
+ const char *english;
+};
+
+extern struct language_table_entry language_table[];
+extern const size_t language_table_size;
+
+extern struct language_table_entry language_variant_table[];
+extern const size_t language_variant_table_size;
+
+#endif /* _LANG_TABLE_H */
diff --git a/gettext-tools/src/libexpat-compat.c b/gettext-tools/src/libexpat-compat.c
new file mode 100644
index 0000000..ad680db
--- /dev/null
+++ b/gettext-tools/src/libexpat-compat.c
@@ -0,0 +1,326 @@
+/* xgettext libexpat compatibility.
+ Copyright (C) 2002-2003, 2005-2009, 2013 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#if DYNLOAD_LIBEXPAT
+# include <dlfcn.h>
+#else
+# if HAVE_LIBEXPAT
+# include <expat.h>
+# endif
+#endif
+
+/* Keep the references to XML_GetCurrent{Line,Column}Number symbols
+ before loading libexpat-compat.h, since they are redefined to
+ rpl_XML_GetCurrent{Line,Column}Number . */
+#if !DYNLOAD_LIBEXPAT && XML_MAJOR_VERSION >= 2
+static void *p_XML_GetCurrentLineNumber = (void *) &XML_GetCurrentLineNumber;
+static void *p_XML_GetCurrentColumnNumber = (void *) &XML_GetCurrentColumnNumber;
+#endif
+
+#include "libexpat-compat.h"
+
+/* ======================= Different libexpat ABIs. ======================= */
+
+/* There are three different ABIs of libexpat, regarding the functions
+ XML_GetCurrentLineNumber and XML_GetCurrentColumnNumber.
+ In expat < 2.0, they return an 'int'.
+ In expat >= 2.0, they return
+ - a 'long' if expat was compiled with the default flags, or
+ - a 'long long' if expat was compiled with -DXML_LARGE_SIZE.
+ But the <expat.h> include file does not contain the information whether
+ expat was compiled with -DXML_LARGE_SIZE; so the include file is lying!
+ For this information, we need to call XML_GetFeatureList(), for
+ expat >= 2.0.1; for expat = 2.0.0, we have to assume the default flags. */
+
+#if !DYNLOAD_LIBEXPAT && XML_MAJOR_VERSION >= 2
+
+/* expat >= 2.0 -> Return type is 'int64_t' worst-case. */
+
+/* Return true if libexpat was compiled with -DXML_LARGE_SIZE. */
+static bool
+is_XML_LARGE_SIZE_ABI (void)
+{
+ static bool tested;
+ static bool is_large;
+
+ if (!tested)
+ {
+ const XML_Feature *features;
+
+ is_large = false;
+ for (features = XML_GetFeatureList (); features->name != NULL; features++)
+ if (strcmp (features->name, "XML_LARGE_SIZE") == 0)
+ {
+ is_large = true;
+ break;
+ }
+
+ tested = true;
+ }
+ return is_large;
+}
+
+int64_t
+rpl_XML_GetCurrentLineNumber (XML_Parser parser)
+{
+ if (is_XML_LARGE_SIZE_ABI ())
+ return ((int64_t (*) (XML_Parser)) p_XML_GetCurrentLineNumber) (parser);
+ else
+ return ((long (*) (XML_Parser)) p_XML_GetCurrentLineNumber) (parser);
+}
+
+int64_t
+rpl_XML_GetCurrentColumnNumber (XML_Parser parser)
+{
+ if (is_XML_LARGE_SIZE_ABI ())
+ return ((int64_t (*) (XML_Parser)) p_XML_GetCurrentColumnNumber) (parser);
+ else
+ return ((long (*) (XML_Parser)) p_XML_GetCurrentColumnNumber) (parser);
+}
+#endif
+
+
+/* ===================== Dynamic loading of libexpat. ===================== */
+
+#if DYNLOAD_LIBEXPAT
+
+static XML_Expat_Version (*p_XML_ExpatVersionInfo) (void);
+
+XML_Expat_Version
+XML_ExpatVersionInfo (void)
+{
+ return (*p_XML_ExpatVersionInfo) ();
+}
+
+static const XML_Feature * (*p_XML_GetFeatureList) (void);
+
+const XML_Feature *
+XML_GetFeatureList (void)
+{
+ return (*p_XML_GetFeatureList) ();
+}
+
+enum XML_Size_ABI
+get_XML_Size_ABI (void)
+{
+ static bool tested;
+ static enum XML_Size_ABI abi;
+
+ if (!tested)
+ {
+ if (XML_ExpatVersionInfo () .major >= 2)
+ /* expat >= 2.0 -> XML_Size is 'int64_t' or 'long'. */
+ {
+ const XML_Feature *features;
+
+ abi = is_long;
+ for (features = XML_GetFeatureList ();
+ features->name != NULL;
+ features++)
+ if (strcmp (features->name, "XML_LARGE_SIZE") == 0)
+ {
+ abi = is_int64_t;
+ break;
+ }
+ }
+ else
+ /* expat < 2.0 -> XML_Size is 'int'. */
+ abi = is_int;
+ tested = true;
+ }
+ return abi;
+}
+
+static XML_Parser (*p_XML_ParserCreate) (const XML_Char *encoding);
+
+XML_Parser
+XML_ParserCreate (const XML_Char *encoding)
+{
+ return (*p_XML_ParserCreate) (encoding);
+}
+
+static void (*p_XML_SetElementHandler) (XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+
+void
+XML_SetElementHandler (XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end)
+{
+ (*p_XML_SetElementHandler) (parser, start, end);
+}
+
+
+static void (*p_XML_SetCharacterDataHandler) (XML_Parser parser,
+ XML_CharacterDataHandler handler);
+
+void
+XML_SetCharacterDataHandler (XML_Parser parser,
+ XML_CharacterDataHandler handler)
+{
+ (*p_XML_SetCharacterDataHandler) (parser, handler);
+}
+
+
+static void (*p_XML_SetCommentHandler) (XML_Parser parser,
+ XML_CommentHandler handler);
+
+void
+XML_SetCommentHandler (XML_Parser parser, XML_CommentHandler handler)
+{
+ (*p_XML_SetCommentHandler) (parser, handler);
+}
+
+
+static int (*p_XML_Parse) (XML_Parser parser, const char *s,
+ int len, int isFinal);
+
+int
+XML_Parse (XML_Parser parser, const char *s, int len, int isFinal)
+{
+ return (*p_XML_Parse) (parser, s, len, isFinal);
+}
+
+
+static enum XML_Error (*p_XML_GetErrorCode) (XML_Parser parser);
+
+enum XML_Error
+XML_GetErrorCode (XML_Parser parser)
+{
+ return (*p_XML_GetErrorCode) (parser);
+}
+
+
+static void *p_XML_GetCurrentLineNumber;
+
+int64_t
+XML_GetCurrentLineNumber (XML_Parser parser)
+{
+ switch (get_XML_Size_ABI ())
+ {
+ case is_int:
+ return ((int (*) (XML_Parser)) p_XML_GetCurrentLineNumber) (parser);
+ case is_long:
+ return ((long (*) (XML_Parser)) p_XML_GetCurrentLineNumber) (parser);
+ case is_int64_t:
+ return ((int64_t (*) (XML_Parser)) p_XML_GetCurrentLineNumber) (parser);
+ default:
+ abort ();
+ }
+}
+
+static void *p_XML_GetCurrentColumnNumber;
+
+int64_t
+XML_GetCurrentColumnNumber (XML_Parser parser)
+{
+ switch (get_XML_Size_ABI ())
+ {
+ case is_int:
+ return ((int (*) (XML_Parser)) p_XML_GetCurrentColumnNumber) (parser);
+ case is_long:
+ return ((long (*) (XML_Parser)) p_XML_GetCurrentColumnNumber) (parser);
+ case is_int64_t:
+ return ((int64_t (*) (XML_Parser)) p_XML_GetCurrentColumnNumber) (parser);
+ default:
+ abort ();
+ }
+}
+
+
+static const XML_LChar * (*p_XML_ErrorString) (int code);
+
+const XML_LChar *
+XML_ErrorString (int code)
+{
+ return (*p_XML_ErrorString) (code);
+}
+
+static void (*p_XML_ParserFree) (XML_Parser parser);
+
+void
+XML_ParserFree (XML_Parser parser)
+{
+ return (*p_XML_ParserFree) (parser);
+}
+
+static int libexpat_loaded = 0;
+
+bool
+load_libexpat ()
+{
+ if (libexpat_loaded == 0)
+ {
+ void *handle;
+
+ /* Try to load libexpat-2.x. */
+ handle = dlopen ("libexpat.so.1", RTLD_LAZY);
+ if (handle == NULL)
+ /* Try to load libexpat-1.x. */
+ handle = dlopen ("libexpat.so.0", RTLD_LAZY);
+ if (handle != NULL
+ && (p_XML_ExpatVersionInfo =
+ (XML_Expat_Version (*) (void))
+ dlsym (handle, "XML_ExpatVersionInfo")) != NULL
+ && (p_XML_GetFeatureList =
+ (const XML_Feature * (*) (void))
+ dlsym (handle, "XML_GetFeatureList")) != NULL
+ && (p_XML_ParserCreate =
+ (XML_Parser (*) (const XML_Char *))
+ dlsym (handle, "XML_ParserCreate")) != NULL
+ && (p_XML_SetElementHandler =
+ (void (*) (XML_Parser, XML_StartElementHandler, XML_EndElementHandler))
+ dlsym (handle, "XML_SetElementHandler")) != NULL
+ && (p_XML_SetCharacterDataHandler =
+ (void (*) (XML_Parser, XML_CharacterDataHandler))
+ dlsym (handle, "XML_SetCharacterDataHandler")) != NULL
+ && (p_XML_SetCommentHandler =
+ (void (*) (XML_Parser, XML_CommentHandler))
+ dlsym (handle, "XML_SetCommentHandler")) != NULL
+ && (p_XML_Parse =
+ (int (*) (XML_Parser, const char *, int, int))
+ dlsym (handle, "XML_Parse")) != NULL
+ && (p_XML_GetErrorCode =
+ (enum XML_Error (*) (XML_Parser))
+ dlsym (handle, "XML_GetErrorCode")) != NULL
+ && (p_XML_GetCurrentLineNumber =
+ dlsym (handle, "XML_GetCurrentLineNumber")) != NULL
+ && (p_XML_GetCurrentColumnNumber =
+ dlsym (handle, "XML_GetCurrentColumnNumber")) != NULL
+ && (p_XML_ParserFree =
+ (void (*) (XML_Parser))
+ dlsym (handle, "XML_ParserFree")) != NULL
+ && (p_XML_ErrorString =
+ (const XML_LChar * (*) (int))
+ dlsym (handle, "XML_ErrorString")) != NULL)
+ libexpat_loaded = 1;
+ else
+ libexpat_loaded = -1;
+ }
+ return libexpat_loaded >= 0;
+}
+
+#endif
diff --git a/gettext-tools/src/libexpat-compat.h b/gettext-tools/src/libexpat-compat.h
new file mode 100644
index 0000000..2ff6465
--- /dev/null
+++ b/gettext-tools/src/libexpat-compat.h
@@ -0,0 +1,94 @@
+/* xgettext libexpat compatibility.
+ Copyright (C) 2002-2003, 2005-2009, 2013 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+#include <stdint.h>
+#if DYNLOAD_LIBEXPAT
+# include <dlfcn.h>
+#else
+# if HAVE_LIBEXPAT
+# include <expat.h>
+# endif
+#endif
+
+#if !DYNLOAD_LIBEXPAT && XML_MAJOR_VERSION >= 2
+int64_t rpl_XML_GetCurrentLineNumber (XML_Parser parser);
+# undef XML_GetCurrentLineNumber
+# define XML_GetCurrentLineNumber rpl_XML_GetCurrentLineNumber
+
+int64_t rpl_XML_GetCurrentColumnNumber (XML_Parser parser);
+# undef XML_GetCurrentColumnNumber
+# define XML_GetCurrentColumnNumber rpl_XML_GetCurrentColumnNumber
+#endif
+
+/* ===================== Dynamic loading of libexpat. ===================== */
+
+#if DYNLOAD_LIBEXPAT
+typedef struct
+ {
+ int major;
+ int minor;
+ int micro;
+ }
+ XML_Expat_Version;
+enum XML_FeatureEnum { XML_FEATURE_END = 0 };
+typedef struct
+ {
+ enum XML_FeatureEnum feature;
+ const char *name;
+ long int value;
+ }
+ XML_Feature;
+typedef void *XML_Parser;
+typedef char XML_Char;
+typedef char XML_LChar;
+enum XML_Error { XML_ERROR_NONE };
+typedef void (*XML_StartElementHandler) (void *userData, const XML_Char *name, const XML_Char **atts);
+typedef void (*XML_EndElementHandler) (void *userData, const XML_Char *name);
+typedef void (*XML_CharacterDataHandler) (void *userData, const XML_Char *s, int len);
+typedef void (*XML_CommentHandler) (void *userData, const XML_Char *data);
+
+XML_Expat_Version XML_ExpatVersionInfo (void);
+const XML_Feature * XML_GetFeatureList (void);
+
+enum XML_Size_ABI { is_int, is_long, is_int64_t };
+enum XML_Size_ABI get_XML_Size_ABI (void);
+
+XML_Parser XML_ParserCreate (const XML_Char *encoding);
+void XML_SetElementHandler (XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+void XML_SetCharacterDataHandler (XML_Parser parser,
+ XML_CharacterDataHandler handler);
+void XML_SetCommentHandler (XML_Parser parser, XML_CommentHandler handler);
+int XML_Parse (XML_Parser parser, const char *s, int len, int isFinal);
+enum XML_Error XML_GetErrorCode (XML_Parser parser);
+int64_t XML_GetCurrentLineNumber (XML_Parser parser);
+int64_t XML_GetCurrentColumnNumber (XML_Parser parser);
+const XML_LChar * XML_ErrorString (int code);
+void XML_ParserFree (XML_Parser parser);
+
+bool load_libexpat ();
+
+#define LIBEXPAT_AVAILABLE() (load_libexpat ())
+
+#elif HAVE_LIBEXPAT
+
+#define LIBEXPAT_AVAILABLE() true
+
+#endif
diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c
new file mode 100644
index 0000000..586675f
--- /dev/null
+++ b/gettext-tools/src/message.c
@@ -0,0 +1,914 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "message.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "fstrcmp.h"
+#include "hash.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+
+
+const char *const format_language[NFORMATS] =
+{
+ /* format_c */ "c",
+ /* format_objc */ "objc",
+ /* format_sh */ "sh",
+ /* format_python */ "python",
+ /* format_python_brace */ "python-brace",
+ /* format_lisp */ "lisp",
+ /* format_elisp */ "elisp",
+ /* format_librep */ "librep",
+ /* format_scheme */ "scheme",
+ /* format_smalltalk */ "smalltalk",
+ /* format_java */ "java",
+ /* format_csharp */ "csharp",
+ /* format_awk */ "awk",
+ /* format_pascal */ "object-pascal",
+ /* format_ycp */ "ycp",
+ /* format_tcl */ "tcl",
+ /* format_perl */ "perl",
+ /* format_perl_brace */ "perl-brace",
+ /* format_php */ "php",
+ /* format_gcc_internal */ "gcc-internal",
+ /* format_gfc_internal */ "gfc-internal",
+ /* format_qt */ "qt",
+ /* format_qt_plursl */ "qt-plural",
+ /* format_kde */ "kde",
+ /* format_boost */ "boost",
+ /* format_lua */ "lua",
+ /* format_javascript */ "javascript"
+};
+
+const char *const format_language_pretty[NFORMATS] =
+{
+ /* format_c */ "C",
+ /* format_objc */ "Objective C",
+ /* format_sh */ "Shell",
+ /* format_python */ "Python",
+ /* format_python_brace */ "Python brace",
+ /* format_lisp */ "Lisp",
+ /* format_elisp */ "Emacs Lisp",
+ /* format_librep */ "librep",
+ /* format_scheme */ "Scheme",
+ /* format_smalltalk */ "Smalltalk",
+ /* format_java */ "Java",
+ /* format_csharp */ "C#",
+ /* format_awk */ "awk",
+ /* format_pascal */ "Object Pascal",
+ /* format_ycp */ "YCP",
+ /* format_tcl */ "Tcl",
+ /* format_perl */ "Perl",
+ /* format_perl_brace */ "Perl brace",
+ /* format_php */ "PHP",
+ /* format_gcc_internal */ "GCC internal",
+ /* format_gfc_internal */ "GFC internal",
+ /* format_qt */ "Qt",
+ /* format_qt_plural */ "Qt plural",
+ /* format_kde */ "KDE",
+ /* format_boost */ "Boost",
+ /* format_lua */ "Lua",
+ /* format_javascript */ "JavaScript"
+};
+
+
+bool
+possible_format_p (enum is_format is_format)
+{
+ return is_format == possible
+ || is_format == yes_according_to_context
+ || is_format == yes;
+}
+
+
+message_ty *
+message_alloc (const char *msgctxt,
+ const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ const lex_pos_ty *pp)
+{
+ message_ty *mp;
+ size_t i;
+
+ mp = XMALLOC (message_ty);
+ mp->msgctxt = msgctxt;
+ mp->msgid = msgid;
+ mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
+ mp->msgstr = msgstr;
+ mp->msgstr_len = msgstr_len;
+ mp->pos = *pp;
+ mp->comment = NULL;
+ mp->comment_dot = NULL;
+ mp->filepos_count = 0;
+ mp->filepos = NULL;
+ mp->is_fuzzy = false;
+ for (i = 0; i < NFORMATS; i++)
+ mp->is_format[i] = undecided;
+ mp->range.min = -1;
+ mp->range.max = -1;
+ mp->do_wrap = undecided;
+ mp->prev_msgctxt = NULL;
+ mp->prev_msgid = NULL;
+ mp->prev_msgid_plural = NULL;
+ mp->used = 0;
+ mp->obsolete = false;
+ return mp;
+}
+
+
+void
+message_free (message_ty *mp)
+{
+ size_t j;
+
+ free ((char *) mp->msgid);
+ if (mp->msgid_plural != NULL)
+ free ((char *) mp->msgid_plural);
+ free ((char *) mp->msgstr);
+ if (mp->comment != NULL)
+ string_list_free (mp->comment);
+ if (mp->comment_dot != NULL)
+ string_list_free (mp->comment_dot);
+ for (j = 0; j < mp->filepos_count; ++j)
+ free ((char *) mp->filepos[j].file_name);
+ if (mp->filepos != NULL)
+ free (mp->filepos);
+ if (mp->prev_msgctxt != NULL)
+ free ((char *) mp->prev_msgctxt);
+ if (mp->prev_msgid != NULL)
+ free ((char *) mp->prev_msgid);
+ if (mp->prev_msgid_plural != NULL)
+ free ((char *) mp->prev_msgid_plural);
+ free (mp);
+}
+
+
+void
+message_comment_append (message_ty *mp, const char *s)
+{
+ if (mp->comment == NULL)
+ mp->comment = string_list_alloc ();
+ string_list_append (mp->comment, s);
+}
+
+
+void
+message_comment_dot_append (message_ty *mp, const char *s)
+{
+ if (mp->comment_dot == NULL)
+ mp->comment_dot = string_list_alloc ();
+ string_list_append (mp->comment_dot, s);
+}
+
+
+void
+message_comment_filepos (message_ty *mp, const char *name, size_t line)
+{
+ size_t j;
+ size_t nbytes;
+ lex_pos_ty *pp;
+
+ /* See if we have this position already. */
+ for (j = 0; j < mp->filepos_count; j++)
+ {
+ pp = &mp->filepos[j];
+ if (strcmp (pp->file_name, name) == 0 && pp->line_number == line)
+ return;
+ }
+
+ /* Extend the list so that we can add a position to it. */
+ nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
+ mp->filepos = xrealloc (mp->filepos, nbytes);
+
+ /* Insert the position at the end. Don't sort the file positions here. */
+ pp = &mp->filepos[mp->filepos_count++];
+ pp->file_name = xstrdup (name);
+ pp->line_number = line;
+}
+
+
+message_ty *
+message_copy (message_ty *mp)
+{
+ message_ty *result;
+ size_t j, i;
+
+ result = message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL,
+ xstrdup (mp->msgid), mp->msgid_plural,
+ mp->msgstr, mp->msgstr_len, &mp->pos);
+
+ if (mp->comment)
+ {
+ for (j = 0; j < mp->comment->nitems; ++j)
+ message_comment_append (result, mp->comment->item[j]);
+ }
+ if (mp->comment_dot)
+ {
+ for (j = 0; j < mp->comment_dot->nitems; ++j)
+ message_comment_dot_append (result, mp->comment_dot->item[j]);
+ }
+ result->is_fuzzy = mp->is_fuzzy;
+ for (i = 0; i < NFORMATS; i++)
+ result->is_format[i] = mp->is_format[i];
+ result->range = mp->range;
+ result->do_wrap = mp->do_wrap;
+ for (j = 0; j < mp->filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &mp->filepos[j];
+ message_comment_filepos (result, pp->file_name, pp->line_number);
+ }
+ result->prev_msgctxt =
+ (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL);
+ result->prev_msgid =
+ (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL);
+ result->prev_msgid_plural =
+ (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL);
+ return result;
+}
+
+
+message_list_ty *
+message_list_alloc (bool use_hashtable)
+{
+ message_list_ty *mlp;
+
+ mlp = XMALLOC (message_list_ty);
+ mlp->nitems = 0;
+ mlp->nitems_max = 0;
+ mlp->item = NULL;
+ if ((mlp->use_hashtable = use_hashtable))
+ hash_init (&mlp->htable, 10);
+ return mlp;
+}
+
+
+void
+message_list_free (message_list_ty *mlp, int keep_messages)
+{
+ size_t j;
+
+ if (keep_messages == 0)
+ for (j = 0; j < mlp->nitems; ++j)
+ message_free (mlp->item[j]);
+ if (mlp->item)
+ free (mlp->item);
+ if (mlp->use_hashtable)
+ hash_destroy (&mlp->htable);
+ free (mlp);
+}
+
+
+static int
+message_list_hash_insert_entry (hash_table *htable, message_ty *mp)
+{
+ char *alloced_key;
+ const char *key;
+ size_t keylen;
+ int found;
+
+ if (mp->msgctxt != NULL)
+ {
+ /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */
+ size_t msgctxt_len = strlen (mp->msgctxt);
+ size_t msgid_len = strlen (mp->msgid);
+ keylen = msgctxt_len + 1 + msgid_len + 1;
+ alloced_key = (char *) xmalloca (keylen);
+ memcpy (alloced_key, mp->msgctxt, msgctxt_len);
+ alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
+ memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1);
+ key = alloced_key;
+ }
+ else
+ {
+ alloced_key = NULL;
+ key = mp->msgid;
+ keylen = strlen (mp->msgid) + 1;
+ }
+
+ found = (hash_insert_entry (htable, key, keylen, mp) == NULL);
+
+ if (mp->msgctxt != NULL)
+ freea (alloced_key);
+
+ return found;
+}
+
+
+void
+message_list_append (message_list_ty *mlp, message_ty *mp)
+{
+ if (mlp->nitems >= mlp->nitems_max)
+ {
+ size_t nbytes;
+
+ mlp->nitems_max = mlp->nitems_max * 2 + 4;
+ nbytes = mlp->nitems_max * sizeof (message_ty *);
+ mlp->item = xrealloc (mlp->item, nbytes);
+ }
+ mlp->item[mlp->nitems++] = mp;
+
+ if (mlp->use_hashtable)
+ if (message_list_hash_insert_entry (&mlp->htable, mp))
+ /* A message list has duplicates, although it was allocated with the
+ assertion that it wouldn't have duplicates. It is a bug. */
+ abort ();
+}
+
+
+void
+message_list_prepend (message_list_ty *mlp, message_ty *mp)
+{
+ size_t j;
+
+ if (mlp->nitems >= mlp->nitems_max)
+ {
+ size_t nbytes;
+
+ mlp->nitems_max = mlp->nitems_max * 2 + 4;
+ nbytes = mlp->nitems_max * sizeof (message_ty *);
+ mlp->item = xrealloc (mlp->item, nbytes);
+ }
+ for (j = mlp->nitems; j > 0; j--)
+ mlp->item[j] = mlp->item[j - 1];
+ mlp->item[0] = mp;
+ mlp->nitems++;
+
+ if (mlp->use_hashtable)
+ if (message_list_hash_insert_entry (&mlp->htable, mp))
+ /* A message list has duplicates, although it was allocated with the
+ assertion that it wouldn't have duplicates. It is a bug. */
+ abort ();
+}
+
+
+void
+message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
+{
+ size_t j;
+
+ if (mlp->nitems >= mlp->nitems_max)
+ {
+ size_t nbytes;
+
+ mlp->nitems_max = mlp->nitems_max * 2 + 4;
+ nbytes = mlp->nitems_max * sizeof (message_ty *);
+ mlp->item = xrealloc (mlp->item, nbytes);
+ }
+ for (j = mlp->nitems; j > n; j--)
+ mlp->item[j] = mlp->item[j - 1];
+ mlp->item[j] = mp;
+ mlp->nitems++;
+
+ if (mlp->use_hashtable)
+ if (message_list_hash_insert_entry (&mlp->htable, mp))
+ /* A message list has duplicates, although it was allocated with the
+ assertion that it wouldn't have duplicates. It is a bug. */
+ abort ();
+}
+
+
+#if 0 /* unused */
+void
+message_list_delete_nth (message_list_ty *mlp, size_t n)
+{
+ size_t j;
+
+ if (n >= mlp->nitems)
+ return;
+ message_free (mlp->item[n]);
+ for (j = n + 1; j < mlp->nitems; ++j)
+ mlp->item[j - 1] = mlp->item[j];
+ mlp->nitems--;
+
+ if (mlp->use_hashtable)
+ {
+ /* Our simple-minded hash tables don't support removal. */
+ hash_destroy (&mlp->htable);
+ mlp->use_hashtable = false;
+ }
+}
+#endif
+
+
+void
+message_list_remove_if_not (message_list_ty *mlp,
+ message_predicate_ty *predicate)
+{
+ size_t i, j;
+
+ for (j = 0, i = 0; j < mlp->nitems; j++)
+ if (predicate (mlp->item[j]))
+ mlp->item[i++] = mlp->item[j];
+ if (mlp->use_hashtable && i < mlp->nitems)
+ {
+ /* Our simple-minded hash tables don't support removal. */
+ hash_destroy (&mlp->htable);
+ mlp->use_hashtable = false;
+ }
+ mlp->nitems = i;
+}
+
+
+bool
+message_list_msgids_changed (message_list_ty *mlp)
+{
+ if (mlp->use_hashtable)
+ {
+ unsigned long int size = mlp->htable.size;
+ size_t j;
+
+ hash_destroy (&mlp->htable);
+ hash_init (&mlp->htable, size);
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (message_list_hash_insert_entry (&mlp->htable, mp))
+ /* A message list has duplicates, although it was allocated with
+ the assertion that it wouldn't have duplicates, and before the
+ msgids changed it indeed didn't have duplicates. */
+ {
+ hash_destroy (&mlp->htable);
+ mlp->use_hashtable = false;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+message_list_ty *
+message_list_copy (message_list_ty *mlp, int copy_level)
+{
+ message_list_ty *result;
+ size_t j;
+
+ result = message_list_alloc (mlp->use_hashtable);
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ message_list_append (result, copy_level ? mp : message_copy (mp));
+ }
+
+ return result;
+}
+
+
+message_ty *
+message_list_search (message_list_ty *mlp,
+ const char *msgctxt, const char *msgid)
+{
+ if (mlp->use_hashtable)
+ {
+ char *alloced_key;
+ const char *key;
+ size_t keylen;
+
+ if (msgctxt != NULL)
+ {
+ /* Concatenate the msgctxt and msgid, to form the hash table key. */
+ size_t msgctxt_len = strlen (msgctxt);
+ size_t msgid_len = strlen (msgid);
+ keylen = msgctxt_len + 1 + msgid_len + 1;
+ alloced_key = (char *) xmalloca (keylen);
+ memcpy (alloced_key, msgctxt, msgctxt_len);
+ alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
+ memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1);
+ key = alloced_key;
+ }
+ else
+ {
+ alloced_key = NULL;
+ key = msgid;
+ keylen = strlen (msgid) + 1;
+ }
+
+ {
+ void *htable_value;
+ int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value);
+
+ if (msgctxt != NULL)
+ freea (alloced_key);
+
+ if (found)
+ return (message_ty *) htable_value;
+ else
+ return NULL;
+ }
+ }
+ else
+ {
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ message_ty *mp;
+
+ mp = mlp->item[j];
+ if ((msgctxt != NULL
+ ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0
+ : mp->msgctxt == NULL)
+ && strcmp (msgid, mp->msgid) == 0)
+ return mp;
+ }
+ return NULL;
+ }
+}
+
+
+double
+fuzzy_search_goal_function (const message_ty *mp,
+ const char *msgctxt, const char *msgid,
+ double lower_bound)
+{
+ double bonus = 0.0;
+ /* A translation for a context is a good proposal also for another. But
+ give mp a small advantage if mp is valid regardless of any context or
+ has the same context as the one being looked up. */
+ if (mp->msgctxt == NULL
+ || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0))
+ {
+ bonus = 0.00001;
+ /* Since we will consider (weight + bonus) at the end, we are only
+ interested in weights that are >= lower_bound - bonus. Subtract
+ a little more than the bonus, in order to avoid trouble due to
+ rounding errors. */
+ lower_bound -= bonus * 1.01;
+ }
+
+ {
+ /* The use of 'volatile' guarantees that excess precision bits are dropped
+ before the addition and before the following comparison at the caller's
+ site. It is necessary on x86 systems where double-floats are not IEEE
+ compliant by default, to avoid that msgmerge results become platform and
+ compiler option dependent. 'volatile' is a portable alternative to
+ gcc's -ffloat-store option. */
+ volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound);
+
+ weight += bonus;
+
+ return weight;
+ }
+}
+
+
+static message_ty *
+message_list_search_fuzzy_inner (message_list_ty *mlp,
+ const char *msgctxt, const char *msgid,
+ double *best_weight_p)
+{
+ size_t j;
+ message_ty *best_mp;
+
+ best_mp = NULL;
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ message_ty *mp;
+
+ mp = mlp->item[j];
+
+ if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
+ {
+ double weight =
+ fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p);
+ if (weight > *best_weight_p)
+ {
+ *best_weight_p = weight;
+ best_mp = mp;
+ }
+ }
+ }
+ return best_mp;
+}
+
+
+message_ty *
+message_list_search_fuzzy (message_list_ty *mlp,
+ const char *msgctxt, const char *msgid)
+{
+ double best_weight;
+
+ best_weight = FUZZY_THRESHOLD;
+ return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
+}
+
+
+message_list_list_ty *
+message_list_list_alloc ()
+{
+ message_list_list_ty *mllp;
+
+ mllp = XMALLOC (message_list_list_ty);
+ mllp->nitems = 0;
+ mllp->nitems_max = 0;
+ mllp->item = NULL;
+ return mllp;
+}
+
+
+void
+message_list_list_free (message_list_list_ty *mllp, int keep_level)
+{
+ size_t j;
+
+ if (keep_level < 2)
+ for (j = 0; j < mllp->nitems; ++j)
+ message_list_free (mllp->item[j], keep_level);
+ if (mllp->item)
+ free (mllp->item);
+ free (mllp);
+}
+
+
+void
+message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp)
+{
+ if (mllp->nitems >= mllp->nitems_max)
+ {
+ size_t nbytes;
+
+ mllp->nitems_max = mllp->nitems_max * 2 + 4;
+ nbytes = mllp->nitems_max * sizeof (message_list_ty *);
+ mllp->item = xrealloc (mllp->item, nbytes);
+ }
+ mllp->item[mllp->nitems++] = mlp;
+}
+
+
+void
+message_list_list_append_list (message_list_list_ty *mllp,
+ message_list_list_ty *mllp2)
+{
+ size_t j;
+
+ for (j = 0; j < mllp2->nitems; ++j)
+ message_list_list_append (mllp, mllp2->item[j]);
+}
+
+
+message_ty *
+message_list_list_search (message_list_list_ty *mllp,
+ const char *msgctxt, const char *msgid)
+{
+ message_ty *best_mp;
+ int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */
+ size_t j;
+
+ best_mp = NULL;
+ best_weight = 0;
+ for (j = 0; j < mllp->nitems; ++j)
+ {
+ message_list_ty *mlp;
+ message_ty *mp;
+
+ mlp = mllp->item[j];
+ mp = message_list_search (mlp, msgctxt, msgid);
+ if (mp)
+ {
+ int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2);
+ if (weight > best_weight)
+ {
+ best_mp = mp;
+ best_weight = weight;
+ }
+ }
+ }
+ return best_mp;
+}
+
+
+#if 0 /* unused */
+message_ty *
+message_list_list_search_fuzzy (message_list_list_ty *mllp,
+ const char *msgctxt, const char *msgid)
+{
+ size_t j;
+ double best_weight;
+ message_ty *best_mp;
+
+ best_weight = FUZZY_THRESHOLD;
+ best_mp = NULL;
+ for (j = 0; j < mllp->nitems; ++j)
+ {
+ message_list_ty *mlp;
+ message_ty *mp;
+
+ mlp = mllp->item[j];
+ mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
+ if (mp)
+ best_mp = mp;
+ }
+ return best_mp;
+}
+#endif
+
+
+msgdomain_ty*
+msgdomain_alloc (const char *domain, bool use_hashtable)
+{
+ msgdomain_ty *mdp;
+
+ mdp = XMALLOC (msgdomain_ty);
+ mdp->domain = domain;
+ mdp->messages = message_list_alloc (use_hashtable);
+ return mdp;
+}
+
+
+void
+msgdomain_free (msgdomain_ty *mdp)
+{
+ message_list_free (mdp->messages, 0);
+ free (mdp);
+}
+
+
+msgdomain_list_ty *
+msgdomain_list_alloc (bool use_hashtable)
+{
+ msgdomain_list_ty *mdlp;
+
+ mdlp = XMALLOC (msgdomain_list_ty);
+ /* Put the default domain first, so that when we output it,
+ we can omit the 'domain' directive. */
+ mdlp->nitems = 1;
+ mdlp->nitems_max = 1;
+ mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *);
+ mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable);
+ mdlp->use_hashtable = use_hashtable;
+ mdlp->encoding = NULL;
+ return mdlp;
+}
+
+
+void
+msgdomain_list_free (msgdomain_list_ty *mdlp)
+{
+ size_t j;
+
+ for (j = 0; j < mdlp->nitems; ++j)
+ msgdomain_free (mdlp->item[j]);
+ if (mdlp->item)
+ free (mdlp->item);
+ free (mdlp);
+}
+
+
+void
+msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp)
+{
+ if (mdlp->nitems >= mdlp->nitems_max)
+ {
+ size_t nbytes;
+
+ mdlp->nitems_max = mdlp->nitems_max * 2 + 4;
+ nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *);
+ mdlp->item = xrealloc (mdlp->item, nbytes);
+ }
+ mdlp->item[mdlp->nitems++] = mdp;
+}
+
+
+#if 0 /* unused */
+void
+msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2)
+{
+ size_t j;
+
+ for (j = 0; j < mdlp2->nitems; ++j)
+ msgdomain_list_append (mdlp, mdlp2->item[j]);
+}
+#endif
+
+
+message_list_ty *
+msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
+ bool create)
+{
+ size_t j;
+
+ for (j = 0; j < mdlp->nitems; j++)
+ if (strcmp (mdlp->item[j]->domain, domain) == 0)
+ return mdlp->item[j]->messages;
+
+ if (create)
+ {
+ msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable);
+ msgdomain_list_append (mdlp, mdp);
+ return mdp->messages;
+ }
+ else
+ return NULL;
+}
+
+
+/* Copy a message domain list.
+ If copy_level = 0, also copy the messages. If copy_level = 1, share the
+ messages but copy the domains. If copy_level = 2, share the domains. */
+msgdomain_list_ty *
+msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level)
+{
+ msgdomain_list_ty *result;
+ size_t j;
+
+ result = XMALLOC (msgdomain_list_ty);
+ result->nitems = 0;
+ result->nitems_max = 0;
+ result->item = NULL;
+ result->use_hashtable = mdlp->use_hashtable;
+ result->encoding = mdlp->encoding;
+
+ for (j = 0; j < mdlp->nitems; j++)
+ {
+ msgdomain_ty *mdp = mdlp->item[j];
+
+ if (copy_level < 2)
+ {
+ msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty);
+
+ result_mdp->domain = mdp->domain;
+ result_mdp->messages = message_list_copy (mdp->messages, copy_level);
+
+ msgdomain_list_append (result, result_mdp);
+ }
+ else
+ msgdomain_list_append (result, mdp);
+ }
+
+ return result;
+}
+
+
+#if 0 /* unused */
+message_ty *
+msgdomain_list_search (msgdomain_list_ty *mdlp,
+ const char *msgctxt, const char *msgid)
+{
+ size_t j;
+
+ for (j = 0; j < mdlp->nitems; ++j)
+ {
+ msgdomain_ty *mdp;
+ message_ty *mp;
+
+ mdp = mdlp->item[j];
+ mp = message_list_search (mdp->messages, msgctxt, msgid);
+ if (mp)
+ return mp;
+ }
+ return NULL;
+}
+#endif
+
+
+#if 0 /* unused */
+message_ty *
+msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
+ const char *msgctxt, const char *msgid)
+{
+ size_t j;
+ double best_weight;
+ message_ty *best_mp;
+
+ best_weight = FUZZY_THRESHOLD;
+ best_mp = NULL;
+ for (j = 0; j < mdlp->nitems; ++j)
+ {
+ msgdomain_ty *mdp;
+ message_ty *mp;
+
+ mdp = mdlp->item[j];
+ mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid,
+ &best_weight);
+ if (mp)
+ best_mp = mp;
+ }
+ return best_mp;
+}
+#endif
diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h
new file mode 100644
index 0000000..bf2215a
--- /dev/null
+++ b/gettext-tools/src/message.h
@@ -0,0 +1,370 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MESSAGE_H
+#define _MESSAGE_H
+
+#include "str-list.h"
+#include "pos.h"
+#include "hash.h"
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* According to Sun's Uniforum proposal the default message domain is
+ named 'messages'. */
+#define MESSAGE_DOMAIN_DEFAULT "messages"
+
+
+/* Separator between msgctxt and msgid in .mo files. */
+#define MSGCTXT_SEPARATOR '\004' /* EOT */
+
+
+/* Kinds of format strings. */
+enum format_type
+{
+ format_c,
+ format_objc,
+ format_sh,
+ format_python,
+ format_python_brace,
+ format_lisp,
+ format_elisp,
+ format_librep,
+ format_scheme,
+ format_smalltalk,
+ format_java,
+ format_csharp,
+ format_awk,
+ format_pascal,
+ format_ycp,
+ format_tcl,
+ format_perl,
+ format_perl_brace,
+ format_php,
+ format_gcc_internal,
+ format_gfc_internal,
+ format_qt,
+ format_qt_plural,
+ format_kde,
+ format_boost,
+ format_lua,
+ format_javascript
+};
+#define NFORMATS 27 /* Number of format_type enum values. */
+extern DLL_VARIABLE const char *const format_language[NFORMATS];
+extern DLL_VARIABLE const char *const format_language_pretty[NFORMATS];
+
+/* Is current msgid a format string? */
+enum is_format
+{
+ undecided,
+ yes,
+ no,
+ yes_according_to_context,
+ possible,
+ impossible
+};
+
+extern bool
+ possible_format_p (enum is_format);
+
+
+/* Range of an unsigned integer argument. */
+struct argument_range
+{
+ int min;
+ int max;
+};
+
+/* Tests whether a range is present. */
+#define has_range_p(range) ((range).min >= 0 && (range).max >= 0)
+
+
+/* Is current msgid wrappable? */
+#if 0
+enum is_wrap
+{
+ undecided,
+ yes,
+ no
+};
+#else /* HACK - C's enum concept is so stupid */
+#define is_wrap is_format
+#endif
+
+
+struct altstr
+{
+ const char *msgstr;
+ size_t msgstr_len;
+ const char *msgstr_end;
+ string_list_ty *comment;
+ string_list_ty *comment_dot;
+ char *id;
+};
+
+
+typedef struct message_ty message_ty;
+struct message_ty
+{
+ /* The msgctxt string, if present. */
+ const char *msgctxt;
+
+ /* The msgid string. */
+ const char *msgid;
+
+ /* The msgid's plural, if present. */
+ const char *msgid_plural;
+
+ /* The msgstr strings. */
+ const char *msgstr;
+ /* The number of bytes in msgstr, including the terminating NUL. */
+ size_t msgstr_len;
+
+ /* Position in the source PO file. */
+ lex_pos_ty pos;
+
+ /* Plain comments (#) appearing before the message. */
+ string_list_ty *comment;
+
+ /* Extracted comments (#.) appearing before the message. */
+ string_list_ty *comment_dot;
+
+ /* File position comments (#:) appearing before the message, one for
+ each unique file position instance, sorted by file name and then
+ by line. */
+ size_t filepos_count;
+ lex_pos_ty *filepos;
+
+ /* Informations from special comments (#,).
+ Some of them come from extracted comments. They are manipulated by
+ the tools, e.g. msgmerge. */
+
+ /* Fuzzy means "needs translator review". */
+ bool is_fuzzy;
+
+ /* Designation of format string syntax requirements for specific
+ programming languages. */
+ enum is_format is_format[NFORMATS];
+
+ /* Lower and upper bound for the argument whose format directive can be
+ omitted in specific cases of singular or plural. */
+ struct argument_range range;
+
+ /* Do we want the string to be wrapped in the emitted PO file? */
+ enum is_wrap do_wrap;
+
+ /* The prev_msgctxt, prev_msgid and prev_msgid_plural strings appearing
+ before the message, if present. Generated by msgmerge. */
+ const char *prev_msgctxt;
+ const char *prev_msgid;
+ const char *prev_msgid_plural;
+
+ /* If set the message is obsolete and while writing out it should be
+ commented out. */
+ bool obsolete;
+
+ /* Used for checking that messages have been used, in the msgcmp,
+ msgmerge, msgcomm and msgcat programs. */
+ int used;
+
+ /* Used for looking up the target message, in the msgcat program. */
+ message_ty *tmp;
+
+ /* Used for combining alternative translations, in the msgcat program. */
+ int alternative_count;
+ struct altstr *alternative;
+};
+
+extern message_ty *
+ message_alloc (const char *msgctxt,
+ const char *msgid, const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ const lex_pos_ty *pp);
+#define is_header(mp) ((mp)->msgctxt == NULL && (mp)->msgid[0] == '\0')
+extern void
+ message_free (message_ty *mp);
+extern void
+ message_comment_append (message_ty *mp, const char *comment);
+extern void
+ message_comment_dot_append (message_ty *mp, const char *comment);
+extern void
+ message_comment_filepos (message_ty *mp, const char *name, size_t line);
+extern message_ty *
+ message_copy (message_ty *mp);
+
+
+typedef struct message_list_ty message_list_ty;
+struct message_list_ty
+{
+ message_ty **item;
+ size_t nitems;
+ size_t nitems_max;
+ bool use_hashtable;
+ hash_table htable; /* Table mapping msgid to 'message_ty *'. */
+};
+
+/* Create a fresh message list.
+ If USE_HASHTABLE is true, a hash table will be used to speed up
+ message_list_search(). USE_HASHTABLE can only be set to true if it is
+ known that the message list will not contain duplicate msgids. */
+extern message_list_ty *
+ message_list_alloc (bool use_hashtable);
+/* Free a message list.
+ If keep_messages = 0, also free the messages. If keep_messages = 1, don't
+ free the messages. */
+extern void
+ message_list_free (message_list_ty *mlp, int keep_messages);
+extern void
+ message_list_append (message_list_ty *mlp, message_ty *mp);
+extern void
+ message_list_prepend (message_list_ty *mlp, message_ty *mp);
+extern void
+ message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp);
+extern void
+ message_list_delete_nth (message_list_ty *mlp, size_t n);
+typedef bool message_predicate_ty (const message_ty *mp);
+extern void
+ message_list_remove_if_not (message_list_ty *mlp,
+ message_predicate_ty *predicate);
+/* Recompute the hash table of a message list after the msgids or msgctxts
+ changed. */
+extern bool
+ message_list_msgids_changed (message_list_ty *mlp);
+/* Copy a message list.
+ If copy_level = 0, also copy the messages. If copy_level = 1, share the
+ messages. */
+extern message_list_ty *
+ message_list_copy (message_list_ty *mlp, int copy_level);
+extern message_ty *
+ message_list_search (message_list_ty *mlp,
+ const char *msgctxt, const char *msgid);
+/* Return the message in MLP which maximizes the fuzzy_search_goal_function.
+ Only messages with a fuzzy_search_goal_function > FUZZY_THRESHOLD are
+ considered. In case of several messages with the same goal function value,
+ the one with the smaller index is returned. */
+extern message_ty *
+ message_list_search_fuzzy (message_list_ty *mlp,
+ const char *msgctxt, const char *msgid);
+
+
+typedef struct message_list_list_ty message_list_list_ty;
+struct message_list_list_ty
+{
+ message_list_ty **item;
+ size_t nitems;
+ size_t nitems_max;
+};
+
+extern message_list_list_ty *
+ message_list_list_alloc (void);
+/* Free a list of message lists.
+ If keep_level = 0, also free the messages. If keep_level = 1, don't free
+ the messages but free the lists. If keep_level = 2, don't free the
+ the messages and the lists. */
+extern void
+ message_list_list_free (message_list_list_ty *mllp, int keep_level);
+extern void
+ message_list_list_append (message_list_list_ty *mllp,
+ message_list_ty *mlp);
+extern void
+ message_list_list_append_list (message_list_list_ty *mllp,
+ message_list_list_ty *mllp2);
+extern message_ty *
+ message_list_list_search (message_list_list_ty *mllp,
+ const char *msgctxt, const char *msgid);
+extern message_ty *
+ message_list_list_search_fuzzy (message_list_list_ty *mllp,
+ const char *msgctxt, const char *msgid);
+
+
+typedef struct msgdomain_ty msgdomain_ty;
+struct msgdomain_ty
+{
+ const char *domain;
+ message_list_ty *messages;
+};
+
+extern msgdomain_ty *
+ msgdomain_alloc (const char *domain, bool use_hashtable);
+extern void
+ msgdomain_free (msgdomain_ty *mdp);
+
+
+typedef struct msgdomain_list_ty msgdomain_list_ty;
+struct msgdomain_list_ty
+{
+ msgdomain_ty **item;
+ size_t nitems;
+ size_t nitems_max;
+ bool use_hashtable;
+ const char *encoding; /* canonicalized encoding or NULL if unknown */
+};
+
+extern msgdomain_list_ty *
+ msgdomain_list_alloc (bool use_hashtable);
+extern void
+ msgdomain_list_free (msgdomain_list_ty *mdlp);
+extern void
+ msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp);
+extern void
+ msgdomain_list_append_list (msgdomain_list_ty *mdlp,
+ msgdomain_list_ty *mdlp2);
+extern message_list_ty *
+ msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
+ bool create);
+/* Copy a message domain list.
+ If copy_level = 0, also copy the messages. If copy_level = 1, share the
+ messages but copy the domains. If copy_level = 2, share the domains. */
+extern msgdomain_list_ty *
+ msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level);
+extern message_ty *
+ msgdomain_list_search (msgdomain_list_ty *mdlp,
+ const char *msgctxt, const char *msgid);
+extern message_ty *
+ msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
+ const char *msgctxt, const char *msgid);
+
+
+/* The goal function used in fuzzy search.
+ Higher values indicate a closer match.
+ If the result is < LOWER_BOUND, an arbitrary other value < LOWER_BOUND can
+ be returned. */
+extern double
+ fuzzy_search_goal_function (const message_ty *mp,
+ const char *msgctxt, const char *msgid,
+ double lower_bound);
+
+/* The threshold for fuzzy-searching.
+ A message is considered only if
+ fuzzy_search_goal_function (mp, given, 0.0) > FUZZY_THRESHOLD. */
+#define FUZZY_THRESHOLD 0.6
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* message.h */
diff --git a/gettext-tools/src/msgattrib.c b/gettext-tools/src/msgattrib.c
new file mode 100644
index 0000000..326f28c
--- /dev/null
+++ b/gettext-tools/src/msgattrib.c
@@ -0,0 +1,683 @@
+/* Manipulates attributes of messages in translation catalogs.
+ Copyright (C) 2001-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "propername.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Bit mask of subsets to remove. */
+enum
+{
+ REMOVE_UNTRANSLATED = 1 << 0,
+ REMOVE_TRANSLATED = 1 << 1,
+ REMOVE_FUZZY = 1 << 2,
+ REMOVE_NONFUZZY = 1 << 3,
+ REMOVE_OBSOLETE = 1 << 4,
+ REMOVE_NONOBSOLETE = 1 << 5
+};
+static int to_remove;
+
+/* Bit mask of actions to perform on all messages. */
+enum
+{
+ SET_FUZZY = 1 << 0,
+ RESET_FUZZY = 1 << 1,
+ SET_OBSOLETE = 1 << 2,
+ RESET_OBSOLETE = 1 << 3,
+ REMOVE_PREV = 1 << 4,
+ ADD_PREV = 1 << 5,
+ REMOVE_TRANSLATION = 1 << 6
+};
+static int to_change;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "clear-fuzzy", no_argument, NULL, CHAR_MAX + 8 },
+ { "clear-obsolete", no_argument, NULL, CHAR_MAX + 10 },
+ { "clear-previous", no_argument, NULL, CHAR_MAX + 18 },
+ { "empty", no_argument, NULL, CHAR_MAX + 23 },
+ { "color", optional_argument, NULL, CHAR_MAX + 19 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "fuzzy", no_argument, NULL, CHAR_MAX + 11 },
+ { "help", no_argument, NULL, 'h' },
+ { "ignore-file", required_argument, NULL, CHAR_MAX + 15 },
+ { "indent", no_argument, NULL, 'i' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-fuzzy", no_argument, NULL, CHAR_MAX + 3 },
+ { "no-location", no_argument, NULL, CHAR_MAX + 22 },
+ { "no-obsolete", no_argument, NULL, CHAR_MAX + 5 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 13 },
+ { "obsolete", no_argument, NULL, CHAR_MAX + 12 },
+ { "only-file", required_argument, NULL, CHAR_MAX + 14 },
+ { "only-fuzzy", no_argument, NULL, CHAR_MAX + 4 },
+ { "only-obsolete", no_argument, NULL, CHAR_MAX + 6 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "previous", no_argument, NULL, CHAR_MAX + 21 },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "set-fuzzy", no_argument, NULL, CHAR_MAX + 7 },
+ { "set-obsolete", no_argument, NULL, CHAR_MAX + 9 },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 16 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 17 },
+ { "strict", no_argument, NULL, 'S' },
+ { "style", required_argument, NULL, CHAR_MAX + 20 },
+ { "translated", no_argument, NULL, CHAR_MAX + 1 },
+ { "untranslated", no_argument, NULL, CHAR_MAX + 2 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static msgdomain_list_ty *process_msgdomain_list (msgdomain_list_ty *mdlp,
+ msgdomain_list_ty *only_mdlp,
+ msgdomain_list_ty *ignore_mdlp);
+
+
+int
+main (int argc, char **argv)
+{
+ int optchar;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ const char *only_file;
+ const char *ignore_file;
+ msgdomain_list_ty *only_mdlp;
+ msgdomain_list_ty *ignore_mdlp;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_msgid = false;
+ bool sort_by_filepos = false;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+ only_file = NULL;
+ ignore_file = NULL;
+
+ while ((optchar = getopt_long (argc, argv, "D:eEFhino:pPsVw:", long_options,
+ NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1: /* --translated */
+ to_remove |= REMOVE_UNTRANSLATED;
+ break;
+
+ case CHAR_MAX + 2: /* --untranslated */
+ to_remove |= REMOVE_TRANSLATED;
+ break;
+
+ case CHAR_MAX + 3: /* --no-fuzzy */
+ to_remove |= REMOVE_FUZZY;
+ break;
+
+ case CHAR_MAX + 4: /* --only-fuzzy */
+ to_remove |= REMOVE_NONFUZZY;
+ break;
+
+ case CHAR_MAX + 5: /* --no-obsolete */
+ to_remove |= REMOVE_OBSOLETE;
+ break;
+
+ case CHAR_MAX + 6: /* --only-obsolete */
+ to_remove |= REMOVE_NONOBSOLETE;
+ break;
+
+ case CHAR_MAX + 7: /* --set-fuzzy */
+ to_change |= SET_FUZZY;
+ break;
+
+ case CHAR_MAX + 8: /* --clear-fuzzy */
+ to_change |= RESET_FUZZY;
+ break;
+
+ case CHAR_MAX + 9: /* --set-obsolete */
+ to_change |= SET_OBSOLETE;
+ break;
+
+ case CHAR_MAX + 10: /* --clear-obsolete */
+ to_change |= RESET_OBSOLETE;
+ break;
+
+ case CHAR_MAX + 11: /* --fuzzy */
+ to_remove |= REMOVE_NONFUZZY;
+ to_change |= RESET_FUZZY;
+ break;
+
+ case CHAR_MAX + 12: /* --obsolete */
+ to_remove |= REMOVE_NONOBSOLETE;
+ to_change |= RESET_OBSOLETE;
+ break;
+
+ case CHAR_MAX + 13: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 14: /* --only-file */
+ only_file = optarg;
+ break;
+
+ case CHAR_MAX + 15: /* --ignore-file */
+ ignore_file = optarg;
+ break;
+
+ case CHAR_MAX + 16: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 17: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 18: /* --clear-previous */
+ to_change |= REMOVE_PREV;
+ break;
+
+ case CHAR_MAX + 19: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 20: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 21: /* --previous */
+ to_change |= ADD_PREV;
+ break;
+
+ case CHAR_MAX + 22: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ case CHAR_MAX + 23: /* --empty */
+ to_change |= REMOVE_TRANSLATION;
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind == argc)
+ input_file = "-";
+ else if (optind + 1 == argc)
+ input_file = argv[optind];
+ else
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ /* Read optional files that limit the extent of the attribute changes. */
+ only_mdlp = (only_file != NULL
+ ? read_catalog_file (only_file, input_syntax)
+ : NULL);
+ ignore_mdlp = (ignore_file != NULL
+ ? read_catalog_file (ignore_file, input_syntax)
+ : NULL);
+
+ /* Filter the messages and manipulate the attributes. */
+ result = process_msgdomain_list (result, only_mdlp, ignore_mdlp);
+
+ /* Sorting the list of messages. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the PO file. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Filters the messages of a translation catalog according to their attributes,\n\
+and manipulates the attributes.\n"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Message selection:\n"));
+ printf (_("\
+ --translated keep translated, remove untranslated messages\n"));
+ printf (_("\
+ --untranslated keep untranslated, remove translated messages\n"));
+ printf (_("\
+ --no-fuzzy remove 'fuzzy' marked messages\n"));
+ printf (_("\
+ --only-fuzzy keep 'fuzzy' marked messages\n"));
+ printf (_("\
+ --no-obsolete remove obsolete #~ messages\n"));
+ printf (_("\
+ --only-obsolete keep obsolete #~ messages\n"));
+ printf ("\n");
+ printf (_("\
+Attribute manipulation:\n"));
+ printf (_("\
+ --set-fuzzy set all messages 'fuzzy'\n"));
+ printf (_("\
+ --clear-fuzzy set all messages non-'fuzzy'\n"));
+ printf (_("\
+ --set-obsolete set all messages obsolete\n"));
+ printf (_("\
+ --clear-obsolete set all messages non-obsolete\n"));
+ printf (_("\
+ --previous when setting 'fuzzy', keep previous msgids\n\
+ of translated messages.\n"));
+ printf (_("\
+ --clear-previous remove the \"previous msgid\" from all messages\n"));
+ printf (_("\
+ --empty when removing 'fuzzy', also set msgstr empty\n"));
+ printf (_("\
+ --only-file=FILE.po manipulate only entries listed in FILE.po\n"));
+ printf (_("\
+ --ignore-file=FILE.po manipulate only entries not listed in FILE.po\n"));
+ printf (_("\
+ --fuzzy synonym for --only-fuzzy --clear-fuzzy\n"));
+ printf (_("\
+ --obsolete synonym for --only-obsolete --clear-obsolete\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write the .po file using indented style\n"));
+ printf (_("\
+ --no-location do not write '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location generate '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict write out strict Uniforum conforming .po file\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Return true if a message should be kept. */
+static bool
+is_message_selected (const message_ty *mp)
+{
+ /* Always keep the header entry. */
+ if (is_header (mp))
+ return true;
+
+ if ((to_remove & (REMOVE_UNTRANSLATED | REMOVE_TRANSLATED))
+ && (mp->msgstr[0] == '\0'
+ ? to_remove & REMOVE_UNTRANSLATED
+ : to_remove & REMOVE_TRANSLATED))
+ return false;
+
+ if ((to_remove & (REMOVE_FUZZY | REMOVE_NONFUZZY))
+ && (mp->is_fuzzy
+ ? to_remove & REMOVE_FUZZY
+ : to_remove & REMOVE_NONFUZZY))
+ return false;
+
+ if ((to_remove & (REMOVE_OBSOLETE | REMOVE_NONOBSOLETE))
+ && (mp->obsolete
+ ? to_remove & REMOVE_OBSOLETE
+ : to_remove & REMOVE_NONOBSOLETE))
+ return false;
+
+ return true;
+}
+
+
+static void
+process_message_list (message_list_ty *mlp,
+ message_list_ty *only_mlp, message_list_ty *ignore_mlp)
+{
+ /* Keep only the selected messages. */
+ message_list_remove_if_not (mlp, is_message_selected);
+
+ /* Change the attributes. */
+ if (to_change)
+ {
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ /* Attribute changes only affect messages listed in --only-file
+ and not listed in --ignore-file. */
+ if ((only_mlp
+ ? message_list_search (only_mlp, mp->msgctxt, mp->msgid) != NULL
+ : true)
+ && (ignore_mlp
+ ? message_list_search (ignore_mlp, mp->msgctxt, mp->msgid) == NULL
+ : true))
+ {
+ if (to_change & SET_FUZZY)
+ {
+ if ((to_change & ADD_PREV) && !is_header (mp)
+ && !mp->is_fuzzy && mp->msgstr[0] != '\0')
+ {
+ mp->prev_msgctxt =
+ (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL);
+ mp->prev_msgid =
+ (mp->msgid != NULL ? xstrdup (mp->msgid) : NULL);
+ mp->prev_msgid_plural =
+ (mp->msgid_plural != NULL
+ ? xstrdup (mp->msgid_plural)
+ : NULL);
+ }
+ mp->is_fuzzy = true;
+ }
+
+ if (to_change & RESET_FUZZY)
+ {
+ if ((to_change & REMOVE_TRANSLATION)
+ && mp->is_fuzzy && !mp->obsolete)
+ {
+ unsigned long int nplurals = 0;
+ char *msgstr;
+ size_t pos;
+
+ for (pos = 0; pos < mp->msgstr_len; ++pos)
+ if (!mp->msgstr[pos])
+ ++nplurals;
+ free ((char *) mp->msgstr);
+ msgstr = XNMALLOC (nplurals, char);
+ memset (msgstr, '\0', nplurals);
+ mp->msgstr = msgstr;
+ mp->msgstr_len = nplurals;
+ }
+ mp->is_fuzzy = false;
+ }
+ /* Always keep the header entry non-obsolete. */
+ if ((to_change & SET_OBSOLETE) && !is_header (mp))
+ mp->obsolete = true;
+ if (to_change & RESET_OBSOLETE)
+ mp->obsolete = false;
+ if (to_change & REMOVE_PREV)
+ {
+ mp->prev_msgctxt = NULL;
+ mp->prev_msgid = NULL;
+ mp->prev_msgid_plural = NULL;
+ }
+ }
+ }
+ }
+}
+
+
+static msgdomain_list_ty *
+process_msgdomain_list (msgdomain_list_ty *mdlp,
+ msgdomain_list_ty *only_mdlp,
+ msgdomain_list_ty *ignore_mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ process_message_list (mdlp->item[k]->messages,
+ only_mdlp
+ ? msgdomain_list_sublist (only_mdlp,
+ mdlp->item[k]->domain,
+ true)
+ : NULL,
+ ignore_mdlp
+ ? msgdomain_list_sublist (ignore_mdlp,
+ mdlp->item[k]->domain,
+ false)
+ : NULL);
+
+ return mdlp;
+}
diff --git a/gettext-tools/src/msgcat.c b/gettext-tools/src/msgcat.c
new file mode 100644
index 0000000..0cc432c
--- /dev/null
+++ b/gettext-tools/src/msgcat.c
@@ -0,0 +1,485 @@
+/* Concatenates several translation catalogs.
+ Copyright (C) 2001-2007, 2009-2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "str-list.h"
+#include "file-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "msgl-cat.h"
+#include "msgl-header.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Target encoding. */
+static const char *to_code;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 5 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "files-from", required_argument, NULL, 'f' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "lang", required_argument, NULL, CHAR_MAX + 7 },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 8 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
+ { "style", required_argument, NULL, CHAR_MAX + 6 },
+ { "to-code", required_argument, NULL, 't' },
+ { "unique", no_argument, NULL, 'u' },
+ { "use-first", no_argument, NULL, CHAR_MAX + 1 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { "more-than", required_argument, NULL, '>', },
+ { "less-than", required_argument, NULL, '<', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+
+
+int
+main (int argc, char **argv)
+{
+ int cnt;
+ int optchar;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *files_from;
+ string_list_ty *file_list;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_msgid = false;
+ bool sort_by_filepos = false;
+ /* Language (ISO-639 code) and optional territory (ISO-3166 code). */
+ const char *catalogname = NULL;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ files_from = NULL;
+ more_than = 0;
+ less_than = INT_MAX;
+ use_first = false;
+
+ while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:",
+ long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case '>':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ more_than = value;
+ }
+ break;
+
+ case '<':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ less_than = value;
+ }
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'f':
+ files_from = optarg;
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 't':
+ to_code = optarg;
+ break;
+
+ case 'u':
+ less_than = 2;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1:
+ use_first = true;
+ break;
+
+ case CHAR_MAX + 2: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --color */
+ if (handle_color_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 6: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 7: /* --lang */
+ catalogname = optarg;
+ break;
+
+ case CHAR_MAX + 8: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ if (color_test_mode)
+ {
+ print_color_test ();
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Check the message selection criteria for sanity. */
+ if (more_than >= less_than || less_than < 2)
+ error (EXIT_FAILURE, 0,
+ _("impossible selection criteria specified (%d < n < %d)"),
+ more_than, less_than);
+
+ /* Determine list of files we have to process. */
+ if (files_from != NULL)
+ file_list = read_names_from_file (files_from);
+ else
+ file_list = string_list_alloc ();
+ /* Append names from command line. */
+ for (cnt = optind; cnt < argc; ++cnt)
+ string_list_append_unique (file_list, argv[cnt]);
+
+ /* Read input files, then filter, convert and merge messages. */
+ result =
+ catenate_msgdomain_list (file_list, input_syntax,
+ output_syntax->requires_utf8 ? "UTF-8" : to_code);
+
+ string_list_free (file_list);
+
+ /* Sorting the list of messages. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Set the Language field in the header. */
+ if (catalogname != NULL)
+ msgdomain_list_set_header_field (result, "Language:", catalogname);
+
+ /* Write the PO file. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]...\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Concatenates and merges the specified PO files.\n\
+Find messages which are common to two or more of the specified PO files.\n\
+By using the --more-than option, greater commonality may be requested\n\
+before messages are printed. Conversely, the --less-than option may be\n\
+used to specify less commonality before messages are printed (i.e.\n\
+--less-than=2 will only print the unique messages). Translations,\n\
+comments, extracted comments, and file positions will be cumulated, except\n\
+that if --use-first is specified, they will be taken from the first PO file\n\
+to define them.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE ... input files\n"));
+ printf (_("\
+ -f, --files-from=FILE get list of input files from FILE\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If input file is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Message selection:\n"));
+ printf (_("\
+ -<, --less-than=NUMBER print messages with less than this many\n\
+ definitions, defaults to infinite if not set\n"));
+ printf (_("\
+ ->, --more-than=NUMBER print messages with more than this many\n\
+ definitions, defaults to 0 if not set\n"));
+ printf (_("\
+ -u, --unique shorthand for --less-than=2, requests\n\
+ that only unique messages be printed\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ -t, --to-code=NAME encoding for output\n"));
+ printf (_("\
+ --use-first use first available translation for each\n\
+ message, don't merge several translations\n"));
+ printf (_("\
+ --lang=CATALOGNAME set 'Language' field in the header entry\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write the .po file using indented style\n"));
+ printf (_("\
+ --no-location do not write '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location generate '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict write out strict Uniforum conforming .po file\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
diff --git a/gettext-tools/src/msgcmp.c b/gettext-tools/src/msgcmp.c
new file mode 100644
index 0000000..f82131d
--- /dev/null
+++ b/gettext-tools/src/msgcmp.c
@@ -0,0 +1,555 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2010, 2012 Free Software Foundation, Inc.
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "xmalloca.h"
+#include "po-charset.h"
+#include "msgl-iconv.h"
+#include "msgl-fsearch.h"
+#include "c-strstr.h"
+#include "c-strcase.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Apply the .pot file to each of the domains in the PO file. */
+static bool multi_domain_mode = false;
+
+/* Determines whether to use fuzzy matching. */
+static bool use_fuzzy_matching = true;
+
+/* Whether to consider fuzzy messages as translations. */
+static bool include_fuzzies = false;
+
+/* Whether to consider untranslated messages as translations. */
+static bool include_untranslated = false;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "directory", required_argument, NULL, 'D' },
+ { "help", no_argument, NULL, 'h' },
+ { "multi-domain", no_argument, NULL, 'm' },
+ { "no-fuzzy-matching", no_argument, NULL, 'N' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
+ { "use-fuzzy", no_argument, NULL, CHAR_MAX + 2 },
+ { "use-untranslated", no_argument, NULL, CHAR_MAX + 3 },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void compare (const char *fn1, const char *fn2,
+ catalog_input_format_ty input_syntax);
+
+
+int
+main (int argc, char *argv[])
+{
+ int optchar;
+ bool do_help;
+ bool do_version;
+ catalog_input_format_ty input_syntax = &input_format_po;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+ gram_max_allowed_errors = UINT_MAX;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ do_help = false;
+ do_version = false;
+ while ((optchar = getopt_long (argc, argv, "D:hmNPV", long_options, NULL))
+ != EOF)
+ switch (optchar)
+ {
+ case '\0': /* long option */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'm':
+ multi_domain_mode = true;
+ break;
+
+ case 'N':
+ use_fuzzy_matching = false;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case CHAR_MAX + 1: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 2: /* --use-fuzzy */
+ include_fuzzies = true;
+ break;
+
+ case CHAR_MAX + 3: /* --use-untranslated */
+ include_untranslated = true;
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2010");
+ printf (_("Written by %s.\n"), proper_name ("Peter Miller"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind >= argc)
+ {
+ error (EXIT_SUCCESS, 0, _("no input files given"));
+ usage (EXIT_FAILURE);
+ }
+ if (optind + 2 != argc)
+ {
+ error (EXIT_SUCCESS, 0, _("exactly 2 input files required"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* compare the two files */
+ compare (argv[optind], argv[optind + 1], input_syntax);
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] def.po ref.pot\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Compare two Uniforum style .po files to check that both contain the same\n\
+set of msgid strings. The def.po file is an existing PO file with the\n\
+translations. The ref.pot file is the last created PO file, or a PO Template\n\
+file (generally created by xgettext). This is useful for checking that\n\
+you have translated each and every message in your program. Where an exact\n\
+match cannot be found, fuzzy matching is used to produce better diagnostics.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ def.po translations\n"));
+ printf (_("\
+ ref.pot references to the sources\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf ("\n");
+ printf (_("\
+Operation modifiers:\n"));
+ printf (_("\
+ -m, --multi-domain apply ref.pot to each of the domains in def.po\n"));
+ printf (_("\
+ -N, --no-fuzzy-matching do not use fuzzy matching\n"));
+ printf (_("\
+ --use-fuzzy consider fuzzy entries\n"));
+ printf (_("\
+ --use-untranslated consider untranslated entries\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Return true if a message should be kept. */
+static bool
+is_message_selected (const message_ty *mp)
+{
+ /* Always keep the header entry. */
+ if (is_header (mp))
+ return true;
+
+ return !mp->obsolete;
+}
+
+
+/* Remove obsolete messages from a message list. Return the modified list. */
+static msgdomain_list_ty *
+remove_obsoletes (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ message_list_remove_if_not (mdlp->item[k]->messages, is_message_selected);
+
+ return mdlp;
+}
+
+
+static void
+match_domain (const char *fn1, const char *fn2,
+ message_list_ty *defmlp, message_fuzzy_index_ty **defmlp_findex,
+ const char *def_canon_charset,
+ message_list_ty *refmlp,
+ int *nerrors)
+{
+ size_t j;
+
+ for (j = 0; j < refmlp->nitems; j++)
+ {
+ message_ty *refmsg;
+ message_ty *defmsg;
+
+ refmsg = refmlp->item[j];
+
+ /* See if it is in the other file. */
+ defmsg = message_list_search (defmlp, refmsg->msgctxt, refmsg->msgid);
+ if (defmsg)
+ {
+ if (!include_untranslated && defmsg->msgstr[0] == '\0')
+ {
+ (*nerrors)++;
+ po_gram_error_at_line (&defmsg->pos, _("\
+this message is untranslated"));
+ }
+ else if (!include_fuzzies && defmsg->is_fuzzy && !is_header (defmsg))
+ {
+ (*nerrors)++;
+ po_gram_error_at_line (&defmsg->pos, _("\
+this message needs to be reviewed by the translator"));
+ }
+ else
+ defmsg->used = 1;
+ }
+ else
+ {
+ /* If the message was not defined at all, try to find a very
+ similar message, it could be a typo, or the suggestion may
+ help. */
+ (*nerrors)++;
+ if (use_fuzzy_matching)
+ {
+ if (false)
+ {
+ /* Old, slow code. */
+ defmsg =
+ message_list_search_fuzzy (defmlp,
+ refmsg->msgctxt, refmsg->msgid);
+ }
+ else
+ {
+ /* Speedup through early abort in fstrcmp(), combined with
+ pre-sorting of the messages through a hashed index. */
+ /* Create the fuzzy index lazily. */
+ if (*defmlp_findex == NULL)
+ *defmlp_findex =
+ message_fuzzy_index_alloc (defmlp, def_canon_charset);
+ defmsg =
+ message_fuzzy_index_search (*defmlp_findex,
+ refmsg->msgctxt, refmsg->msgid,
+ FUZZY_THRESHOLD, false);
+ }
+ }
+ else
+ defmsg = NULL;
+ if (defmsg)
+ {
+ po_gram_error_at_line (&refmsg->pos, _("\
+this message is used but not defined..."));
+ error_message_count--;
+ po_gram_error_at_line (&defmsg->pos, _("\
+...but this definition is similar"));
+ defmsg->used = 1;
+ }
+ else
+ po_gram_error_at_line (&refmsg->pos, _("\
+this message is used but not defined in %s"), fn1);
+ }
+ }
+}
+
+
+static void
+compare (const char *fn1, const char *fn2, catalog_input_format_ty input_syntax)
+{
+ msgdomain_list_ty *def;
+ msgdomain_list_ty *ref;
+ int nerrors;
+ size_t j, k;
+ const char *def_canon_charset;
+ message_list_ty *empty_list;
+
+ /* This is the master file, created by a human. */
+ def = remove_obsoletes (read_catalog_file (fn1, input_syntax));
+
+ /* This is the generated file, created by groping the sources with
+ the xgettext program. */
+ ref = remove_obsoletes (read_catalog_file (fn2, input_syntax));
+
+ /* The references file can be either in ASCII or in UTF-8. If it is
+ in UTF-8, we have to convert the definitions to UTF-8 as well. */
+ {
+ bool was_utf8 = false;
+ for (k = 0; k < ref->nitems; k++)
+ {
+ message_list_ty *mlp = ref->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) /* && !mlp->item[j]->obsolete */)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ if (len == strlen ("UTF-8")
+ && c_strncasecmp (charsetstr, "UTF-8", len) == 0)
+ was_utf8 = true;
+ }
+ }
+ }
+ }
+ if (was_utf8)
+ def = iconv_msgdomain_list (def, "UTF-8", true, fn1);
+ }
+
+ /* Determine canonicalized encoding name of the definitions now, after
+ conversion. Only used for fuzzy matching. */
+ if (use_fuzzy_matching)
+ {
+ def_canon_charset = def->encoding;
+ if (def_canon_charset == NULL)
+ {
+ char *charset = NULL;
+
+ /* Get the encoding of the definitions file. */
+ for (k = 0; k < def->nitems; k++)
+ {
+ message_list_ty *mlp = def->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+ break;
+ }
+ }
+ }
+ if (charset != NULL)
+ break;
+ }
+ if (charset != NULL)
+ def_canon_charset = po_charset_canonicalize (charset);
+ if (def_canon_charset == NULL)
+ /* Unspecified encoding. Assume unibyte encoding. */
+ def_canon_charset = po_charset_ascii;
+ }
+ }
+ else
+ def_canon_charset = NULL;
+
+ empty_list = message_list_alloc (false);
+
+ /* Every entry in the xgettext generated file must be matched by a
+ (single) entry in the human created file. */
+ nerrors = 0;
+ if (!multi_domain_mode)
+ for (k = 0; k < ref->nitems; k++)
+ {
+ const char *domain = ref->item[k]->domain;
+ message_list_ty *refmlp = ref->item[k]->messages;
+ message_list_ty *defmlp;
+ message_fuzzy_index_ty *defmlp_findex;
+
+ defmlp = msgdomain_list_sublist (def, domain, false);
+ if (defmlp == NULL)
+ defmlp = empty_list;
+
+ defmlp_findex = NULL;
+
+ match_domain (fn1, fn2, defmlp, &defmlp_findex, def_canon_charset,
+ refmlp, &nerrors);
+
+ if (defmlp_findex != NULL)
+ message_fuzzy_index_free (defmlp_findex);
+ }
+ else
+ {
+ /* Apply the references messages in the default domain to each of
+ the definition domains. */
+ message_list_ty *refmlp = ref->item[0]->messages;
+
+ for (k = 0; k < def->nitems; k++)
+ {
+ message_list_ty *defmlp = def->item[k]->messages;
+
+ /* Ignore the default message domain if it has no messages. */
+ if (k > 0 || defmlp->nitems > 0)
+ {
+ message_fuzzy_index_ty *defmlp_findex = NULL;
+
+ match_domain (fn1, fn2, defmlp, &defmlp_findex, def_canon_charset,
+ refmlp, &nerrors);
+
+ if (defmlp_findex != NULL)
+ message_fuzzy_index_free (defmlp_findex);
+ }
+ }
+ }
+
+ /* Look for messages in the definition file, which are not present
+ in the reference file, indicating messages which defined but not
+ used in the program. */
+ for (k = 0; k < def->nitems; ++k)
+ {
+ message_list_ty *defmlp = def->item[k]->messages;
+
+ for (j = 0; j < defmlp->nitems; j++)
+ {
+ message_ty *defmsg = defmlp->item[j];
+
+ if (!defmsg->used)
+ po_gram_error_at_line (&defmsg->pos,
+ _("warning: this message is not used"));
+ }
+ }
+
+ /* Exit with status 1 on any error. */
+ if (nerrors > 0)
+ error (EXIT_FAILURE, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors", nerrors),
+ nerrors);
+}
diff --git a/gettext-tools/src/msgcomm.c b/gettext-tools/src/msgcomm.c
new file mode 100644
index 0000000..daddf2b
--- /dev/null
+++ b/gettext-tools/src/msgcomm.c
@@ -0,0 +1,471 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1997-1998, 2000-2007, 2009-2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "str-list.h"
+#include "file-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "msgl-cat.h"
+#include "propername.h"
+#include "gettext.h"
+
+
+/* A convenience macro. I don't like writing gettext() every time. */
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Target encoding. */
+static const char *to_code;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 5 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "files-from", required_argument, NULL, 'f' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 7 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
+ { "omit-header", no_argument, NULL, CHAR_MAX + 1 },
+ { "output", required_argument, NULL, 'o' }, /* for backward compatibility */
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
+ { "style", required_argument, NULL, CHAR_MAX + 6 },
+ { "to-code", required_argument, NULL, 't' },
+ { "unique", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { "more-than", required_argument, NULL, '>', },
+ { "less-than", required_argument, NULL, '<', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ > 4) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+
+
+int
+main (int argc, char *argv[])
+{
+ int cnt;
+ int optchar;
+ bool do_help = false;
+ bool do_version = false;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_msgid = false;
+ bool sort_by_filepos = false;
+ const char *files_from = NULL;
+ string_list_ty *file_list;
+ char *output_file = NULL;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ more_than = -1;
+ less_than = -1;
+ use_first = false;
+
+ while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:",
+ long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case '>':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ more_than = value;
+ }
+ break;
+
+ case '<':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ less_than = value;
+ }
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'f':
+ files_from = optarg;
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 't':
+ to_code = optarg;
+ break;
+
+ case 'u':
+ less_than = 2;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1:
+ omit_header = true;
+ break;
+
+ case CHAR_MAX + 2: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 6: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 7: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2010");
+ printf (_("Written by %s.\n"), proper_name ("Peter Miller"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Determine list of files we have to process. */
+ if (files_from != NULL)
+ file_list = read_names_from_file (files_from);
+ else
+ file_list = string_list_alloc ();
+ /* Append names from command line. */
+ for (cnt = optind; cnt < argc; ++cnt)
+ string_list_append_unique (file_list, argv[cnt]);
+
+ /* Test whether sufficient input files were given. */
+ if (file_list->nitems < 2)
+ {
+ error (EXIT_SUCCESS, 0, _("at least two files must be specified"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Default the message selection criteria, and check them for sanity. */
+ if (more_than < 0)
+ more_than = (less_than < 0 ? 1 : 0);
+ if (less_than < 0)
+ less_than = INT_MAX;
+ if (more_than >= less_than || less_than < 2)
+ error (EXIT_FAILURE, 0,
+ _("impossible selection criteria specified (%d < n < %d)"),
+ more_than, less_than);
+
+ /* Read input files, then filter, convert and merge messages. */
+ allow_duplicates = true;
+ msgcomm_mode = true;
+ result = catenate_msgdomain_list (file_list, input_syntax, to_code);
+
+ string_list_free (file_list);
+
+ /* Sorting the list of messages. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the PO file. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]...\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Find messages which are common to two or more of the specified PO files.\n\
+By using the --more-than option, greater commonality may be requested\n\
+before messages are printed. Conversely, the --less-than option may be\n\
+used to specify less commonality before messages are printed (i.e.\n\
+--less-than=2 will only print the unique messages). Translations,\n\
+comments and extracted comments will be preserved, but only from the first\n\
+PO file to define them. File positions from all PO files will be\n\
+cumulated.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE ... input files\n"));
+ printf (_("\
+ -f, --files-from=FILE get list of input files from FILE\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If input file is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Message selection:\n"));
+ printf (_("\
+ -<, --less-than=NUMBER print messages with less than this many\n\
+ definitions, defaults to infinite if not set\n"));
+ printf (_("\
+ ->, --more-than=NUMBER print messages with more than this many\n\
+ definitions, defaults to 1 if not set\n"));
+ printf (_("\
+ -u, --unique shorthand for --less-than=2, requests\n\
+ that only unique messages be printed\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write the .po file using indented style\n"));
+ printf (_("\
+ --no-location do not write '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location generate '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict write out strict Uniforum conforming .po file\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf (_("\
+ --omit-header don't write header with 'msgid \"\"' entry\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
diff --git a/gettext-tools/src/msgconv.c b/gettext-tools/src/msgconv.c
new file mode 100644
index 0000000..5c9f7ea
--- /dev/null
+++ b/gettext-tools/src/msgconv.c
@@ -0,0 +1,401 @@
+/* Converts a translation catalog to a different character encoding.
+ Copyright (C) 2001-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "msgl-iconv.h"
+#include "localcharset.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Target encoding. */
+static const char *to_code;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 4 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 6 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 1 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
+ { "style", required_argument, NULL, CHAR_MAX + 5 },
+ { "to-code", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_filepos = false;
+ bool sort_by_msgid = false;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+
+ while ((opt = getopt_long (argc, argv, "D:eEFhin:o:pPst:Vw:", long_options,
+ NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 't':
+ to_code = optarg;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 2: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 5: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 6: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind == argc)
+ input_file = "-";
+ else if (optind + 1 == argc)
+ input_file = argv[optind];
+ else
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Default for target encoding is current locale's encoding. */
+ if (to_code == NULL)
+ to_code = locale_charset ();
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ /* Convert if and only if the output syntax supports different encodings. */
+ if (!output_syntax->requires_utf8)
+ result = iconv_msgdomain_list (result, to_code, true, input_file);
+
+ /* Sort the results. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Converts a translation catalog to a different character encoding.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Conversion target:\n"));
+ printf (_("\
+ -t, --to-code=NAME encoding for output\n"));
+ printf (_("\
+The default encoding is the current locale's encoding.\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent indented output style\n"));
+ printf (_("\
+ --no-location suppress '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location preserve '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict strict Uniforum output style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
diff --git a/gettext-tools/src/msgen.c b/gettext-tools/src/msgen.c
new file mode 100644
index 0000000..e33c61a
--- /dev/null
+++ b/gettext-tools/src/msgen.c
@@ -0,0 +1,397 @@
+/* Creates an English translation catalog.
+ Copyright (C) 2001-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "msgl-english.h"
+#include "msgl-header.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 5 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "lang", required_argument, NULL, CHAR_MAX + 4 },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 7 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 1 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
+ { "style", required_argument, NULL, CHAR_MAX + 6 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_filepos = false;
+ bool sort_by_msgid = false;
+ /* Language (ISO-639 code) and optional territory (ISO-3166 code). */
+ const char *catalogname = NULL;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+
+ while ((opt = getopt_long (argc, argv,
+ "D:eEFhin:o:pPsVw:",
+ long_options, NULL)) != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 2: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --lang */
+ catalogname = optarg;
+ break;
+
+ case CHAR_MAX + 5: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 6: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 7: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind >= argc)
+ {
+ error (EXIT_SUCCESS, 0, _("no input file given"));
+ usage (EXIT_FAILURE);
+ }
+ if (optind + 1 != argc)
+ {
+ error (EXIT_SUCCESS, 0, _("exactly one input file required"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Read input file. */
+ result = read_catalog_file (argv[optind], input_syntax);
+
+ /* Add English translations. */
+ result = msgdomain_list_english (result);
+
+ /* Sort the results. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Set the Language field in the header. */
+ if (catalogname != NULL)
+ msgdomain_list_set_header_field (result, "Language:", catalogname);
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] INPUTFILE\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Creates an English translation catalog. The input file is the last\n\
+created English PO file, or a PO Template file (generally created by\n\
+xgettext). Untranslated entries are assigned a translation that is\n\
+identical to the msgid.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE input PO or POT file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If input file is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --lang=CATALOGNAME set 'Language' field in the header entry\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent indented output style\n"));
+ printf (_("\
+ --no-location suppress '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location preserve '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict strict Uniforum output style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
diff --git a/gettext-tools/src/msgexec.c b/gettext-tools/src/msgexec.c
new file mode 100644
index 0000000..5239518
--- /dev/null
+++ b/gettext-tools/src/msgexec.c
@@ -0,0 +1,468 @@
+/* Pass translations to a subprocess.
+ Copyright (C) 2001-2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "xvasprintf.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "msgl-charset.h"
+#include "xalloc.h"
+#include "full-write.h"
+#include "findprog.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "xsetenv.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+
+/* Name of the subprogram. */
+static const char *sub_name;
+
+/* Pathname of the subprogram. */
+static const char *sub_path;
+
+/* Argument list for the subprogram. */
+static char **sub_argv;
+static int sub_argc;
+
+/* Maximum exit code encountered. */
+static int exitcode;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "directory", required_argument, NULL, 'D' },
+ { "help", no_argument, NULL, 'h' },
+ { "input", required_argument, NULL, 'i' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void process_msgdomain_list (const msgdomain_list_ty *mdlp);
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ const char *input_file;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ size_t i;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ input_file = NULL;
+
+ /* The '+' in the options string causes option parsing to terminate when
+ the first non-option, i.e. the subprogram name, is encountered. */
+ while ((opt = getopt_long (argc, argv, "+D:hi:PV", long_options, NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ if (input_file != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+ input_file = optarg;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case CHAR_MAX + 1: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test for the subprogram name. */
+ if (optind == argc)
+ error (EXIT_FAILURE, 0, _("missing command name"));
+ sub_name = argv[optind];
+
+ /* Build argument list for the program. */
+ sub_argc = argc - optind;
+ sub_argv = XNMALLOC (sub_argc + 1, char *);
+ for (i = 0; i < sub_argc; i++)
+ sub_argv[i] = argv[optind + i];
+ sub_argv[i] = NULL;
+
+ /* By default, input comes from standard input. */
+ if (input_file == NULL)
+ input_file = "-";
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ if (strcmp (sub_name, "0") != 0)
+ {
+ /* Warn if the current locale is not suitable for this PO file. */
+ compare_po_locale_charsets (result);
+
+ /* Block SIGPIPE for this process and for the subprocesses.
+ The subprogram may have side effects (additionally to producing some
+ output), therefore if there are no readers on stdout, processing of the
+ strings must continue nevertheless. */
+ {
+ sigset_t sigpipe_set;
+
+ sigemptyset (&sigpipe_set);
+ sigaddset (&sigpipe_set, SIGPIPE);
+ sigprocmask (SIG_UNBLOCK, &sigpipe_set, NULL);
+ }
+
+ /* Attempt to locate the program.
+ This is an optimization, to avoid that spawn/exec searches the PATH
+ on every call. */
+ sub_path = find_in_path (sub_name);
+
+ /* Finish argument list for the program. */
+ sub_argv[0] = (char *) sub_path;
+ }
+
+ exitcode = 0; /* = EXIT_SUCCESS */
+
+ /* Apply the subprogram. */
+ process_msgdomain_list (result);
+
+ exit (exitcode);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Applies a command to all translations of a translation catalog.\n\
+The COMMAND can be any program that reads a translation from standard\n\
+input. It is invoked once for each translation. Its output becomes\n\
+msgexec's output. msgexec's return code is the maximum return code\n\
+across all invocations.\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+A special builtin command called '0' outputs the translation, followed by a\n\
+null byte. The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ -i, --input=INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+#ifdef EINTR
+
+/* EINTR handling for close().
+ These functions can return -1/EINTR even though we don't have any
+ signal handlers set up, namely when we get interrupted via SIGSTOP. */
+
+static inline int
+nonintr_close (int fd)
+{
+ int retval;
+
+ do
+ retval = close (fd);
+ while (retval < 0 && errno == EINTR);
+
+ return retval;
+}
+#define close nonintr_close
+
+#endif
+
+
+/* Pipe a string STR of size LEN bytes to the subprogram.
+ The byte after STR is known to be a '\0' byte. */
+static void
+process_string (const message_ty *mp, const char *str, size_t len)
+{
+ if (strcmp (sub_name, "0") == 0)
+ {
+ /* Built-in command "0". */
+ if (full_write (STDOUT_FILENO, str, len + 1) < len + 1)
+ error (EXIT_FAILURE, errno, _("write to stdout failed"));
+ }
+ else
+ {
+ /* General command. */
+ char *location;
+ pid_t child;
+ int fd[1];
+ void (*orig_sigpipe_handler)(int);
+ int exitstatus;
+
+ /* Set environment variables for the subprocess.
+ Note: These environment variables, especially MSGEXEC_MSGCTXT and
+ MSGEXEC_MSGID, may contain non-ASCII characters. The subprocess
+ may not interpret these values correctly if the locale encoding is
+ different from the PO file's encoding. We want about this situation,
+ above.
+ On Unix, this problem is often harmless. On Windows, however, - both
+ native Windows and Cygwin - the values of environment variables *must*
+ be in the encoding that is the value of GetACP(), because the system
+ may convert the environment from char** to wchar_t** before spawning
+ the subprocess and back from wchar_t** to char** in the subprocess,
+ and it does so using the GetACP() codepage. */
+ if (mp->msgctxt != NULL)
+ xsetenv ("MSGEXEC_MSGCTXT", mp->msgctxt, 1);
+ else
+ unsetenv ("MSGEXEC_MSGCTXT");
+ xsetenv ("MSGEXEC_MSGID", mp->msgid, 1);
+ if (mp->msgid_plural != NULL)
+ xsetenv ("MSGEXEC_MSGID_PLURAL", mp->msgid_plural, 1);
+ else
+ unsetenv ("MSGEXEC_MSGID_PLURAL");
+ location = xasprintf ("%s:%ld", mp->pos.file_name,
+ (long) mp->pos.line_number);
+ xsetenv ("MSGEXEC_LOCATION", location, 1);
+ free (location);
+ if (mp->prev_msgctxt != NULL)
+ xsetenv ("MSGEXEC_PREV_MSGCTXT", mp->prev_msgctxt, 1);
+ else
+ unsetenv ("MSGEXEC_PREV_MSGCTXT");
+ if (mp->prev_msgid != NULL)
+ xsetenv ("MSGEXEC_PREV_MSGID", mp->prev_msgid, 1);
+ else
+ unsetenv ("MSGEXEC_PREV_MSGID");
+ if (mp->prev_msgid_plural != NULL)
+ xsetenv ("MSGEXEC_PREV_MSGID_PLURAL", mp->prev_msgid_plural, 1);
+ else
+ unsetenv ("MSGEXEC_PREV_MSGID_PLURAL");
+
+ /* Open a pipe to a subprocess. */
+ child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
+ true, fd);
+
+ /* Ignore SIGPIPE here. We don't care if the subprocesses terminates
+ successfully without having read all of the input that we feed it. */
+ orig_sigpipe_handler = signal (SIGPIPE, SIG_IGN);
+
+ if (full_write (fd[0], str, len) < len)
+ if (errno != EPIPE)
+ error (EXIT_FAILURE, errno,
+ _("write to %s subprocess failed"), sub_name);
+
+ close (fd[0]);
+
+ signal (SIGPIPE, orig_sigpipe_handler);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ /* FIXME: Should ignore_sigpipe be set to true here? It depends on the
+ semantics of the subprogram... */
+ exitstatus =
+ wait_subprocess (child, sub_name, false, false, true, true, NULL);
+ if (exitcode < exitstatus)
+ exitcode = exitstatus;
+ }
+}
+
+
+static void
+process_message (const message_ty *mp)
+{
+ const char *msgstr = mp->msgstr;
+ size_t msgstr_len = mp->msgstr_len;
+ const char *p;
+ size_t k;
+
+ /* Process each NUL delimited substring separately. */
+ for (p = msgstr, k = 0; p < msgstr + msgstr_len; k++)
+ {
+ size_t length = strlen (p);
+
+ if (mp->msgid_plural != NULL)
+ {
+ char *plural_form_string = xasprintf ("%zu", k);
+
+ xsetenv ("MSGEXEC_PLURAL_FORM", plural_form_string, 1);
+ free (plural_form_string);
+ }
+ else
+ unsetenv ("MSGEXEC_PLURAL_FORM");
+ process_string (mp, p, length);
+
+ p += length + 1;
+ }
+}
+
+
+static void
+process_message_list (const message_list_ty *mlp)
+{
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ process_message (mlp->item[j]);
+}
+
+
+static void
+process_msgdomain_list (const msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ process_message_list (mdlp->item[k]->messages);
+}
diff --git a/gettext-tools/src/msgfilter.c b/gettext-tools/src/msgfilter.c
new file mode 100644
index 0000000..f9cb1cc
--- /dev/null
+++ b/gettext-tools/src/msgfilter.c
@@ -0,0 +1,753 @@
+/* Edit translations using a subprocess.
+ Copyright (C) 2001-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "xvasprintf.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "msgl-charset.h"
+#include "xalloc.h"
+#include "findprog.h"
+#include "pipe-filter.h"
+#include "xsetenv.h"
+#include "filters.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* We use a child process, and communicate through a bidirectional pipe. */
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Keep the header entry unmodified. */
+static int keep_header;
+
+/* Name of the subprogram. */
+static const char *sub_name;
+
+/* Pathname of the subprogram. */
+static const char *sub_path;
+
+/* Argument list for the subprogram. */
+static const char **sub_argv;
+static int sub_argc;
+
+/* Filter function. */
+static void (*filter) (const char *str, size_t len, char **resultp, size_t *lengthp);
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 6 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, CHAR_MAX + 1 },
+ { "input", required_argument, NULL, 'i' },
+ { "keep-header", no_argument, &keep_header, 1 },
+ { "no-escape", no_argument, NULL, CHAR_MAX + 2 },
+ { "no-location", no_argument, NULL, CHAR_MAX + 8 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 3 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 4 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 5 },
+ { "style", required_argument, NULL, CHAR_MAX + 7 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void generic_filter (const char *str, size_t len, char **resultp, size_t *lengthp);
+static msgdomain_list_ty *process_msgdomain_list (msgdomain_list_ty *mdlp);
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_filepos = false;
+ bool sort_by_msgid = false;
+ int i;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+
+ /* The '+' in the options string causes option parsing to terminate when
+ the first non-option, i.e. the subprogram name, is encountered. */
+ while ((opt = getopt_long (argc, argv, "+D:EFhi:n:o:pPsVw:", long_options,
+ NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ if (input_file != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+ input_file = optarg;
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1:
+ message_print_style_indent ();
+ break;
+
+ case CHAR_MAX + 2:
+ message_print_style_escape (false);
+ break;
+
+ case CHAR_MAX + 3: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 6: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 7: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 8: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test for the subprogram name. */
+ if (optind == argc)
+ error (EXIT_FAILURE, 0, _("missing filter name"));
+ sub_name = argv[optind];
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Build argument list for the program. */
+ sub_argc = argc - optind;
+ sub_argv = XNMALLOC (sub_argc + 1, const char *);
+ for (i = 0; i < sub_argc; i++)
+ sub_argv[i] = argv[optind + i];
+ sub_argv[i] = NULL;
+
+ /* Extra checks for sed scripts. */
+ if (strcmp (sub_name, "sed") == 0)
+ {
+ if (sub_argc == 1)
+ error (EXIT_FAILURE, 0,
+ _("at least one sed script must be specified"));
+
+ /* Replace GNU sed specific options with portable sed options. */
+ for (i = 1; i < sub_argc; i++)
+ {
+ if (strcmp (sub_argv[i], "--expression") == 0)
+ sub_argv[i] = "-e";
+ else if (strcmp (sub_argv[i], "--file") == 0)
+ sub_argv[i] = "-f";
+ else if (strcmp (sub_argv[i], "--quiet") == 0
+ || strcmp (sub_argv[i], "--silent") == 0)
+ sub_argv[i] = "-n";
+
+ if (strcmp (sub_argv[i], "-e") == 0
+ || strcmp (sub_argv[i], "-f") == 0)
+ i++;
+ }
+ }
+
+ /* By default, input comes from standard input. */
+ if (input_file == NULL)
+ input_file = "-";
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ /* Recognize special programs as built-ins. */
+ if (strcmp (sub_name, "recode-sr-latin") == 0 && sub_argc == 1)
+ {
+ filter = serbian_to_latin;
+
+ /* Convert the input to UTF-8 first. */
+ result = iconv_msgdomain_list (result, po_charset_utf8, true, input_file);
+ }
+ else if (strcmp (sub_name, "quot") == 0 && sub_argc == 1)
+ {
+ filter = ascii_quote_to_unicode;
+
+ /* Convert the input to UTF-8 first. */
+ result = iconv_msgdomain_list (result, po_charset_utf8, true, input_file);
+ }
+ else if (strcmp (sub_name, "boldquot") == 0 && sub_argc == 1)
+ {
+ filter = ascii_quote_to_unicode_bold;
+
+ /* Convert the input to UTF-8 first. */
+ result = iconv_msgdomain_list (result, po_charset_utf8, true, input_file);
+ }
+ else
+ {
+ filter = generic_filter;
+
+ /* Warn if the current locale is not suitable for this PO file. */
+ compare_po_locale_charsets (result);
+
+ /* Attempt to locate the program.
+ This is an optimization, to avoid that spawn/exec searches the PATH
+ on every call. */
+ sub_path = find_in_path (sub_name);
+
+ /* Finish argument list for the program. */
+ sub_argv[0] = sub_path;
+ }
+
+ /* Apply the subprogram. */
+ result = process_msgdomain_list (result);
+
+ /* Sort the results. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] FILTER [FILTER-OPTION]\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Applies a filter to all translations of a translation catalog.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ -i, --input=INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+The FILTER can be any program that reads a translation from standard input\n\
+and writes a modified translation to standard output.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Useful FILTER-OPTIONs when the FILTER is 'sed':\n"));
+ printf (_("\
+ -e, --expression=SCRIPT add SCRIPT to the commands to be executed\n"));
+ printf (_("\
+ -f, --file=SCRIPTFILE add the contents of SCRIPTFILE to the commands\n\
+ to be executed\n"));
+ printf (_("\
+ -n, --quiet, --silent suppress automatic printing of pattern space\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ --indent indented output style\n"));
+ printf (_("\
+ --keep-header keep header entry unmodified, don't filter it\n"));
+ printf (_("\
+ --no-location suppress '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location preserve '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict strict Uniforum output style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Callbacks called from pipe_filter_ii_execute. */
+
+struct locals
+{
+ /* String being written. */
+ const char *str;
+ size_t len;
+ /* String being read and accumulated. */
+ char *result;
+ size_t allocated;
+ size_t length;
+};
+
+static const void *
+prepare_write (size_t *num_bytes_p, void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+
+ if (l->len > 0)
+ {
+ *num_bytes_p = l->len;
+ return l->str;
+ }
+ else
+ return NULL;
+}
+
+static void
+done_write (void *data_written, size_t num_bytes_written, void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+
+ l->str += num_bytes_written;
+ l->len -= num_bytes_written;
+}
+
+static void *
+prepare_read (size_t *num_bytes_p, void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+
+ if (l->length == l->allocated)
+ {
+ l->allocated = l->allocated + (l->allocated >> 1) + 1;
+ l->result = (char *) xrealloc (l->result, l->allocated);
+ }
+ *num_bytes_p = l->allocated - l->length;
+ return l->result + l->length;
+}
+
+static void
+done_read (void *data_read, size_t num_bytes_read, void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+
+ l->length += num_bytes_read;
+}
+
+
+/* Process a string STR of size LEN bytes through the subprogram.
+ Store the freshly allocated result at *RESULTP and its length at *LENGTHP.
+ */
+static void
+generic_filter (const char *str, size_t len, char **resultp, size_t *lengthp)
+{
+ struct locals l;
+
+ l.str = str;
+ l.len = len;
+ l.allocated = len + (len >> 2) + 1;
+ l.result = XNMALLOC (l.allocated, char);
+ l.length = 0;
+
+ pipe_filter_ii_execute (sub_name, sub_path, sub_argv, false, true,
+ prepare_write, done_write, prepare_read, done_read,
+ &l);
+
+ *resultp = l.result;
+ *lengthp = l.length;
+}
+
+
+/* Process a string STR of size LEN bytes, then remove NUL bytes.
+ Store the freshly allocated result at *RESULTP and its length at *LENGTHP.
+ */
+static void
+process_string (const char *str, size_t len, char **resultp, size_t *lengthp)
+{
+ char *result;
+ size_t length;
+
+ filter (str, len, &result, &length);
+
+ /* Remove NUL bytes from result. */
+ {
+ char *p = result;
+ char *pend = result + length;
+
+ for (; p < pend; p++)
+ if (*p == '\0')
+ {
+ char *q;
+
+ q = p;
+ for (; p < pend; p++)
+ if (*p != '\0')
+ *q++ = *p;
+ length = q - result;
+ break;
+ }
+ }
+
+ *resultp = result;
+ *lengthp = length;
+}
+
+
+static void
+process_message (message_ty *mp)
+{
+ const char *msgstr = mp->msgstr;
+ size_t msgstr_len = mp->msgstr_len;
+ char *location;
+ size_t nsubstrings;
+ char **substrings;
+ size_t total_len;
+ char *total_str;
+ const char *p;
+ char *q;
+ size_t k;
+
+ /* Keep the header entry unmodified, if --keep-header was given. */
+ if (is_header (mp) && keep_header)
+ return;
+
+ /* Set environment variables for the subprocess.
+ Note: These environment variables, especially MSGFILTER_MSGCTXT and
+ MSGFILTER_MSGID, may contain non-ASCII characters. The subprocess
+ may not interpret these values correctly if the locale encoding is
+ different from the PO file's encoding. We want about this situation,
+ above.
+ On Unix, this problem is often harmless. On Windows, however, - both
+ native Windows and Cygwin - the values of environment variables *must*
+ be in the encoding that is the value of GetACP(), because the system
+ may convert the environment from char** to wchar_t** before spawning
+ the subprocess and back from wchar_t** to char** in the subprocess,
+ and it does so using the GetACP() codepage. */
+ if (mp->msgctxt != NULL)
+ xsetenv ("MSGFILTER_MSGCTXT", mp->msgctxt, 1);
+ else
+ unsetenv ("MSGFILTER_MSGCTXT");
+ xsetenv ("MSGFILTER_MSGID", mp->msgid, 1);
+ if (mp->msgid_plural != NULL)
+ xsetenv ("MSGFILTER_MSGID_PLURAL", mp->msgid_plural, 1);
+ else
+ unsetenv ("MSGFILTER_MSGID_PLURAL");
+ location = xasprintf ("%s:%ld", mp->pos.file_name,
+ (long) mp->pos.line_number);
+ xsetenv ("MSGFILTER_LOCATION", location, 1);
+ free (location);
+ if (mp->prev_msgctxt != NULL)
+ xsetenv ("MSGFILTER_PREV_MSGCTXT", mp->prev_msgctxt, 1);
+ else
+ unsetenv ("MSGFILTER_PREV_MSGCTXT");
+ if (mp->prev_msgid != NULL)
+ xsetenv ("MSGFILTER_PREV_MSGID", mp->prev_msgid, 1);
+ else
+ unsetenv ("MSGFILTER_PREV_MSGID");
+ if (mp->prev_msgid_plural != NULL)
+ xsetenv ("MSGFILTER_PREV_MSGID_PLURAL", mp->prev_msgid_plural, 1);
+ else
+ unsetenv ("MSGFILTER_PREV_MSGID_PLURAL");
+
+ /* Count NUL delimited substrings. */
+ for (p = msgstr, nsubstrings = 0;
+ p < msgstr + msgstr_len;
+ p += strlen (p) + 1, nsubstrings++);
+
+ /* Process each NUL delimited substring separately. */
+ substrings = XNMALLOC (nsubstrings, char *);
+ for (p = msgstr, k = 0, total_len = 0; k < nsubstrings; k++)
+ {
+ char *result;
+ size_t length;
+
+ if (mp->msgid_plural != NULL)
+ {
+ char *plural_form_string = xasprintf ("%zu", k);
+
+ xsetenv ("MSGFILTER_PLURAL_FORM", plural_form_string, 1);
+ free (plural_form_string);
+ }
+ else
+ unsetenv ("MSGFILTER_PLURAL_FORM");
+ process_string (p, strlen (p), &result, &length);
+ result = (char *) xrealloc (result, length + 1);
+ result[length] = '\0';
+ substrings[k] = result;
+ total_len += length + 1;
+
+ p += strlen (p) + 1;
+ }
+
+ /* Concatenate the results, including the NUL after each. */
+ total_str = XNMALLOC (total_len, char);
+ for (k = 0, q = total_str; k < nsubstrings; k++)
+ {
+ size_t length = strlen (substrings[k]);
+
+ memcpy (q, substrings[k], length + 1);
+ free (substrings[k]);
+ q += length + 1;
+ }
+ free (substrings);
+
+ mp->msgstr = total_str;
+ mp->msgstr_len = total_len;
+}
+
+
+static void
+process_message_list (message_list_ty *mlp)
+{
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ process_message (mlp->item[j]);
+}
+
+
+static msgdomain_list_ty *
+process_msgdomain_list (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ process_message_list (mdlp->item[k]->messages);
+
+ return mdlp;
+}
diff --git a/gettext-tools/src/msgfmt.c b/gettext-tools/src/msgfmt.c
new file mode 100644
index 0000000..3fabd87
--- /dev/null
+++ b/gettext-tools/src/msgfmt.c
@@ -0,0 +1,1473 @@
+/* Converts Uniforum style .po files to binary .mo files
+ Copyright (C) 1995-1998, 2000-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "closeout.h"
+#include "str-list.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "xalloc.h"
+#include "msgfmt.h"
+#include "write-mo.h"
+#include "write-java.h"
+#include "write-csharp.h"
+#include "write-resources.h"
+#include "write-tcl.h"
+#include "write-qt.h"
+#include "write-desktop.h"
+#include "propername.h"
+#include "message.h"
+#include "open-catalog.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "read-desktop.h"
+#include "po-charset.h"
+#include "msgl-check.h"
+#include "msgl-iconv.h"
+#include "concat-filename.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Contains exit status for case in which no premature exit occurs. */
+static int exit_status;
+
+/* If true include even fuzzy translations in output file. */
+static bool include_fuzzies = false;
+
+/* If true include even untranslated messages in output file. */
+static bool include_untranslated = false;
+
+/* Specifies name of the output file. */
+static const char *output_file_name;
+
+/* Java mode output file specification. */
+static bool java_mode;
+static bool assume_java2;
+static const char *java_resource_name;
+static const char *java_locale_name;
+static const char *java_class_directory;
+static bool java_output_source;
+
+/* C# mode output file specification. */
+static bool csharp_mode;
+static const char *csharp_resource_name;
+static const char *csharp_locale_name;
+static const char *csharp_base_directory;
+
+/* C# resources mode output file specification. */
+static bool csharp_resources_mode;
+
+/* Tcl mode output file specification. */
+static bool tcl_mode;
+static const char *tcl_locale_name;
+static const char *tcl_base_directory;
+
+/* Qt mode output file specification. */
+static bool qt_mode;
+
+/* Desktop Entry mode output file specification. */
+static bool desktop_mode;
+static const char *desktop_locale_name;
+static const char *desktop_template_name;
+static const char *desktop_base_directory;
+static hash_table desktop_keywords;
+static bool desktop_default_keywords = true;
+
+/* We may have more than one input file. Domains with same names in
+ different files have to merged. So we need a list of tables for
+ each output file. */
+struct msg_domain
+{
+ /* List for mapping message IDs to message strings. */
+ message_list_ty *mlp;
+ /* Name of domain these ID/String pairs are part of. */
+ const char *domain_name;
+ /* Output file name. */
+ const char *file_name;
+ /* Link to the next domain. */
+ struct msg_domain *next;
+};
+static struct msg_domain *domain_list;
+static struct msg_domain *current_domain;
+
+/* Be more verbose. Use only 'fprintf' and 'multiline_warning' but not
+ 'error' or 'multiline_error' to emit verbosity messages, because 'error'
+ and 'multiline_error' during PO file parsing cause the program to exit
+ with EXIT_FAILURE. See function lex_end(). */
+int verbose = 0;
+
+/* If true check strings according to format string rules for the
+ language. */
+static bool check_format_strings = false;
+
+/* If true check the header entry is present and complete. */
+static bool check_header = false;
+
+/* Check that domain directives can be satisfied. */
+static bool check_domain = false;
+
+/* Check that msgfmt's behaviour is semantically compatible with
+ X/Open msgfmt or XView msgfmt. */
+static bool check_compatibility = false;
+
+/* If true, consider that strings containing an '&' are menu items and
+ the '&' designates a keyboard accelerator, and verify that the translations
+ also have a keyboard accelerator. */
+static bool check_accelerators = false;
+static char accelerator_char = '&';
+
+/* Counters for statistics on translations for the processed files. */
+static int msgs_translated;
+static int msgs_untranslated;
+static int msgs_fuzzy;
+
+/* If not zero print statistics about translation at the end. */
+static int do_statistics;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "alignment", required_argument, NULL, 'a' },
+ { "check", no_argument, NULL, 'c' },
+ { "check-accelerators", optional_argument, NULL, CHAR_MAX + 1 },
+ { "check-compatibility", no_argument, NULL, 'C' },
+ { "check-domain", no_argument, NULL, CHAR_MAX + 2 },
+ { "check-format", no_argument, NULL, CHAR_MAX + 3 },
+ { "check-header", no_argument, NULL, CHAR_MAX + 4 },
+ { "csharp", no_argument, NULL, CHAR_MAX + 10 },
+ { "csharp-resources", no_argument, NULL, CHAR_MAX + 11 },
+ { "desktop", no_argument, NULL, CHAR_MAX + 15 },
+ { "directory", required_argument, NULL, 'D' },
+ { "endianness", required_argument, NULL, CHAR_MAX + 13 },
+ { "help", no_argument, NULL, 'h' },
+ { "java", no_argument, NULL, 'j' },
+ { "java2", no_argument, NULL, CHAR_MAX + 5 },
+ { "keyword", required_argument, NULL, 'k' },
+ { "locale", required_argument, NULL, 'l' },
+ { "no-hash", no_argument, NULL, CHAR_MAX + 6 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "qt", no_argument, NULL, CHAR_MAX + 9 },
+ { "resource", required_argument, NULL, 'r' },
+ { "source", no_argument, NULL, CHAR_MAX + 14 },
+ { "statistics", no_argument, &do_statistics, 1 },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 8 },
+ { "tcl", no_argument, NULL, CHAR_MAX + 7 },
+ { "template", required_argument, NULL, CHAR_MAX + 16 },
+ { "use-fuzzy", no_argument, NULL, 'f' },
+ { "use-untranslated", no_argument, NULL, CHAR_MAX + 12 },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static const char *add_mo_suffix (const char *);
+static struct msg_domain *new_domain (const char *name, const char *file_name);
+static bool is_nonobsolete (const message_ty *mp);
+static void read_catalog_file_msgfmt (char *filename,
+ catalog_input_format_ty input_syntax);
+static string_list_ty *get_languages (const char *directory);
+static int msgfmt_desktop_bulk (const char *directory,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
+
+
+int
+main (int argc, char *argv[])
+{
+ int opt;
+ bool do_help = false;
+ bool do_version = false;
+ bool strict_uniforum = false;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ int arg_i;
+ const char *canon_encoding;
+ struct msg_domain *domain;
+
+ /* Set default value for global variables. */
+ alignment = DEFAULT_OUTPUT_ALIGNMENT;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+ error_one_per_line = 1;
+ exit_status = EXIT_SUCCESS;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ while ((opt = getopt_long (argc, argv, "a:cCd:D:fhjl:o:Pr:vV", long_options,
+ NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+ case 'a':
+ {
+ char *endp;
+ size_t new_align = strtoul (optarg, &endp, 0);
+
+ if (endp != optarg)
+ alignment = new_align;
+ }
+ break;
+ case 'c':
+ check_domain = true;
+ check_format_strings = true;
+ check_header = true;
+ break;
+ case 'C':
+ check_compatibility = true;
+ break;
+ case 'd':
+ java_class_directory = optarg;
+ csharp_base_directory = optarg;
+ tcl_base_directory = optarg;
+ desktop_base_directory = optarg;
+ break;
+ case 'D':
+ dir_list_append (optarg);
+ break;
+ case 'f':
+ include_fuzzies = true;
+ break;
+ case 'h':
+ do_help = true;
+ break;
+ case 'j':
+ java_mode = true;
+ break;
+ case 'k':
+ if (optarg == NULL)
+ desktop_default_keywords = false;
+ else
+ {
+ if (desktop_keywords.table == NULL)
+ {
+ hash_init (&desktop_keywords, 100);
+ desktop_default_keywords = false;
+ }
+
+ desktop_add_keyword (&desktop_keywords, optarg, false);
+ }
+ break;
+ case 'l':
+ java_locale_name = optarg;
+ csharp_locale_name = optarg;
+ tcl_locale_name = optarg;
+ desktop_locale_name = optarg;
+ break;
+ case 'o':
+ output_file_name = optarg;
+ break;
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+ case 'r':
+ java_resource_name = optarg;
+ csharp_resource_name = optarg;
+ break;
+ case 'S':
+ strict_uniforum = true;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'V':
+ do_version = true;
+ break;
+ case CHAR_MAX + 1: /* --check-accelerators */
+ check_accelerators = true;
+ if (optarg != NULL)
+ {
+ if (optarg[0] != '\0' && ispunct ((unsigned char) optarg[0])
+ && optarg[1] == '\0')
+ accelerator_char = optarg[0];
+ else
+ error (EXIT_FAILURE, 0,
+ _("the argument to %s should be a single punctuation character"),
+ "--check-accelerators");
+ }
+ break;
+ case CHAR_MAX + 2: /* --check-domain */
+ check_domain = true;
+ break;
+ case CHAR_MAX + 3: /* --check-format */
+ check_format_strings = true;
+ break;
+ case CHAR_MAX + 4: /* --check-header */
+ check_header = true;
+ break;
+ case CHAR_MAX + 5: /* --java2 */
+ java_mode = true;
+ assume_java2 = true;
+ break;
+ case CHAR_MAX + 6: /* --no-hash */
+ no_hash_table = true;
+ break;
+ case CHAR_MAX + 7: /* --tcl */
+ tcl_mode = true;
+ break;
+ case CHAR_MAX + 8: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+ case CHAR_MAX + 9: /* --qt */
+ qt_mode = true;
+ break;
+ case CHAR_MAX + 10: /* --csharp */
+ csharp_mode = true;
+ break;
+ case CHAR_MAX + 11: /* --csharp-resources */
+ csharp_resources_mode = true;
+ break;
+ case CHAR_MAX + 12: /* --use-untranslated (undocumented) */
+ include_untranslated = true;
+ break;
+ case CHAR_MAX + 13: /* --endianness={big|little} */
+ {
+ int endianness;
+
+ if (strcmp (optarg, "big") == 0)
+ endianness = 1;
+ else if (strcmp (optarg, "little") == 0)
+ endianness = 0;
+ else
+ error (EXIT_FAILURE, 0, _("invalid endianness: %s"), optarg);
+
+ byteswap = endianness ^ ENDIANNESS;
+ }
+ break;
+ case CHAR_MAX + 14: /* --source */
+ java_output_source = true;
+ break;
+ case CHAR_MAX + 15: /* --desktop */
+ desktop_mode = true;
+ break;
+ case CHAR_MAX + 16: /* --template=TEMPLATE */
+ desktop_template_name = optarg;
+ break;
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2010");
+ printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have a .po file name as argument. */
+ if (optind >= argc && !(desktop_mode && desktop_base_directory))
+ {
+ error (EXIT_SUCCESS, 0, _("no input file given"));
+ usage (EXIT_FAILURE);
+ }
+ if (optind < argc && desktop_mode && desktop_base_directory)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("no input file should be given if %s and %s are specified"),
+ "--desktop", "-d");
+ usage (EXIT_FAILURE);
+ }
+
+ /* Check for contradicting options. */
+ {
+ unsigned int modes =
+ (java_mode ? 1 : 0)
+ | (csharp_mode ? 2 : 0)
+ | (csharp_resources_mode ? 4 : 0)
+ | (tcl_mode ? 8 : 0)
+ | (qt_mode ? 16 : 0)
+ | (desktop_mode ? 32 : 0);
+ static const char *mode_options[] =
+ { "--java", "--csharp", "--csharp-resources", "--tcl", "--qt",
+ "--desktop" };
+ /* More than one bit set? */
+ if (modes & (modes - 1))
+ {
+ const char *first_option;
+ const char *second_option;
+ unsigned int i;
+ for (i = 0; ; i++)
+ if (modes & (1 << i))
+ break;
+ first_option = mode_options[i];
+ for (i = i + 1; ; i++)
+ if (modes & (1 << i))
+ break;
+ second_option = mode_options[i];
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ first_option, second_option);
+ }
+ }
+ if (java_mode)
+ {
+ if (output_file_name != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--java", "--output-file");
+ }
+ if (java_class_directory == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-d directory\" specification"),
+ "--java");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else if (csharp_mode)
+ {
+ if (output_file_name != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--csharp", "--output-file");
+ }
+ if (csharp_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ if (csharp_base_directory == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-d directory\" specification"),
+ "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else if (tcl_mode)
+ {
+ if (output_file_name != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--tcl", "--output-file");
+ }
+ if (tcl_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ if (tcl_base_directory == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-d directory\" specification"),
+ "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else if (desktop_mode)
+ {
+ if (desktop_template_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"--template template\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ if (output_file_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-o file\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ if (desktop_base_directory != NULL && desktop_locale_name != NULL)
+ error (EXIT_FAILURE, 0,
+ _("%s and %s are mutually exclusive in %s"),
+ "-d", "-l", "--desktop");
+ if (desktop_base_directory == NULL && desktop_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--desktop");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ if (java_resource_name != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
+ "--resource", "--java", "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ if (java_locale_name != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s, %s or %s"),
+ "--locale", "--java", "--csharp", "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ if (java_class_directory != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s, %s or %s"),
+ "-d", "--java", "--csharp", "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (desktop_mode && desktop_default_keywords)
+ {
+ if (desktop_keywords.table == NULL)
+ hash_init (&desktop_keywords, 100);
+ desktop_add_default_keywords (&desktop_keywords);
+ }
+
+ /* Bulk processing mode for .desktop files.
+ Process all .po files in desktop_base_directory. */
+ if (desktop_mode && desktop_base_directory)
+ {
+ exit_status = msgfmt_desktop_bulk (desktop_base_directory,
+ desktop_template_name,
+ &desktop_keywords,
+ output_file_name);
+ if (desktop_keywords.table != NULL)
+ hash_destroy (&desktop_keywords);
+ exit (exit_status);
+ }
+
+ /* The -o option determines the name of the domain and therefore
+ the output file. */
+ if (output_file_name != NULL)
+ current_domain =
+ new_domain (output_file_name,
+ strict_uniforum && !csharp_resources_mode && !qt_mode
+ ? add_mo_suffix (output_file_name)
+ : output_file_name);
+
+ /* Process all given .po files. */
+ for (arg_i = optind; arg_i < argc; arg_i++)
+ {
+ /* Remember that we currently have not specified any domain. This
+ is of course not true when we saw the -o option. */
+ if (output_file_name == NULL)
+ current_domain = NULL;
+
+ /* And process the input file. */
+ read_catalog_file_msgfmt (argv[arg_i], input_syntax);
+ }
+
+ /* We know a priori that some input_syntax->parse() functions convert
+ strings to UTF-8. */
+ canon_encoding = (input_syntax->produces_utf8 ? po_charset_utf8 : NULL);
+
+ /* Remove obsolete messages. They were only needed for duplicate
+ checking. */
+ for (domain = domain_list; domain != NULL; domain = domain->next)
+ message_list_remove_if_not (domain->mlp, is_nonobsolete);
+
+ /* Perform all kinds of checks: plural expressions, format strings, ... */
+ {
+ int nerrors = 0;
+
+ for (domain = domain_list; domain != NULL; domain = domain->next)
+ nerrors +=
+ check_message_list (domain->mlp,
+ /* Untranslated and fuzzy messages have already
+ been dealt with during parsing, see below in
+ msgfmt_frob_new_message. */
+ 0, 0,
+ 1, check_format_strings, check_header,
+ check_compatibility,
+ check_accelerators, accelerator_char);
+
+ /* Exit with status 1 on any error. */
+ if (nerrors > 0)
+ {
+ error (0, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors",
+ nerrors),
+ nerrors);
+ exit_status = EXIT_FAILURE;
+ }
+ }
+
+ /* Now write out all domains. */
+ for (domain = domain_list; domain != NULL; domain = domain->next)
+ {
+ if (java_mode)
+ {
+ if (msgdomain_write_java (domain->mlp, canon_encoding,
+ java_resource_name, java_locale_name,
+ java_class_directory, assume_java2,
+ java_output_source))
+ exit_status = EXIT_FAILURE;
+ }
+ else if (csharp_mode)
+ {
+ if (msgdomain_write_csharp (domain->mlp, canon_encoding,
+ csharp_resource_name, csharp_locale_name,
+ csharp_base_directory))
+ exit_status = EXIT_FAILURE;
+ }
+ else if (csharp_resources_mode)
+ {
+ if (msgdomain_write_csharp_resources (domain->mlp, canon_encoding,
+ domain->domain_name,
+ domain->file_name))
+ exit_status = EXIT_FAILURE;
+ }
+ else if (tcl_mode)
+ {
+ if (msgdomain_write_tcl (domain->mlp, canon_encoding,
+ tcl_locale_name, tcl_base_directory))
+ exit_status = EXIT_FAILURE;
+ }
+ else if (qt_mode)
+ {
+ if (msgdomain_write_qt (domain->mlp, canon_encoding,
+ domain->domain_name, domain->file_name))
+ exit_status = EXIT_FAILURE;
+ }
+ else if (desktop_mode)
+ {
+ if (msgdomain_write_desktop (domain->mlp, canon_encoding,
+ desktop_locale_name,
+ desktop_template_name,
+ &desktop_keywords,
+ domain->file_name))
+ exit_status = EXIT_FAILURE;
+
+ if (desktop_keywords.table != NULL)
+ hash_destroy (&desktop_keywords);
+ }
+ else
+ {
+ if (msgdomain_write_mo (domain->mlp, domain->domain_name,
+ domain->file_name))
+ exit_status = EXIT_FAILURE;
+ }
+
+ /* List is not used anymore. */
+ message_list_free (domain->mlp, 0);
+ }
+
+ /* Print statistics if requested. */
+ if (verbose || do_statistics)
+ {
+ if (do_statistics + verbose >= 2 && optind < argc)
+ {
+ /* Print the input file name(s) in front of the statistics line. */
+ char *all_input_file_names;
+
+ {
+ string_list_ty input_file_names;
+
+ string_list_init (&input_file_names);;
+ for (arg_i = optind; arg_i < argc; arg_i++)
+ string_list_append (&input_file_names, argv[arg_i]);
+ all_input_file_names =
+ string_list_join (&input_file_names, ", ", '\0', false);
+ string_list_destroy (&input_file_names);
+ }
+
+ /* TRANSLATORS: The prefix before a statistics message. The argument
+ is a file name or a comma separated list of file names. */
+ fprintf (stderr, _("%s: "), all_input_file_names);
+ free (all_input_file_names);
+ }
+ fprintf (stderr,
+ ngettext ("%d translated message", "%d translated messages",
+ msgs_translated),
+ msgs_translated);
+ if (msgs_fuzzy > 0)
+ fprintf (stderr,
+ ngettext (", %d fuzzy translation", ", %d fuzzy translations",
+ msgs_fuzzy),
+ msgs_fuzzy);
+ if (msgs_untranslated > 0)
+ fprintf (stderr,
+ ngettext (", %d untranslated message",
+ ", %d untranslated messages",
+ msgs_untranslated),
+ msgs_untranslated);
+ fputs (".\n", stderr);
+ }
+
+ exit (exit_status);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] filename.po ...\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Generate binary message catalog from textual translation description.\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+Similarly for optional arguments.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ filename.po ... input files\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If input file is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Operation mode:\n"));
+ printf (_("\
+ -j, --java Java mode: generate a Java ResourceBundle class\n"));
+ printf (_("\
+ --java2 like --java, and assume Java2 (JDK 1.2 or higher)\n"));
+ printf (_("\
+ --csharp C# mode: generate a .NET .dll file\n"));
+ printf (_("\
+ --csharp-resources C# resources mode: generate a .NET .resources file\n"));
+ printf (_("\
+ --tcl Tcl mode: generate a tcl/msgcat .msg file\n"));
+ printf (_("\
+ --qt Qt mode: generate a Qt .qm file\n"));
+ printf (_("\
+ --desktop Desktop Entry mode: generate a .desktop file\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+ --strict enable strict Uniforum mode\n"));
+ printf (_("\
+If output file is -, output is written to standard output.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location in Java mode:\n"));
+ printf (_("\
+ -r, --resource=RESOURCE resource name\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ --source produce a .java file, instead of a .class file\n"));
+ printf (_("\
+ -d DIRECTORY base directory of classes directory hierarchy\n"));
+ printf (_("\
+The class name is determined by appending the locale name to the resource name,\n\
+separated with an underscore. The -d option is mandatory. The class is\n\
+written under the specified directory.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Output file location in C# mode:\n"));
+ printf (_("\
+ -r, --resource=RESOURCE resource name\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -d DIRECTORY base directory for locale dependent .dll files\n"));
+ printf (_("\
+The -l and -d options are mandatory. The .dll file is written in a\n\
+subdirectory of the specified directory whose name depends on the locale.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location in Tcl mode:\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -d DIRECTORY base directory of .msg message catalogs\n"));
+ printf (_("\
+The -l and -d options are mandatory. The .msg file is written in the\n\
+specified directory.\n"));
+ printf ("\n");
+ printf (_("\
+Desktop Entry mode options:\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+ --template=TEMPLATE a .desktop file used as a template\n"));
+ printf (_("\
+ -d DIRECTORY base directory of .po files\n"));
+ printf (_("\
+ -kWORD, --keyword=WORD look for WORD as an additional keyword\n\
+ -k, --keyword do not to use default keywords\n"));
+ printf (_("\
+The -l, -o, and --template options are mandatory. If -D is specified, input\n\
+files are read from the directory instead of the command line arguments.\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
+ printf ("\n");
+ printf (_("\
+Input file interpretation:\n"));
+ printf (_("\
+ -c, --check perform all the checks implied by\n\
+ --check-format, --check-header, --check-domain\n"));
+ printf (_("\
+ --check-format check language dependent format strings\n"));
+ printf (_("\
+ --check-header verify presence and contents of the header entry\n"));
+ printf (_("\
+ --check-domain check for conflicts between domain directives\n\
+ and the --output-file option\n"));
+ printf (_("\
+ -C, --check-compatibility check that GNU msgfmt behaves like X/Open msgfmt\n"));
+ printf (_("\
+ --check-accelerators[=CHAR] check presence of keyboard accelerators for\n\
+ menu items\n"));
+ printf (_("\
+ -f, --use-fuzzy use fuzzy entries in output\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ -a, --alignment=NUMBER align strings to NUMBER bytes (default: %d)\n"), DEFAULT_OUTPUT_ALIGNMENT);
+ printf (_("\
+ --endianness=BYTEORDER write out 32-bit numbers in the given byte order\n\
+ (big or little, default depends on platform)\n"));
+ printf (_("\
+ --no-hash binary file will not include the hash table\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf (_("\
+ --statistics print statistics about translations\n"));
+ printf (_("\
+ -v, --verbose increase verbosity level\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
+ }
+
+ exit (status);
+}
+
+
+static const char *
+add_mo_suffix (const char *fname)
+{
+ size_t len;
+ char *result;
+
+ len = strlen (fname);
+ if (len > 3 && memcmp (fname + len - 3, ".mo", 3) == 0)
+ return fname;
+ if (len > 4 && memcmp (fname + len - 4, ".gmo", 4) == 0)
+ return fname;
+ result = XNMALLOC (len + 4, char);
+ stpcpy (stpcpy (result, fname), ".mo");
+ return result;
+}
+
+
+static struct msg_domain *
+new_domain (const char *name, const char *file_name)
+{
+ struct msg_domain **p_dom = &domain_list;
+
+ while (*p_dom != NULL && strcmp (name, (*p_dom)->domain_name) != 0)
+ p_dom = &(*p_dom)->next;
+
+ if (*p_dom == NULL)
+ {
+ struct msg_domain *domain;
+
+ domain = XMALLOC (struct msg_domain);
+ domain->mlp = message_list_alloc (true);
+ domain->domain_name = name;
+ domain->file_name = file_name;
+ domain->next = NULL;
+ *p_dom = domain;
+ }
+
+ return *p_dom;
+}
+
+
+static bool
+is_nonobsolete (const message_ty *mp)
+{
+ return !mp->obsolete;
+}
+
+
+/* The rest of the file defines a subclass msgfmt_catalog_reader_ty of
+ default_catalog_reader_ty. Its particularities are:
+ - The header entry check is performed on-the-fly.
+ - Comments are not stored, they are discarded right away.
+ (This is achieved by setting handle_comments = false.)
+ - The multi-domain handling is adapted to our domain_list.
+ */
+
+
+/* This structure defines a derived class of the default_catalog_reader_ty
+ class. (See read-catalog-abstract.h for an explanation.) */
+typedef struct msgfmt_catalog_reader_ty msgfmt_catalog_reader_ty;
+struct msgfmt_catalog_reader_ty
+{
+ /* inherited instance variables, etc */
+ DEFAULT_CATALOG_READER_TY
+
+ bool has_header_entry;
+ bool has_nonfuzzy_header_entry;
+};
+
+
+/* Prepare for first message. */
+static void
+msgfmt_constructor (abstract_catalog_reader_ty *that)
+{
+ msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
+
+ /* Invoke superclass constructor. */
+ default_constructor (that);
+
+ this->has_header_entry = false;
+ this->has_nonfuzzy_header_entry = false;
+}
+
+
+/* Some checks after whole file is read. */
+static void
+msgfmt_parse_debrief (abstract_catalog_reader_ty *that)
+{
+ msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
+
+ /* Invoke superclass method. */
+ default_parse_debrief (that);
+
+ /* Test whether header entry was found. */
+ if (check_header)
+ {
+ if (!this->has_header_entry)
+ {
+ multiline_error (xasprintf ("%s: ", this->file_name),
+ xasprintf (_("\
+warning: PO file header missing or invalid\n")));
+ multiline_error (NULL,
+ xasprintf (_("\
+warning: charset conversion will not work\n")));
+ }
+ else if (!this->has_nonfuzzy_header_entry)
+ {
+ /* Has only a fuzzy header entry. Since the versions 0.10.xx
+ ignore a fuzzy header entry and even give an error on it, we
+ give a warning, to increase operability with these older
+ msgfmt versions. This warning can go away in January 2003. */
+ multiline_warning (xasprintf ("%s: ", this->file_name),
+ xasprintf (_("warning: PO file header fuzzy\n")));
+ multiline_warning (NULL,
+ xasprintf (_("\
+warning: older versions of msgfmt will give an error on this\n")));
+ }
+ }
+}
+
+
+/* Set 'domain' directive when seen in .po file. */
+static void
+msgfmt_set_domain (default_catalog_reader_ty *this, char *name)
+{
+ /* If no output file was given, we change it with each 'domain'
+ directive. */
+ if (!java_mode && !csharp_mode && !csharp_resources_mode && !tcl_mode
+ && !qt_mode && !desktop_mode && output_file_name == NULL)
+ {
+ size_t correct;
+
+ correct = strcspn (name, INVALID_PATH_CHAR);
+ if (name[correct] != '\0')
+ {
+ exit_status = EXIT_FAILURE;
+ if (correct == 0)
+ {
+ error (0, 0, _("\
+domain name \"%s\" not suitable as file name"), name);
+ return;
+ }
+ else
+ error (0, 0, _("\
+domain name \"%s\" not suitable as file name: will use prefix"), name);
+ name[correct] = '\0';
+ }
+
+ /* Set new domain. */
+ current_domain = new_domain (name, add_mo_suffix (name));
+ this->domain = current_domain->domain_name;
+ this->mlp = current_domain->mlp;
+ }
+ else
+ {
+ if (check_domain)
+ po_gram_error_at_line (&gram_pos,
+ _("'domain %s' directive ignored"), name);
+
+ /* NAME was allocated in po-gram-gen.y but is not used anywhere. */
+ free (name);
+ }
+}
+
+
+static void
+msgfmt_add_message (default_catalog_reader_ty *this,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ /* Check whether already a domain is specified. If not, use default
+ domain. */
+ if (current_domain == NULL)
+ {
+ current_domain = new_domain (MESSAGE_DOMAIN_DEFAULT,
+ add_mo_suffix (MESSAGE_DOMAIN_DEFAULT));
+ /* Keep current_domain and this->domain synchronized. */
+ this->domain = current_domain->domain_name;
+ this->mlp = current_domain->mlp;
+ }
+
+ /* Invoke superclass method. */
+ default_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+
+static void
+msgfmt_frob_new_message (default_catalog_reader_ty *that, message_ty *mp,
+ const lex_pos_ty *msgid_pos,
+ const lex_pos_ty *msgstr_pos)
+{
+ msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
+
+ if (!mp->obsolete)
+ {
+ /* Don't emit untranslated entries.
+ Also don't emit fuzzy entries, unless --use-fuzzy was specified.
+ But ignore fuzziness of the header entry. */
+ if ((!include_untranslated && mp->msgstr[0] == '\0')
+ || (!include_fuzzies && mp->is_fuzzy && !is_header (mp)))
+ {
+ if (check_compatibility)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0, mp->pos.file_name, mp->pos.line_number,
+ (mp->msgstr[0] == '\0'
+ ? _("empty 'msgstr' entry ignored")
+ : _("fuzzy 'msgstr' entry ignored")));
+ error_with_progname = true;
+ }
+
+ /* Increment counter for fuzzy/untranslated messages. */
+ if (mp->msgstr[0] == '\0')
+ ++msgs_untranslated;
+ else
+ ++msgs_fuzzy;
+
+ mp->obsolete = true;
+ }
+ else
+ {
+ /* Test for header entry. */
+ if (is_header (mp))
+ {
+ this->has_header_entry = true;
+ if (!mp->is_fuzzy)
+ this->has_nonfuzzy_header_entry = true;
+ }
+ else
+ /* We don't count the header entry in the statistic so place
+ the counter incrementation here. */
+ if (mp->is_fuzzy)
+ ++msgs_fuzzy;
+ else
+ ++msgs_translated;
+ }
+ }
+}
+
+
+/* Test for '#, fuzzy' comments and warn. */
+static void
+msgfmt_comment_special (abstract_catalog_reader_ty *that, const char *s)
+{
+ msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
+
+ /* Invoke superclass method. */
+ default_comment_special (that, s);
+
+ if (this->is_fuzzy)
+ {
+ static bool warned = false;
+
+ if (!include_fuzzies && check_compatibility && !warned)
+ {
+ warned = true;
+ error (0, 0, _("\
+%s: warning: source file contains fuzzy translation"),
+ gram_pos.file_name);
+ }
+ }
+}
+
+
+/* So that the one parser can be used for multiple programs, and also
+ use good data hiding and encapsulation practices, an object
+ oriented approach has been taken. An object instance is allocated,
+ and all actions resulting from the parse will be through
+ invocations of method functions of that object. */
+
+static default_catalog_reader_class_ty msgfmt_methods =
+{
+ {
+ sizeof (msgfmt_catalog_reader_ty),
+ msgfmt_constructor,
+ default_destructor,
+ default_parse_brief,
+ msgfmt_parse_debrief,
+ default_directive_domain,
+ default_directive_message,
+ default_comment,
+ default_comment_dot,
+ default_comment_filepos,
+ msgfmt_comment_special
+ },
+ msgfmt_set_domain, /* set_domain */
+ msgfmt_add_message, /* add_message */
+ msgfmt_frob_new_message /* frob_new_message */
+};
+
+
+/* Read .po file FILENAME and store translation pairs. */
+static void
+read_catalog_file_msgfmt (char *filename, catalog_input_format_ty input_syntax)
+{
+ char *real_filename;
+ FILE *fp = open_catalog_file (filename, &real_filename, true);
+ default_catalog_reader_ty *pop;
+
+ pop = default_catalog_reader_alloc (&msgfmt_methods);
+ pop->handle_comments = false;
+ pop->allow_domain_directives = true;
+ pop->allow_duplicates = false;
+ pop->allow_duplicates_if_same_msgstr = false;
+ pop->file_name = real_filename;
+ pop->mdlp = NULL;
+ pop->mlp = NULL;
+ if (current_domain != NULL)
+ {
+ /* Keep current_domain and this->domain synchronized. */
+ pop->domain = current_domain->domain_name;
+ pop->mlp = current_domain->mlp;
+ }
+ po_lex_pass_obsolete_entries (true);
+ catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename,
+ filename, input_syntax);
+ catalog_reader_free ((abstract_catalog_reader_ty *) pop);
+
+ if (fp != stdin)
+ fclose (fp);
+}
+
+static void
+add_languages (string_list_ty *languages, string_list_ty *desired_languages,
+ const char *line, size_t length)
+{
+ char *start;
+
+ /* Split the line by whitespace and build the languages list. */
+ for (start = (char *) line; start - line < length; )
+ {
+ char *p;
+
+ /* Skip whitespace before the string. */
+ while (*start == ' ' || *start == '\t')
+ start++;
+
+ p = start;
+ while (*p != '\0' && *p != ' ' && *p != '\t')
+ p++;
+
+ *p = '\0';
+ if (desired_languages == NULL
+ || string_list_member (desired_languages, start))
+ string_list_append_unique (languages, start);
+ start = p + 1;
+ }
+}
+
+/* Compute the languages list by reading the "LINGUAS" envvar or the
+ LINGUAS file under DIRECTORY. */
+static string_list_ty *
+get_languages (const char *directory)
+{
+ char *envval;
+ string_list_ty *languages;
+ string_list_ty *desired_languages = NULL;
+ char *linguas_file_name;
+ struct stat statbuf;
+ FILE *fp;
+ size_t line_len = 0;
+ char *line_buf = NULL;
+
+ languages = string_list_alloc ();
+ envval = getenv ("LINGUAS");
+ if (envval)
+ {
+ desired_languages = string_list_alloc ();
+ add_languages (desired_languages, NULL, envval, strlen (envval));
+ }
+
+ linguas_file_name = xconcatenated_filename (directory, "LINGUAS", NULL);
+ if (stat (linguas_file_name, &statbuf) < 0)
+ {
+ error (EXIT_SUCCESS, 0, _("%s does not exist"), linguas_file_name);
+ string_list_free (languages);
+ if (desired_languages != NULL)
+ string_list_free (desired_languages);
+ free (linguas_file_name);
+ return NULL;
+ }
+
+ fp = fopen (linguas_file_name, "r");
+ if (fp == NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s exists but cannot read"),
+ linguas_file_name);
+ string_list_free (languages);
+ if (desired_languages != NULL)
+ string_list_free (desired_languages);
+ free (linguas_file_name);
+ return NULL;
+ }
+
+ while (!feof (fp))
+ {
+ /* Read next line from file. */
+ int len = getline (&line_buf, &line_len, fp);
+
+ /* In case of an error leave loop. */
+ if (len < 0)
+ break;
+
+ /* Remove trailing '\n' and trailing whitespace. */
+ if (len > 0 && line_buf[len - 1] == '\n')
+ line_buf[--len] = '\0';
+ while (len > 0
+ && (line_buf[len - 1] == ' '
+ || line_buf[len - 1] == '\t'
+ || line_buf[len - 1] == '\r'))
+ line_buf[--len] = '\0';
+
+ /* Test if we have to ignore the line. */
+ if (*line_buf == '\0' || *line_buf == '#')
+ continue;
+
+ add_languages (languages, desired_languages, line_buf, len);
+ }
+
+ free (line_buf);
+ fclose (fp);
+ if (desired_languages != NULL)
+ string_list_free (desired_languages);
+ free (linguas_file_name);
+
+ return languages;
+}
+
+/* Helper function to support 'bulk' operation mode of --desktop.
+ This reads all .po files in DIRECTORY and merges them into a
+ .desktop file FILE_NAME. Currently it does not support some
+ options available in 'iterative' mode, such as --statistics. */
+static int
+msgfmt_desktop_bulk (const char *directory,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ string_list_ty *languages = NULL;
+ message_list_ty **messages = NULL;
+ void *saved_dir_list;
+ int retval = 0;
+ size_t i;
+
+ languages = get_languages (directory);
+ if (!languages)
+ return EXIT_FAILURE;
+
+ /* Reset the directory search list so only .po files under DIRECTORY
+ will be read. */
+ saved_dir_list = dir_list_save_reset ();
+ dir_list_append (directory);
+
+ /* Read all .po files. */
+ messages = XNMALLOC (languages->nitems, message_list_ty *);
+ for (i = 0; i < languages->nitems; i++)
+ {
+ const char *language = languages->item[i];
+ char *input_file_name;
+ int nerrors;
+
+ current_domain = new_domain (file_name, file_name);
+
+ input_file_name = xconcatenated_filename ("", language, ".po");
+ read_catalog_file_msgfmt (input_file_name, &input_format_po);
+ free (input_file_name);
+
+ /* The domain directive is not supported by --desktop mode.
+ Thus, domain_list should always contain a single domain. */
+ assert (current_domain == domain_list && domain_list->next == NULL);
+ messages[i] = current_domain->mlp;
+ free (current_domain);
+ current_domain = domain_list = NULL;
+
+ /* Remove obsolete messages. They were only needed for duplicate
+ checking. */
+ message_list_remove_if_not (messages[i], is_nonobsolete);
+
+ /* Perform all kinds of checks: plural expressions, format
+ strings, ... */
+ nerrors =
+ check_message_list (messages[i],
+ /* Untranslated and fuzzy messages have already
+ been dealt with during parsing, see below in
+ msgfmt_frob_new_message. */
+ 0, 0,
+ 1, check_format_strings, check_header,
+ check_compatibility,
+ check_accelerators, accelerator_char);
+
+ /* Exit with status 1 on any error. */
+ if (nerrors > 0)
+ {
+ error (0, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors",
+ nerrors),
+ nerrors);
+ retval = EXIT_FAILURE;
+ goto out;
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (messages[i], NULL, po_charset_utf8, NULL);
+ }
+
+ /* Write the messages into .desktop file. */
+ if (msgdomain_write_desktop_bulk (languages,
+ messages,
+ template_file_name,
+ keywords,
+ file_name))
+ {
+ retval = EXIT_FAILURE;
+ goto out;
+ }
+
+ out:
+ dir_list_restore (saved_dir_list);
+ for (i = 0; i < languages->nitems; i++)
+ message_list_free (messages[i], 0);
+ free (messages);
+ string_list_free (languages);
+
+ return retval;
+}
diff --git a/gettext-tools/src/msgfmt.cs b/gettext-tools/src/msgfmt.cs
new file mode 100644
index 0000000..e2f9e7d
--- /dev/null
+++ b/gettext-tools/src/msgfmt.cs
@@ -0,0 +1,119 @@
+/* GNU gettext for C#
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Written by Bruno Haible <bruno@clisp.org>, 2003.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This program creates a .resources file from a set of key/value pairs given
+ * on standard input.
+ */
+
+using System; /* String, Console, Exception */
+using System.IO; /* Stream, BufferedStream, StreamReader */
+using System.Text; /* StringBuilder, UTF8Encoding */
+using System.Resources; /* ResourceWriter */
+
+namespace GNU.Gettext {
+ public class WriteResource {
+ private StreamReader reader;
+ // Read a NUL-terminated UTF-8 encoded string.
+ private String ReadString () {
+ StringBuilder b = new StringBuilder();
+ for (;;) {
+ int c = reader.Read();
+ if (c < 0) // EOF?
+ return null;
+ if (c == 0) // End of String?
+ break;
+ b.Append((char)c);
+ }
+ return b.ToString();
+ }
+ // Read all msgid/msgstr pairs, register them in the ResourceWriter,
+ // and write the binary contents to the output stream.
+ private void ReadAllInput (ResourceWriter rw) {
+ for (;;) {
+ String msgid = ReadString();
+ if (msgid == null)
+ break;
+ String msgstr = ReadString();
+ if (msgstr == null)
+ break;
+ rw.AddResource(msgid, msgstr);
+ }
+ rw.Generate();
+ }
+ // Read all msgid/msgstr pairs (each string being NUL-terminated and
+ // UTF-8 encoded) and write the .resources file to the given filename.
+ WriteResource (String filename) {
+ Stream input = new BufferedStream(Console.OpenStandardInput());
+ reader = new StreamReader(input, new UTF8Encoding());
+ if (filename.Equals("-")) {
+ BufferedStream output = new BufferedStream(Console.OpenStandardOutput());
+ // A temporary output stream is needed because ResourceWriter.Generate
+ // expects to be able to seek in the Stream.
+ MemoryStream tmpoutput = new MemoryStream();
+ ResourceWriter rw = new ResourceWriter(tmpoutput);
+ ReadAllInput(rw);
+#if __CSCC__
+ // Use the ResourceReader to check against pnet-0.6.0 ResourceWriter
+ // bug.
+ try {
+ ResourceReader rr = new ResourceReader(new MemoryStream(tmpoutput.ToArray()));
+ foreach (System.Collections.DictionaryEntry entry in rr);
+ } catch (IOException e) {
+ throw new Exception("class ResourceWriter is buggy", e);
+ }
+#endif
+ tmpoutput.WriteTo(output);
+ rw.Close();
+ output.Close();
+ } else {
+#if __CSCC__
+ MemoryStream tmpoutput = new MemoryStream();
+ ResourceWriter rw = new ResourceWriter(tmpoutput);
+ ReadAllInput(rw);
+ // Use the ResourceReader to check against pnet-0.6.0 ResourceWriter
+ // bug.
+ try {
+ ResourceReader rr = new ResourceReader(new MemoryStream(tmpoutput.ToArray()));
+ foreach (System.Collections.DictionaryEntry entry in rr);
+ } catch (IOException e) {
+ throw new Exception("class ResourceWriter is buggy", e);
+ }
+ BufferedStream output = new BufferedStream(new FileStream(filename, FileMode.Create, FileAccess.Write));
+ tmpoutput.WriteTo(output);
+ rw.Close();
+ output.Close();
+#else
+ ResourceWriter rw = new ResourceWriter(filename);
+ ReadAllInput(rw);
+ rw.Close();
+#endif
+ }
+ }
+ public static int Main (String[] args) {
+ try {
+ new WriteResource(args[0]);
+ } catch (Exception e) {
+ Console.Error.WriteLine(e);
+ Console.Error.WriteLine(e.StackTrace);
+ return 1;
+ }
+ return 0;
+ }
+ }
+}
diff --git a/gettext-tools/src/msgfmt.h b/gettext-tools/src/msgfmt.h
new file mode 100644
index 0000000..e61f72c
--- /dev/null
+++ b/gettext-tools/src/msgfmt.h
@@ -0,0 +1,27 @@
+/* msgfmt specifics
+ Copyright (C) 1995-1998, 2000-2001, 2009 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGFMT_H
+#define _MSGFMT_H
+
+/* Be more verbose. Use only 'fprintf' and 'multiline_warning' but not
+ 'error' or 'multiline_error' to emit verbosity messages, because 'error'
+ and 'multiline_error' during PO file parsing cause the program to exit
+ with EXIT_FAILURE. See function lex_end(). */
+extern int verbose;
+
+#endif /* _MSGFMT_H */
diff --git a/gettext-tools/src/msggrep.c b/gettext-tools/src/msggrep.c
new file mode 100644
index 0000000..73ccb94
--- /dev/null
+++ b/gettext-tools/src/msggrep.c
@@ -0,0 +1,860 @@
+/* Extract some translations of a translation catalog.
+ Copyright (C) 2001-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#if defined _MSC_VER || defined __MINGW32__
+# include <io.h>
+#endif
+
+#include <fnmatch.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "str-list.h"
+#include "msgl-charset.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "libgrep.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Output only non-matching messages. */
+static bool invert_match = false;
+
+/* Selected source files. */
+static string_list_ty *location_files;
+
+/* Selected domain names. */
+static string_list_ty *domain_names;
+
+/* Task for each grep pass. */
+struct grep_task {
+ matcher_t *matcher;
+ size_t pattern_count;
+ char *patterns;
+ size_t patterns_size;
+ bool case_insensitive;
+ void *compiled_patterns;
+};
+static struct grep_task grep_task[5];
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 9 },
+ { "comment", no_argument, NULL, 'C' },
+ { "directory", required_argument, NULL, 'D' },
+ { "domain", required_argument, NULL, 'M' },
+ { "escape", no_argument, NULL, CHAR_MAX + 1 },
+ { "extended-regexp", no_argument, NULL, 'E' },
+ { "extracted-comment", no_argument, NULL, 'X' },
+ { "file", required_argument, NULL, 'f' },
+ { "fixed-strings", no_argument, NULL, 'F' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "ignore-case", no_argument, NULL, 'i' },
+ { "indent", no_argument, NULL, CHAR_MAX + 2 },
+ { "invert-match", no_argument, NULL, 'v' },
+ { "location", required_argument, NULL, 'N' },
+ { "msgctxt", no_argument, NULL, 'J' },
+ { "msgid", no_argument, NULL, 'K' },
+ { "msgstr", no_argument, NULL, 'T' },
+ { "no-escape", no_argument, NULL, CHAR_MAX + 3 },
+ { "no-location", no_argument, NULL, CHAR_MAX + 11 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 6 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "regexp", required_argument, NULL, 'e' },
+ { "sort-by-file", no_argument, NULL, CHAR_MAX + 4 },
+ { "sort-output", no_argument, NULL, CHAR_MAX + 5 },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 7 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 8 },
+ { "style", required_argument, NULL, CHAR_MAX + 10 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void no_pass (int opt)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static msgdomain_list_ty *process_msgdomain_list (msgdomain_list_ty *mdlp);
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ int grep_pass;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_filepos = false;
+ bool sort_by_msgid = false;
+ size_t i;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+ grep_pass = -1;
+ location_files = string_list_alloc ();
+ domain_names = string_list_alloc ();
+
+ for (i = 0; i < 5; i++)
+ {
+ struct grep_task *gt = &grep_task[i];
+
+ gt->matcher = &matcher_grep;
+ gt->pattern_count = 0;
+ gt->patterns = NULL;
+ gt->patterns_size = 0;
+ gt->case_insensitive = false;
+ }
+
+ while ((opt = getopt_long (argc, argv, "CD:e:Ef:FhiJKM:n:N:o:pPTvVw:X",
+ long_options, NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'C':
+ grep_pass = 3;
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ if (grep_pass < 0)
+ no_pass (opt);
+ {
+ struct grep_task *gt = &grep_task[grep_pass];
+ /* Append optarg and a newline to gt->patterns. */
+ size_t len = strlen (optarg);
+ gt->patterns =
+ (char *) xrealloc (gt->patterns, gt->patterns_size + len + 1);
+ memcpy (gt->patterns + gt->patterns_size, optarg, len);
+ gt->patterns_size += len;
+ *(gt->patterns + gt->patterns_size) = '\n';
+ gt->patterns_size += 1;
+ gt->pattern_count++;
+ }
+ break;
+
+ case 'E':
+ if (grep_pass < 0)
+ no_pass (opt);
+ grep_task[grep_pass].matcher = &matcher_egrep;
+ break;
+
+ case 'f':
+ if (grep_pass < 0)
+ no_pass (opt);
+ {
+ struct grep_task *gt = &grep_task[grep_pass];
+ /* Append the contents of the specified file to gt->patterns. */
+ FILE *fp = fopen (optarg, "r");
+
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("\
+error while opening \"%s\" for reading"), optarg);
+
+ while (!feof (fp))
+ {
+ char buf[4096];
+ size_t count = fread (buf, 1, sizeof buf, fp);
+
+ if (count == 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), optarg);
+ /* EOF reached. */
+ break;
+ }
+
+ gt->patterns =
+ (char *) xrealloc (gt->patterns, gt->patterns_size + count);
+ memcpy (gt->patterns + gt->patterns_size, buf, count);
+ gt->patterns_size += count;
+ }
+
+ /* Append a final newline if file ended in a non-newline. */
+ if (gt->patterns_size > 0
+ && *(gt->patterns + gt->patterns_size - 1) != '\n')
+ {
+ gt->patterns =
+ (char *) xrealloc (gt->patterns, gt->patterns_size + 1);
+ *(gt->patterns + gt->patterns_size) = '\n';
+ gt->patterns_size += 1;
+ }
+
+ fclose (fp);
+ gt->pattern_count++;
+ }
+ break;
+
+ case 'F':
+ if (grep_pass < 0)
+ no_pass (opt);
+ grep_task[grep_pass].matcher = &matcher_fgrep;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ if (grep_pass < 0)
+ no_pass (opt);
+ grep_task[grep_pass].case_insensitive = true;
+ break;
+
+ case 'J':
+ grep_pass = 0;
+ break;
+
+ case 'K':
+ grep_pass = 1;
+ break;
+
+ case 'M':
+ string_list_append (domain_names, optarg);
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'N':
+ string_list_append (location_files, optarg);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'T':
+ grep_pass = 2;
+ break;
+
+ case 'v':
+ invert_match = true;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case 'X':
+ grep_pass = 4;
+ break;
+
+ case CHAR_MAX + 1:
+ message_print_style_escape (true);
+ break;
+
+ case CHAR_MAX + 2:
+ message_print_style_indent ();
+ break;
+
+ case CHAR_MAX + 3:
+ message_print_style_escape (false);
+ break;
+
+ case CHAR_MAX + 4:
+ sort_by_filepos = true;
+ break;
+
+ case CHAR_MAX + 5:
+ sort_by_msgid = true;
+ break;
+
+ case CHAR_MAX + 6: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 7: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 8: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 9: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 10: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 11: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind == argc)
+ input_file = "-";
+ else if (optind + 1 == argc)
+ input_file = argv[optind];
+ else
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Compile the patterns. */
+ for (grep_pass = 0; grep_pass < 5; grep_pass++)
+ {
+ struct grep_task *gt = &grep_task[grep_pass];
+
+ if (gt->pattern_count > 0)
+ {
+ if (gt->patterns_size > 0)
+ {
+ /* Strip trailing newline. */
+ assert (gt->patterns[gt->patterns_size - 1] == '\n');
+ gt->patterns_size--;
+ }
+ gt->compiled_patterns =
+ gt->matcher->compile (gt->patterns, gt->patterns_size,
+ gt->case_insensitive, false, false, '\n');
+ }
+ }
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ if (grep_task[0].pattern_count > 0
+ || grep_task[1].pattern_count > 0
+ || grep_task[2].pattern_count > 0
+ || grep_task[3].pattern_count > 0
+ || grep_task[4].pattern_count > 0)
+ {
+ /* Warn if the current locale is not suitable for this PO file. */
+ compare_po_locale_charsets (result);
+ }
+
+ /* Select the messages. */
+ result = process_msgdomain_list (result);
+
+ /* Sort the results. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+static void
+no_pass (int opt)
+{
+ error (EXIT_SUCCESS, 0,
+ _("option '%c' cannot be used before 'J' or 'K' or 'T' or 'C' or 'X' has been specified"),
+ opt);
+ usage (EXIT_FAILURE);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Extracts all messages of a translation catalog that match a given pattern\n\
+or belong to some given source files.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Message selection:\n\
+ [-N SOURCEFILE]... [-M DOMAINNAME]...\n\
+ [-J MSGCTXT-PATTERN] [-K MSGID-PATTERN] [-T MSGSTR-PATTERN]\n\
+ [-C COMMENT-PATTERN] [-X EXTRACTED-COMMENT-PATTERN]\n\
+A message is selected if it comes from one of the specified source files,\n\
+or if it comes from one of the specified domains,\n\
+or if -J is given and its context (msgctxt) matches MSGCTXT-PATTERN,\n\
+or if -K is given and its key (msgid or msgid_plural) matches MSGID-PATTERN,\n\
+or if -T is given and its translation (msgstr) matches MSGSTR-PATTERN,\n\
+or if -C is given and the translator's comment matches COMMENT-PATTERN,\n\
+or if -X is given and the extracted comment matches EXTRACTED-COMMENT-PATTERN.\n\
+\n\
+When more than one selection criterion is specified, the set of selected\n\
+messages is the union of the selected messages of each criterion.\n\
+\n\
+MSGCTXT-PATTERN or MSGID-PATTERN or MSGSTR-PATTERN or COMMENT-PATTERN or\n\
+EXTRACTED-COMMENT-PATTERN syntax:\n\
+ [-E | -F] [-e PATTERN | -f FILE]...\n\
+PATTERNs are basic regular expressions by default, or extended regular\n\
+expressions if -E is given, or fixed strings if -F is given.\n\
+\n\
+ -N, --location=SOURCEFILE select messages extracted from SOURCEFILE\n\
+ -M, --domain=DOMAINNAME select messages belonging to domain DOMAINNAME\n\
+ -J, --msgctxt start of patterns for the msgctxt\n\
+ -K, --msgid start of patterns for the msgid\n\
+ -T, --msgstr start of patterns for the msgstr\n\
+ -C, --comment start of patterns for the translator's comment\n\
+ -X, --extracted-comment start of patterns for the extracted comment\n\
+ -E, --extended-regexp PATTERN is an extended regular expression\n\
+ -F, --fixed-strings PATTERN is a set of newline-separated strings\n\
+ -e, --regexp=PATTERN use PATTERN as a regular expression\n\
+ -f, --file=FILE obtain PATTERN from FILE\n\
+ -i, --ignore-case ignore case distinctions\n\
+ -v, --invert-match output only the messages that do not match any\n\
+ selection criterion\n\
+"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ --indent indented output style\n"));
+ printf (_("\
+ --no-location suppress '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location preserve '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict strict Uniforum output style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ --sort-output generate sorted output\n"));
+ printf (_("\
+ --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Return 1 if FILENAME is contained in a list of filename patterns,
+ 0 otherwise. */
+static bool
+filename_list_match (const string_list_ty *slp, const char *filename)
+{
+ size_t j;
+
+ for (j = 0; j < slp->nitems; ++j)
+ if (fnmatch (slp->item[j], filename, FNM_PATHNAME) == 0)
+ return true;
+ return false;
+}
+
+
+#ifdef EINTR
+
+/* EINTR handling for close().
+ These functions can return -1/EINTR even though we don't have any
+ signal handlers set up, namely when we get interrupted via SIGSTOP. */
+
+static inline int
+nonintr_close (int fd)
+{
+ int retval;
+
+ do
+ retval = close (fd);
+ while (retval < 0 && errno == EINTR);
+
+ return retval;
+}
+#define close nonintr_close
+
+#endif
+
+
+/* Process a string STR of size LEN bytes through grep, and return true
+ if it matches. */
+static bool
+is_string_selected (int grep_pass, const char *str, size_t len)
+{
+ const struct grep_task *gt = &grep_task[grep_pass];
+
+ if (gt->pattern_count > 0)
+ {
+ size_t match_size;
+ size_t match_offset;
+
+ match_offset =
+ gt->matcher->execute (gt->compiled_patterns, str, len,
+ &match_size, false);
+ return (match_offset != (size_t) -1);
+ }
+ else
+ return 0;
+}
+
+
+/* Return true if a message matches, considering only the positive selection
+ criteria and ignoring --invert-match. */
+static bool
+is_message_selected_no_invert (const message_ty *mp)
+{
+ size_t i;
+ const char *msgstr;
+ size_t msgstr_len;
+ const char *p;
+
+ /* Test whether one of mp->filepos[] is selected. */
+ for (i = 0; i < mp->filepos_count; i++)
+ if (filename_list_match (location_files, mp->filepos[i].file_name))
+ return true;
+
+ /* Test msgctxt using the --msgctxt arguments. */
+ if (mp->msgctxt != NULL
+ && is_string_selected (0, mp->msgctxt, strlen (mp->msgctxt)))
+ return true;
+
+ /* Test msgid and msgid_plural using the --msgid arguments. */
+ if (is_string_selected (1, mp->msgid, strlen (mp->msgid)))
+ return true;
+ if (mp->msgid_plural != NULL
+ && is_string_selected (1, mp->msgid_plural, strlen (mp->msgid_plural)))
+ return true;
+
+ /* Test msgstr using the --msgstr arguments. */
+ msgstr = mp->msgstr;
+ msgstr_len = mp->msgstr_len;
+ /* Process each NUL delimited substring separately. */
+ for (p = msgstr; p < msgstr + msgstr_len; )
+ {
+ size_t length = strlen (p);
+
+ if (is_string_selected (2, p, length))
+ return true;
+
+ p += length + 1;
+ }
+
+ /* Test translator comments using the --comment arguments. */
+ if (grep_task[3].pattern_count > 0
+ && mp->comment != NULL && mp->comment->nitems > 0)
+ {
+ size_t length;
+ char *total_comment;
+ char *q;
+ size_t j;
+ bool selected;
+
+ length = 0;
+ for (j = 0; j < mp->comment->nitems; j++)
+ length += strlen (mp->comment->item[j]) + 1;
+ total_comment = (char *) xmalloca (length);
+
+ q = total_comment;
+ for (j = 0; j < mp->comment->nitems; j++)
+ {
+ size_t l = strlen (mp->comment->item[j]);
+
+ memcpy (q, mp->comment->item[j], l);
+ q += l;
+ *q++ = '\n';
+ }
+ if (q != total_comment + length)
+ abort ();
+
+ selected = is_string_selected (3, total_comment, length);
+
+ freea (total_comment);
+
+ if (selected)
+ return true;
+ }
+
+ /* Test extracted comments using the --extracted-comment arguments. */
+ if (grep_task[4].pattern_count > 0
+ && mp->comment_dot != NULL && mp->comment_dot->nitems > 0)
+ {
+ size_t length;
+ char *total_comment;
+ char *q;
+ size_t j;
+ bool selected;
+
+ length = 0;
+ for (j = 0; j < mp->comment_dot->nitems; j++)
+ length += strlen (mp->comment_dot->item[j]) + 1;
+ total_comment = (char *) xmalloca (length);
+
+ q = total_comment;
+ for (j = 0; j < mp->comment_dot->nitems; j++)
+ {
+ size_t l = strlen (mp->comment_dot->item[j]);
+
+ memcpy (q, mp->comment_dot->item[j], l);
+ q += l;
+ *q++ = '\n';
+ }
+ if (q != total_comment + length)
+ abort ();
+
+ selected = is_string_selected (4, total_comment, length);
+
+ freea (total_comment);
+
+ if (selected)
+ return true;
+ }
+
+ return false;
+}
+
+
+/* Return true if a message matches. */
+static bool
+is_message_selected (const message_ty *mp)
+{
+ bool result;
+
+ /* Always keep the header entry. */
+ if (is_header (mp))
+ return true;
+
+ result = is_message_selected_no_invert (mp);
+
+ if (invert_match)
+ return !result;
+ else
+ return result;
+}
+
+
+static void
+process_message_list (const char *domain, message_list_ty *mlp)
+{
+ if (string_list_member (domain_names, domain))
+ /* Keep all the messages in the list. */
+ ;
+ else
+ /* Keep only the selected messages. */
+ message_list_remove_if_not (mlp, is_message_selected);
+}
+
+
+static msgdomain_list_ty *
+process_msgdomain_list (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ process_message_list (mdlp->item[k]->domain, mdlp->item[k]->messages);
+
+ return mdlp;
+}
diff --git a/gettext-tools/src/msginit.c b/gettext-tools/src/msginit.c
new file mode 100644
index 0000000..951aa2e
--- /dev/null
+++ b/gettext-tools/src/msginit.c
@@ -0,0 +1,1776 @@
+/* Initializes a new PO file.
+ Copyright (C) 2001-2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#include <unistd.h>
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#if HAVE_DIRENT_H
+# define HAVE_DIR 1
+#else
+# define HAVE_DIR 0
+#endif
+
+#include "closeout.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "c-strstr.h"
+#include "c-strcase.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "po-charset.h"
+#include "localcharset.h"
+#include "localename.h"
+#include "po-time.h"
+#include "plural-table.h"
+#include "lang-table.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "concat-filename.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "msgl-english.h"
+#include "plural-count.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "xsetenv.h"
+#include "str-list.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+#define N_(str) (str)
+
+/* Get F_OK. It is lacking from <fcntl.h> on Woe32. */
+#ifndef F_OK
+# define F_OK 0
+#endif
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+extern const char * _nl_expand_alias (const char *name);
+
+/* Locale name. */
+static const char *locale;
+
+/* Language (ISO-639 code) and optional territory (ISO-3166 code). */
+static const char *catalogname;
+
+/* Language (ISO-639 code). */
+static const char *language;
+
+/* If true, the user is not considered to be the translator. */
+static bool no_translator;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "color", optional_argument, NULL, CHAR_MAX + 5 },
+ { "help", no_argument, NULL, 'h' },
+ { "input", required_argument, NULL, 'i' },
+ { "locale", required_argument, NULL, 'l' },
+ { "no-translator", no_argument, NULL, CHAR_MAX + 1 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
+ { "style", required_argument, NULL, CHAR_MAX + 6 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w' },
+ { NULL, 0, NULL, 0 }
+};
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static const char *find_pot (void);
+static const char *catalogname_for_locale (const char *locale);
+static const char *language_of_locale (const char *locale);
+static char *get_field (const char *header, const char *field);
+static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp);
+static msgdomain_list_ty *update_msgstr_plurals (msgdomain_list_ty *mdlp);
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+ locale = NULL;
+
+ while ((opt = getopt_long (argc, argv, "hi:l:o:pPVw:", long_options, NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ if (input_file != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+ input_file = optarg;
+ break;
+
+ case 'l':
+ locale = optarg;
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1:
+ no_translator = true;
+ break;
+
+ case CHAR_MAX + 2: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 6: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test for extraneous arguments. */
+ if (optind != argc)
+ error (EXIT_FAILURE, 0, _("too many arguments"));
+
+ /* Search for the input file. */
+ if (input_file == NULL)
+ input_file = find_pot ();
+
+ /* Determine target locale. */
+ if (locale == NULL)
+ {
+ locale = gl_locale_name (LC_MESSAGES, "LC_MESSAGES");
+ if (strcmp (locale, "C") == 0)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+You are in a language indifferent environment. Please set\n\
+your LANG environment variable, as described in the ABOUT-NLS\n\
+file. This is necessary so you can test your translations.\n")));
+ exit (EXIT_FAILURE);
+ }
+ }
+ {
+ const char *alias = _nl_expand_alias (locale);
+ if (alias != NULL)
+ locale = alias;
+ }
+ catalogname = catalogname_for_locale (locale);
+ language = language_of_locale (locale);
+
+ /* Default output file name is CATALOGNAME.po. */
+ if (output_file == NULL)
+ {
+ output_file = xasprintf ("%s.po", catalogname);
+
+ /* But don't overwrite existing PO files. */
+ if (access (output_file, F_OK) == 0)
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+Output file %s already exists.\n\
+Please specify the locale through the --locale option or\n\
+the output .po file through the --output-file option.\n"),
+ output_file));
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Read input file. */
+ result = read_catalog_file (input_file, input_syntax);
+
+ /* Fill the header entry. */
+ result = fill_header (result);
+
+ /* Initialize translations. */
+ if (strcmp (language, "en") == 0)
+ result = msgdomain_list_english (result);
+ else
+ result = update_msgstr_plurals (result);
+
+ /* Write the modified message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, true, false);
+
+ if (!no_translator)
+ fprintf (stderr, "\n");
+ fprintf (stderr, _("Created %s.\n"), output_file);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Creates a new PO file, initializing the meta information with values from the\n\
+user's environment.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ -i, --input=INPUTFILE input POT file\n"));
+ printf (_("\
+If no input file is given, the current directory is searched for the POT file.\n\
+If it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified PO file\n"));
+ printf (_("\
+If no output file is given, it depends on the --locale option or the user's\n\
+locale setting. If it is -, the results are written to standard output.\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ -l, --locale=LL_CC set target locale\n"));
+ printf (_("\
+ --no-translator assume the PO file is automatically generated\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Search for the POT file and return its name. */
+static const char *
+find_pot ()
+{
+#if HAVE_DIR
+ DIR *dirp;
+ char *found = NULL;
+
+ dirp = opendir (".");
+ if (dirp != NULL)
+ {
+ for (;;)
+ {
+ struct dirent *dp;
+
+ errno = 0;
+ dp = readdir (dirp);
+ if (dp != NULL)
+ {
+ const char *name = dp->d_name;
+ size_t namlen = strlen (name);
+
+ if (namlen > 4 && memcmp (name + namlen - 4, ".pot", 4) == 0)
+ {
+ if (found == NULL)
+ found = xstrdup (name);
+ else
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+Found more than one .pot file.\n\
+Please specify the input .pot file through the --input option.\n")));
+ usage (EXIT_FAILURE);
+ }
+ }
+ }
+ else if (errno != 0)
+ error (EXIT_FAILURE, errno, _("error reading current directory"));
+ else
+ break;
+ }
+ if (closedir (dirp))
+ error (EXIT_FAILURE, errno, _("error reading current directory"));
+
+ if (found != NULL)
+ return found;
+ }
+#endif
+
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+Found no .pot file in the current directory.\n\
+Please specify the input .pot file through the --input option.\n")));
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/* Return the gettext catalog name corresponding to a locale. If the locale
+ consists of a language and a territory, and the language is mainly spoken
+ in that territory, the territory is removed from the locale name.
+ For example, "de_DE" or "de_DE.ISO-8859-1" are simplified to "de",
+ because the resulting catalog can be used as a default for all "de_XX",
+ such as "de_AT". */
+static const char *
+catalogname_for_locale (const char *locale)
+{
+ static const char *locales_with_principal_territory[] = {
+ /* Language Main territory */
+ "ace_ID", /* Achinese Indonesia */
+ "af_ZA", /* Afrikaans South Africa */
+ "ak_GH", /* Akan Ghana */
+ "am_ET", /* Amharic Ethiopia */
+ "an_ES", /* Aragonese Spain */
+ "ang_GB", /* Old English Britain */
+ "arn_CL", /* Mapudungun Chile */
+ "as_IN", /* Assamese India */
+ "ast_ES", /* Asturian Spain */
+ "av_RU", /* Avaric Russia */
+ "awa_IN", /* Awadhi India */
+ "az_AZ", /* Azerbaijani Azerbaijan */
+ "ban_ID", /* Balinese Indonesia */
+ "be_BY", /* Belarusian Belarus */
+ "bej_SD", /* Beja Sudan */
+ "bem_ZM", /* Bemba Zambia */
+ "bg_BG", /* Bulgarian Bulgaria */
+ "bho_IN", /* Bhojpuri India */
+ "bik_PH", /* Bikol Philippines */
+ "bin_NG", /* Bini Nigeria */
+ "bm_ML", /* Bambara Mali */
+ "bn_IN", /* Bengali India */
+ "bo_CN", /* Tibetan China */
+ "br_FR", /* Breton France */
+ "bs_BA", /* Bosnian Bosnia */
+ "bug_ID", /* Buginese Indonesia */
+ "ca_ES", /* Catalan Spain */
+ "ce_RU", /* Chechen Russia */
+ "ceb_PH", /* Cebuano Philippines */
+ "co_FR", /* Corsican France */
+ "cr_CA", /* Cree Canada */
+ /* Don't put "crh_UZ" or "crh_UA" here. That would be asking for fruitless
+ political discussion. */
+ "cs_CZ", /* Czech Czech Republic */
+ "csb_PL", /* Kashubian Poland */
+ "cy_GB", /* Welsh Britain */
+ "da_DK", /* Danish Denmark */
+ "de_DE", /* German Germany */
+ "din_SD", /* Dinka Sudan */
+ "doi_IN", /* Dogri India */
+ "dsb_DE", /* Lower Sorbian Germany */
+ "dv_MV", /* Divehi Maldives */
+ "dz_BT", /* Dzongkha Bhutan */
+ "ee_GH", /* Éwé Ghana */
+ "el_GR", /* Greek Greece */
+ /* Don't put "en_GB" or "en_US" here. That would be asking for fruitless
+ political discussion. */
+ "es_ES", /* Spanish Spain */
+ "et_EE", /* Estonian Estonia */
+ "fa_IR", /* Persian Iran */
+ "fi_FI", /* Finnish Finland */
+ "fil_PH", /* Filipino Philippines */
+ "fj_FJ", /* Fijian Fiji */
+ "fo_FO", /* Faroese Faeroe Islands */
+ "fon_BJ", /* Fon Benin */
+ "fr_FR", /* French France */
+ "fur_IT", /* Friulian Italy */
+ "fy_NL", /* Western Frisian Netherlands */
+ "ga_IE", /* Irish Ireland */
+ "gd_GB", /* Scottish Gaelic Britain */
+ "gon_IN", /* Gondi India */
+ "gsw_CH", /* Swiss German Switzerland */
+ "gu_IN", /* Gujarati India */
+ "he_IL", /* Hebrew Israel */
+ "hi_IN", /* Hindi India */
+ "hil_PH", /* Hiligaynon Philippines */
+ "hr_HR", /* Croatian Croatia */
+ "hsb_DE", /* Upper Sorbian Germany */
+ "ht_HT", /* Haitian Haiti */
+ "hu_HU", /* Hungarian Hungary */
+ "hy_AM", /* Armenian Armenia */
+ "id_ID", /* Indonesian Indonesia */
+ "ig_NG", /* Igbo Nigeria */
+ "ii_CN", /* Sichuan Yi China */
+ "ilo_PH", /* Iloko Philippines */
+ "is_IS", /* Icelandic Iceland */
+ "it_IT", /* Italian Italy */
+ "ja_JP", /* Japanese Japan */
+ "jab_NG", /* Hyam Nigeria */
+ "jv_ID", /* Javanese Indonesia */
+ "ka_GE", /* Georgian Georgia */
+ "kab_DZ", /* Kabyle Algeria */
+ "kaj_NG", /* Jju Nigeria */
+ "kam_KE", /* Kamba Kenya */
+ "kmb_AO", /* Kimbundu Angola */
+ "kcg_NG", /* Tyap Nigeria */
+ "kdm_NG", /* Kagoma Nigeria */
+ "kg_CD", /* Kongo Democratic Republic of Congo */
+ "kk_KZ", /* Kazakh Kazakhstan */
+ "kl_GL", /* Kalaallisut Greenland */
+ "km_KH", /* Central Khmer Cambodia */
+ "kn_IN", /* Kannada India */
+ "ko_KR", /* Korean Korea (South) */
+ "kok_IN", /* Konkani India */
+ "kr_NG", /* Kanuri Nigeria */
+ "kru_IN", /* Kurukh India */
+ "lg_UG", /* Ganda Uganda */
+ "li_BE", /* Limburgish Belgium */
+ "lo_LA", /* Laotian Laos */
+ "lt_LT", /* Lithuanian Lithuania */
+ "lu_CD", /* Luba-Katanga Democratic Republic of Congo */
+ "lua_CD", /* Luba-Lulua Democratic Republic of Congo */
+ "luo_KE", /* Luo Kenya */
+ "lv_LV", /* Latvian Latvia */
+ "mad_ID", /* Madurese Indonesia */
+ "mag_IN", /* Magahi India */
+ "mai_IN", /* Maithili India */
+ "mak_ID", /* Makasar Indonesia */
+ "man_ML", /* Mandingo Mali */
+ "men_SL", /* Mende Sierra Leone */
+ "mg_MG", /* Malagasy Madagascar */
+ "mi_NZ", /* Maori New Zealand */
+ "min_ID", /* Minangkabau Indonesia */
+ "mk_MK", /* Macedonian Macedonia */
+ "ml_IN", /* Malayalam India */
+ "mn_MN", /* Mongolian Mongolia */
+ "mni_IN", /* Manipuri India */
+ "mos_BF", /* Mossi Burkina Faso */
+ "mr_IN", /* Marathi India */
+ "ms_MY", /* Malay Malaysia */
+ "mt_MT", /* Maltese Malta */
+ "mwr_IN", /* Marwari India */
+ "my_MM", /* Burmese Myanmar */
+ "na_NR", /* Nauru Nauru */
+ "nah_MX", /* Nahuatl Mexico */
+ "nap_IT", /* Neapolitan Italy */
+ "nb_NO", /* Norwegian Bokmål Norway */
+ "nds_DE", /* Low Saxon Germany */
+ "ne_NP", /* Nepali Nepal */
+ "nl_NL", /* Dutch Netherlands */
+ "nn_NO", /* Norwegian Nynorsk Norway */
+ "no_NO", /* Norwegian Norway */
+ "nr_ZA", /* South Ndebele South Africa */
+ "nso_ZA", /* Northern Sotho South Africa */
+ "nym_TZ", /* Nyamwezi Tanzania */
+ "nyn_UG", /* Nyankole Uganda */
+ "oc_FR", /* Occitan France */
+ "oj_CA", /* Ojibwa Canada */
+ "or_IN", /* Oriya India */
+ "pa_IN", /* Punjabi India */
+ "pag_PH", /* Pangasinan Philippines */
+ "pam_PH", /* Pampanga Philippines */
+ "pap_AN", /* Papiamento Netherlands Antilles */
+ "pbb_CO", /* Páez Colombia */
+ "pl_PL", /* Polish Poland */
+ "ps_AF", /* Pashto Afghanistan */
+ "pt_PT", /* Portuguese Portugal */
+ "raj_IN", /* Rajasthani India */
+ "rm_CH", /* Romansh Switzerland */
+ "rn_BI", /* Kirundi Burundi */
+ "ro_RO", /* Romanian Romania */
+ "ru_RU", /* Russian Russia */
+ "sa_IN", /* Sanskrit India */
+ "sah_RU", /* Yakut Russia */
+ "sas_ID", /* Sasak Indonesia */
+ "sat_IN", /* Santali India */
+ "sc_IT", /* Sardinian Italy */
+ "scn_IT", /* Sicilian Italy */
+ "sg_CF", /* Sango Central African Republic */
+ "shn_MM", /* Shan Myanmar */
+ "si_LK", /* Sinhala Sri Lanka */
+ "sid_ET", /* Sidamo Ethiopia */
+ "sk_SK", /* Slovak Slovakia */
+ "sl_SI", /* Slovenian Slovenia */
+ "smn_FI", /* Inari Sami Finland */
+ "sms_FI", /* Skolt Sami Finland */
+ "so_SO", /* Somali Somalia */
+ "sq_AL", /* Albanian Albania */
+ "sr_RS", /* Serbian Serbia */
+ "sr_YU", /* Serbian Yugoslavia - this line can be removed in 2010 */
+ "srr_SN", /* Serer Senegal */
+ "suk_TZ", /* Sukuma Tanzania */
+ "sus_GN", /* Susu Guinea */
+ "sv_SE", /* Swedish Sweden */
+ "te_IN", /* Telugu India */
+ "tem_SL", /* Timne Sierra Leone */
+ "tet_ID", /* Tetum Indonesia */
+ "tg_TJ", /* Tajik Tajikistan */
+ "th_TH", /* Thai Thailand */
+ "tiv_NG", /* Tiv Nigeria */
+ "tk_TM", /* Turkmen Turkmenistan */
+ "tl_PH", /* Tagalog Philippines */
+ "to_TO", /* Tonga Tonga */
+ "tr_TR", /* Turkish Turkey */
+ "tum_MW", /* Tumbuka Malawi */
+ "ug_CN", /* Uighur China */
+ "uk_UA", /* Ukrainian Ukraine */
+ "umb_AO", /* Umbundu Angola */
+ "ur_PK", /* Urdu Pakistan */
+ "uz_UZ", /* Uzbek Uzbekistan */
+ "ve_ZA", /* Venda South Africa */
+ "vi_VN", /* Vietnamese Vietnam */
+ "wa_BE", /* Walloon Belgium */
+ "wal_ET", /* Walamo Ethiopia */
+ "war_PH", /* Waray Philippines */
+ "wen_DE", /* Sorbian Germany */
+ "yao_MW", /* Yao Malawi */
+ "zap_MX" /* Zapotec Mexico */
+ };
+ const char *dot;
+ size_t i;
+
+ /* Remove the ".codeset" part from the locale. */
+ dot = strchr (locale, '.');
+ if (dot != NULL)
+ {
+ const char *codeset_end;
+ char *shorter_locale;
+
+ codeset_end = strpbrk (dot + 1, "_@");
+ if (codeset_end == NULL)
+ codeset_end = dot + strlen (dot);
+
+ shorter_locale = XNMALLOC (strlen (locale), char);
+ memcpy (shorter_locale, locale, dot - locale);
+ strcpy (shorter_locale + (dot - locale), codeset_end);
+ locale = shorter_locale;
+ }
+
+ /* If the territory is the language's principal territory, drop it. */
+ for (i = 0; i < SIZEOF (locales_with_principal_territory); i++)
+ if (strcmp (locale, locales_with_principal_territory[i]) == 0)
+ {
+ const char *language_end;
+ size_t len;
+ char *shorter_locale;
+
+ language_end = strchr (locale, '_');
+ if (language_end == NULL)
+ abort ();
+
+ len = language_end - locale;
+ shorter_locale = XNMALLOC (len + 1, char);
+ memcpy (shorter_locale, locale, len);
+ shorter_locale[len] = '\0';
+ locale = shorter_locale;
+ break;
+ }
+
+ return locale;
+}
+
+
+/* Return the language of a locale. */
+static const char *
+language_of_locale (const char *locale)
+{
+ const char *language_end;
+
+ language_end = strpbrk (locale, "_.@");
+ if (language_end != NULL)
+ {
+ size_t len;
+ char *result;
+
+ len = language_end - locale;
+ result = XNMALLOC (len + 1, char);
+ memcpy (result, locale, len);
+ result[len] = '\0';
+
+ return result;
+ }
+ else
+ return locale;
+}
+
+
+/* Return the most likely desired charset for the PO file, as a portable
+ charset name. */
+static const char *
+canonical_locale_charset ()
+{
+ const char *tmp;
+ char *old_LC_ALL;
+ const char *charset;
+
+ /* Save LC_ALL environment variable. */
+
+ tmp = getenv ("LC_ALL");
+ old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ xsetenv ("LC_ALL", locale, 1);
+
+#ifdef HAVE_SETLOCALE
+ if (setlocale (LC_ALL, "") == NULL)
+ /* Nonexistent locale. Use anything. */
+ charset = "";
+ else
+#endif
+ /* Get the locale's charset. */
+ charset = locale_charset ();
+
+ /* Restore LC_ALL environment variable. */
+
+ if (old_LC_ALL != NULL)
+ xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
+ else
+ unsetenv ("LC_ALL");
+
+#ifdef HAVE_SETLOCALE
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Canonicalize it. */
+ charset = po_charset_canonicalize (charset);
+ if (charset == NULL)
+ charset = po_charset_ascii;
+
+ return charset;
+}
+
+
+/* Return the English name of the language. */
+static const char *
+englishname_of_language ()
+{
+ size_t i;
+
+ for (i = 0; i < language_table_size; i++)
+ if (strcmp (language_table[i].code, language) == 0)
+ return language_table[i].english;
+
+ return xasprintf ("Language %s", language);
+}
+
+
+/* Construct the value for the PACKAGE name. */
+static const char *
+project_id (const char *header)
+{
+ const char *old_field;
+ const char *gettextlibdir;
+ char *prog;
+ char *argv[3];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+
+ /* Return the first part of the Project-Id-Version field if present, assuming
+ it was already filled in by xgettext. */
+ old_field = get_field (header, "Project-Id-Version");
+ if (old_field != NULL && strcmp (old_field, "PACKAGE VERSION") != 0)
+ {
+ /* Remove the last word from old_field. */
+ const char *last_space;
+
+ last_space = strrchr (old_field, ' ');
+ if (last_space != NULL)
+ {
+ while (last_space > old_field && last_space[-1] == ' ')
+ last_space--;
+ if (last_space > old_field)
+ {
+ size_t package_len = last_space - old_field;
+ char *package = XNMALLOC (package_len + 1, char);
+ memcpy (package, old_field, package_len);
+ package[package_len] = '\0';
+
+ return package;
+ }
+ }
+ /* It contains no version, just a package name. */
+ return old_field;
+ }
+
+ gettextlibdir = getenv ("GETTEXTLIBDIR");
+ if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
+ gettextlibdir = relocate (LIBDIR "/gettext");
+
+ prog = xconcatenated_filename (gettextlibdir, "project-id", NULL);
+
+ /* Call the project-id shell script. */
+ argv[0] = "/bin/sh";
+ argv[1] = prog;
+ argv[2] = NULL;
+ child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
+ fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve its result. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ {
+ error (0, errno, _("fdopen() failed"));
+ goto failed;
+ }
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ {
+ error (0, 0, _("%s subprocess I/O error"), prog);
+ fclose (fp);
+ goto failed;
+ }
+ if (linelen > 0 && line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus = wait_subprocess (child, prog, false, false, true, false, NULL);
+ if (exitstatus != 0)
+ {
+ error (0, 0, _("%s subprocess failed with exit code %d"),
+ prog, exitstatus);
+ goto failed;
+ }
+
+ return line;
+
+failed:
+ return "PACKAGE";
+}
+
+
+/* Construct the value for the Project-Id-Version field. */
+static const char *
+project_id_version (const char *header)
+{
+ const char *old_field;
+ const char *gettextlibdir;
+ char *prog;
+ char *argv[4];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+
+ /* Return the old value if present, assuming it was already filled in by
+ xgettext. */
+ old_field = get_field (header, "Project-Id-Version");
+ if (old_field != NULL && strcmp (old_field, "PACKAGE VERSION") != 0)
+ return old_field;
+
+ gettextlibdir = getenv ("GETTEXTLIBDIR");
+ if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
+ gettextlibdir = relocate (LIBDIR "/gettext");
+
+ prog = xconcatenated_filename (gettextlibdir, "project-id", NULL);
+
+ /* Call the project-id shell script. */
+ argv[0] = "/bin/sh";
+ argv[1] = prog;
+ argv[2] = "yes";
+ argv[3] = NULL;
+ child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
+ fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve its result. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ {
+ error (0, errno, _("fdopen() failed"));
+ goto failed;
+ }
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ {
+ error (0, 0, _("%s subprocess I/O error"), prog);
+ fclose (fp);
+ goto failed;
+ }
+ if (linelen > 0 && line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus = wait_subprocess (child, prog, false, false, true, false, NULL);
+ if (exitstatus != 0)
+ {
+ error (0, 0, _("%s subprocess failed with exit code %d"),
+ prog, exitstatus);
+ goto failed;
+ }
+
+ return line;
+
+failed:
+ return "PACKAGE VERSION";
+}
+
+
+/* Construct the value for the PO-Revision-Date field. */
+static const char *
+po_revision_date (const char *header)
+{
+ if (no_translator)
+ /* Because the PO file is automatically generated, we use the
+ POT-Creation-Date, not the current time. */
+ return get_field (header, "POT-Creation-Date");
+ else
+ {
+ /* Assume the translator will modify the PO file now. */
+ time_t now;
+
+ time (&now);
+ return po_strftime (&now);
+ }
+}
+
+
+/* Returns the struct passwd entry for the current user. */
+static struct passwd *
+get_user_pwd ()
+{
+#if HAVE_PWD_H /* Only Unix, not native Woe32. */
+ const char *username;
+ struct passwd *userpasswd;
+
+ /* 1. attempt: getpwnam(getenv("USER")) */
+ username = getenv ("USER");
+ if (username != NULL)
+ {
+ errno = 0;
+ userpasswd = getpwnam (username);
+ if (userpasswd != NULL)
+ return userpasswd;
+ if (errno != 0)
+ error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
+ }
+
+ /* 2. attempt: getpwnam(getlogin()) */
+ username = getlogin ();
+ if (username != NULL)
+ {
+ errno = 0;
+ userpasswd = getpwnam (username);
+ if (userpasswd != NULL)
+ return userpasswd;
+ if (errno != 0)
+ error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
+ }
+
+ /* 3. attempt: getpwuid(getuid()) */
+ errno = 0;
+ userpasswd = getpwuid (getuid ());
+ if (userpasswd != NULL)
+ return userpasswd;
+ if (errno != 0)
+ error (EXIT_FAILURE, errno, "getpwuid(%ju)", (uintmax_t) getuid ());
+#endif
+
+ return NULL;
+}
+
+
+/* Return the user's full name. */
+static const char *
+get_user_fullname ()
+{
+ struct passwd *pwd;
+
+ pwd = get_user_pwd ();
+#if HAVE_PWD_H
+ if (pwd != NULL)
+ {
+ const char *fullname;
+ const char *fullname_end;
+ char *result;
+
+ /* Return the pw_gecos field, up to the first comma (if any). */
+ fullname = pwd->pw_gecos;
+ fullname_end = strchr (fullname, ',');
+ if (fullname_end == NULL)
+ fullname_end = fullname + strlen (fullname);
+
+ result = XNMALLOC (fullname_end - fullname + 1, char);
+ memcpy (result, fullname, fullname_end - fullname);
+ result[fullname_end - fullname] = '\0';
+
+ return result;
+ }
+#endif
+
+ return NULL;
+}
+
+
+/* Return the user's email address. */
+static const char *
+get_user_email ()
+{
+ const char *prog = relocate (LIBDIR "/gettext/user-email");
+ char *argv[4];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+
+ /* Ask the user for his email address. */
+ argv[0] = "/bin/sh";
+ argv[1] = (char *) prog;
+ argv[2] = (char *) _("\
+The new message catalog should contain your email address, so that users can\n\
+give you feedback about the translations, and so that maintainers can contact\n\
+you in case of unexpected technical problems.\n");
+ argv[3] = NULL;
+ child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
+ fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve his answer. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ {
+ error (0, errno, _("fdopen() failed"));
+ goto failed;
+ }
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ {
+ error (0, 0, _("%s subprocess I/O error"), prog);
+ fclose (fp);
+ goto failed;
+ }
+ if (linelen > 0 && line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus = wait_subprocess (child, prog, false, false, true, false, NULL);
+ if (exitstatus != 0)
+ {
+ error (0, 0, _("%s subprocess failed with exit code %d"),
+ prog, exitstatus);
+ goto failed;
+ }
+
+ return line;
+
+failed:
+ return "EMAIL@ADDRESS";
+}
+
+
+/* Construct the value for the Last-Translator field. */
+static const char *
+last_translator ()
+{
+ if (no_translator)
+ return "Automatically generated";
+ else
+ {
+ const char *fullname = get_user_fullname ();
+ const char *email = get_user_email ();
+
+ if (fullname != NULL)
+ return xasprintf ("%s <%s>", fullname, email);
+ else
+ return xasprintf ("<%s>", email);
+ }
+}
+
+
+/* Return the name of the language used by the language team, in English. */
+static const char *
+language_team_englishname ()
+{
+ size_t i;
+
+ /* Search for a name depending on the catalogname. */
+ for (i = 0; i < language_variant_table_size; i++)
+ if (strcmp (language_variant_table[i].code, catalogname) == 0)
+ return language_variant_table[i].english;
+
+ /* Search for a name depending on the language only. */
+ return englishname_of_language ();
+}
+
+
+/* Return the language team's mailing list address or homepage URL. */
+static const char *
+language_team_address ()
+{
+ const char *prog = relocate (PROJECTSDIR "/team-address");
+ char *argv[7];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+
+ /* Call the team-address shell script. */
+ argv[0] = "/bin/sh";
+ argv[1] = (char *) prog;
+ argv[2] = (char *) relocate (PROJECTSDIR);
+ argv[3] = (char *) relocate (LIBDIR "/gettext");
+ argv[4] = (char *) catalogname;
+ argv[5] = (char *) language;
+ argv[6] = NULL;
+ child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
+ fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve its result. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ {
+ error (0, errno, _("fdopen() failed"));
+ goto failed;
+ }
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ line = "";
+ else if (linelen > 0 && line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus = wait_subprocess (child, prog, false, false, true, false, NULL);
+ if (exitstatus != 0)
+ {
+ error (0, 0, _("%s subprocess failed with exit code %d"),
+ prog, exitstatus);
+ goto failed;
+ }
+
+ return line;
+
+failed:
+ return "";
+}
+
+
+/* Construct the value for the Language-Team field. */
+static const char *
+language_team ()
+{
+ if (no_translator)
+ return "none";
+ else
+ {
+ const char *englishname = language_team_englishname ();
+ const char *address = language_team_address ();
+
+ if (address != NULL && address[0] != '\0')
+ return xasprintf ("%s %s", englishname, address);
+ else
+ return englishname;
+ }
+}
+
+
+/* Construct the value for the Language field. */
+static const char *
+language_value ()
+{
+ return catalogname;
+}
+
+
+/* Construct the value for the MIME-Version field. */
+static const char *
+mime_version ()
+{
+ return "1.0";
+}
+
+
+/* Construct the value for the Content-Type field. */
+static const char *
+content_type (const char *header)
+{
+ bool was_utf8;
+ const char *old_field;
+
+ /* If the POT file contains charset=UTF-8, it means that the POT file
+ contains non-ASCII characters, and we keep the UTF-8 encoding.
+ Otherwise, when the POT file is plain ASCII, we use the locale's
+ encoding. */
+ was_utf8 = false;
+ old_field = get_field (header, "Content-Type");
+ if (old_field != NULL)
+ {
+ const char *charsetstr = c_strstr (old_field, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ charsetstr += strlen ("charset=");
+ was_utf8 = (c_strcasecmp (charsetstr, "UTF-8") == 0);
+ }
+ }
+ return xasprintf ("text/plain; charset=%s",
+ was_utf8 ? "UTF-8" : canonical_locale_charset ());
+}
+
+
+/* Construct the value for the Content-Transfer-Encoding field. */
+static const char *
+content_transfer_encoding ()
+{
+ return "8bit";
+}
+
+
+/* Construct the value for the Plural-Forms field. */
+static const char *
+plural_forms ()
+{
+ size_t i;
+
+ /* Search for a formula depending on the catalogname. */
+ for (i = 0; i < plural_table_size; i++)
+ if (strcmp (plural_table[i].lang, catalogname) == 0)
+ return plural_table[i].value;
+
+ /* Search for a formula depending on the language only. */
+ for (i = 0; i < plural_table_size; i++)
+ if (strcmp (plural_table[i].lang, language) == 0)
+ return plural_table[i].value;
+
+ return NULL;
+}
+
+
+static struct
+{
+ const char *name;
+ const char * (*getter0) (void);
+ const char * (*getter1) (const char *header);
+}
+fields[] =
+ {
+ { "Project-Id-Version", NULL, project_id_version },
+ { "PO-Revision-Date", NULL, po_revision_date },
+ { "Last-Translator", last_translator, NULL },
+ { "Language-Team", language_team, NULL },
+ { "Language", language_value, NULL },
+ { "MIME-Version", mime_version, NULL },
+ { "Content-Type", NULL, content_type },
+ { "Content-Transfer-Encoding", content_transfer_encoding, NULL },
+ { "Plural-Forms", plural_forms, NULL }
+ };
+
+#define NFIELDS SIZEOF (fields)
+#define FIELD_LAST_TRANSLATOR 2
+
+
+/* Retrieve a freshly allocated copy of a field's value. */
+static char *
+get_field (const char *header, const char *field)
+{
+ size_t len = strlen (field);
+ const char *line;
+
+ for (line = header;;)
+ {
+ if (strncmp (line, field, len) == 0 && line[len] == ':')
+ {
+ const char *value_start;
+ const char *value_end;
+ char *value;
+
+ value_start = line + len + 1;
+ if (*value_start == ' ')
+ value_start++;
+ value_end = strchr (value_start, '\n');
+ if (value_end == NULL)
+ value_end = value_start + strlen (value_start);
+
+ value = XNMALLOC (value_end - value_start + 1, char);
+ memcpy (value, value_start, value_end - value_start);
+ value[value_end - value_start] = '\0';
+
+ return value;
+ }
+
+ line = strchr (line, '\n');
+ if (line != NULL)
+ line++;
+ else
+ break;
+ }
+
+ return NULL;
+}
+
+/* Add a field with value to a header, and return the new header. */
+static char *
+put_field (const char *old_header, const char *field, const char *value)
+{
+ size_t len = strlen (field);
+ const char *line;
+ char *new_header;
+ char *p;
+
+ for (line = old_header;;)
+ {
+ if (strncmp (line, field, len) == 0 && line[len] == ':')
+ {
+ const char *value_start;
+ const char *value_end;
+
+ value_start = line + len + 1;
+ if (*value_start == ' ')
+ value_start++;
+ value_end = strchr (value_start, '\n');
+ if (value_end == NULL)
+ value_end = value_start + strlen (value_start);
+
+ new_header = XNMALLOC (strlen (old_header)
+ - (value_end - value_start)
+ + strlen (value)
+ + (*value_end != '\n' ? 1 : 0)
+ + 1,
+ char);
+ p = new_header;
+ memcpy (p, old_header, value_start - old_header);
+ p += value_start - old_header;
+ memcpy (p, value, strlen (value));
+ p += strlen (value);
+ if (*value_end != '\n')
+ *p++ = '\n';
+ strcpy (p, value_end);
+
+ return new_header;
+ }
+
+ line = strchr (line, '\n');
+ if (line != NULL)
+ line++;
+ else
+ break;
+ }
+
+ new_header = XNMALLOC (strlen (old_header) + 1
+ + len + 2 + strlen (value) + 1
+ + 1,
+ char);
+ p = new_header;
+ memcpy (p, old_header, strlen (old_header));
+ p += strlen (old_header);
+ if (p > new_header && p[-1] != '\n')
+ *p++ = '\n';
+ memcpy (p, field, len);
+ p += len;
+ *p++ = ':';
+ *p++ = ' ';
+ memcpy (p, value, strlen (value));
+ p += strlen (value);
+ *p++ = '\n';
+ *p = '\0';
+
+ return new_header;
+}
+
+
+/* Return the title format string. */
+static const char *
+get_title ()
+{
+ /* This is tricky. We want the translation in the given locale specified by
+ the command line, not the current locale. But we want it in the encoding
+ that we put into the header entry, not the encoding of that locale.
+ We could avoid the use of OUTPUT_CHARSET by using a separate message
+ catalog and bind_textdomain_codeset(), but that doesn't seem worth the
+ trouble for one single message. */
+ const char *encoding;
+ const char *tmp;
+ char *old_LC_ALL;
+ char *old_LANGUAGE;
+ char *old_OUTPUT_CHARSET;
+ const char *msgid;
+ const char *english;
+ const char *result;
+
+ encoding = canonical_locale_charset ();
+
+ /* First, the English title. */
+ english = xasprintf ("%s translations for %%s package",
+ englishname_of_language ());
+
+ /* Save LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables. */
+
+ tmp = getenv ("LC_ALL");
+ old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ tmp = getenv ("LANGUAGE");
+ old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ tmp = getenv ("OUTPUT_CHARSET");
+ old_OUTPUT_CHARSET = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+ xsetenv ("LC_ALL", locale, 1);
+ unsetenv ("LANGUAGE");
+ xsetenv ("OUTPUT_CHARSET", encoding, 1);
+
+#ifdef HAVE_SETLOCALE
+ if (setlocale (LC_ALL, "") == NULL)
+ /* Nonexistent locale. Use the English title. */
+ result = english;
+ else
+#endif
+ {
+ /* Fetch the translation. */
+ /* TRANSLATORS: "English" needs to be replaced by your language.
+ For example in it.po write "Traduzioni italiani ...",
+ *not* "Traduzioni inglesi ...". */
+ msgid = N_("English translations for %s package");
+ result = gettext (msgid);
+ if (result != msgid && strcmp (result, msgid) != 0)
+ /* Use the English and the foreign title. */
+ result = xasprintf ("%s\n%s", english, result);
+ else
+ /* No translation found. Use the English title. */
+ result = english;
+ }
+
+ /* Restore LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables. */
+
+ if (old_LC_ALL != NULL)
+ xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
+ else
+ unsetenv ("LC_ALL");
+
+ if (old_LANGUAGE != NULL)
+ xsetenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
+ else
+ unsetenv ("LANGUAGE");
+
+ if (old_OUTPUT_CHARSET != NULL)
+ xsetenv ("OUTPUT_CHARSET", old_OUTPUT_CHARSET, 1), free (old_OUTPUT_CHARSET);
+ else
+ unsetenv ("OUTPUT_CHARSET");
+
+#ifdef HAVE_SETLOCALE
+ setlocale (LC_ALL, "");
+#endif
+
+ return result;
+}
+
+
+/* Perform a set of substitutions in a string and return the resulting
+ string. When subst[j][0] found, it is replaced with subst[j][1].
+ subst[j][0] must not be the empty string. */
+static const char *
+subst_string (const char *str,
+ unsigned int nsubst, const char *(*subst)[2])
+{
+ if (nsubst > 0)
+ {
+ char *malloced = NULL;
+ size_t *substlen;
+ size_t i;
+ unsigned int j;
+
+ substlen = (size_t *) xmalloca (nsubst * sizeof (size_t));
+ for (j = 0; j < nsubst; j++)
+ {
+ substlen[j] = strlen (subst[j][0]);
+ if (substlen[j] == 0)
+ abort ();
+ }
+
+ for (i = 0;;)
+ {
+ if (str[i] == '\0')
+ break;
+ for (j = 0; j < nsubst; j++)
+ if (*(str + i) == *subst[j][0]
+ && strncmp (str + i, subst[j][0], substlen[j]) == 0)
+ {
+ size_t replacement_len = strlen (subst[j][1]);
+ size_t new_len = strlen (str) - substlen[j] + replacement_len;
+ char *new_str = XNMALLOC (new_len + 1, char);
+ memcpy (new_str, str, i);
+ memcpy (new_str + i, subst[j][1], replacement_len);
+ strcpy (new_str + i + replacement_len, str + i + substlen[j]);
+ if (malloced != NULL)
+ free (malloced);
+ str = new_str;
+ malloced = new_str;
+ i += replacement_len;
+ break;
+ }
+ if (j == nsubst)
+ i++;
+ }
+
+ freea (substlen);
+ }
+
+ return str;
+}
+
+/* Perform a set of substitutions on each string of a string list.
+ When subst[j][0] found, it is replaced with subst[j][1]. subst[j][0]
+ must not be the empty string. */
+static void
+subst_string_list (string_list_ty *slp,
+ unsigned int nsubst, const char *(*subst)[2])
+{
+ size_t j;
+
+ for (j = 0; j < slp->nitems; j++)
+ slp->item[j] = subst_string (slp->item[j], nsubst, subst);
+}
+
+
+/* Fill the templates in all fields of the header entry. */
+static msgdomain_list_ty *
+fill_header (msgdomain_list_ty *mdlp)
+{
+ /* Cache the strings filled in, for use when there are multiple domains
+ and a header entry for each domain. */
+ const char *field_value[NFIELDS];
+ size_t k, j, i;
+
+ for (i = 0; i < NFIELDS; i++)
+ field_value[i] = NULL;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ if (mlp->nitems > 0)
+ {
+ message_ty *header_mp = NULL;
+ char *header;
+
+ /* Search the header entry. */
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ header_mp = mlp->item[j];
+ break;
+ }
+
+ /* If it wasn't found, provide one. */
+ if (header_mp == NULL)
+ {
+ static lex_pos_ty pos = { __FILE__, __LINE__ };
+
+ header_mp = message_alloc (NULL, "", NULL, "", 1, &pos);
+ message_list_prepend (mlp, header_mp);
+ }
+
+ header = xstrdup (header_mp->msgstr);
+
+ /* Fill in the fields. */
+ for (i = 0; i < NFIELDS; i++)
+ {
+ if (field_value[i] == NULL)
+ field_value[i] =
+ (fields[i].getter1 != NULL
+ ? fields[i].getter1 (header)
+ : fields[i].getter0 ());
+
+ if (field_value[i] != NULL)
+ {
+ char *old_header = header;
+ header = put_field (header, fields[i].name, field_value[i]);
+ free (old_header);
+ }
+ }
+
+ /* Replace the old translation in the header entry. */
+ header_mp->msgstr = header;
+ header_mp->msgstr_len = strlen (header) + 1;
+
+ /* Update the comments in the header entry. */
+ if (header_mp->comment != NULL)
+ {
+ const char *subst[4][2];
+ const char *id;
+ time_t now;
+
+ id = project_id (header);
+ subst[0][0] = "SOME DESCRIPTIVE TITLE";
+ subst[0][1] = xasprintf (get_title (), id, id);
+ subst[1][0] = "PACKAGE";
+ subst[1][1] = id;
+ subst[2][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
+ subst[2][1] = field_value[FIELD_LAST_TRANSLATOR];
+ subst[3][0] = "YEAR";
+ subst[3][1] =
+ xasprintf ("%d",
+ (time (&now), (localtime (&now))->tm_year + 1900));
+ subst_string_list (header_mp->comment, SIZEOF (subst), subst);
+ }
+
+ /* Finally remove the fuzzy attribute. */
+ header_mp->is_fuzzy = false;
+ }
+ }
+
+ return mdlp;
+}
+
+
+/* Update the msgstr plural entries according to the nplurals count. */
+static msgdomain_list_ty *
+update_msgstr_plurals (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ message_ty *header_entry;
+ unsigned long int nplurals;
+ char *untranslated_plural_msgstr;
+ size_t j;
+
+ header_entry = message_list_search (mlp, NULL, "");
+ nplurals = get_plural_count (header_entry ? header_entry->msgstr : NULL);
+ untranslated_plural_msgstr = XNMALLOC (nplurals, char);
+ memset (untranslated_plural_msgstr, '\0', nplurals);
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+ bool is_untranslated;
+ const char *p;
+ const char *pend;
+
+ if (mp->msgid_plural != NULL)
+ {
+ /* Test if mp is untranslated. (It most likely is.) */
+ is_untranslated = true;
+ for (p = mp->msgstr, pend = p + mp->msgstr_len; p < pend; p++)
+ if (*p != '\0')
+ {
+ is_untranslated = false;
+ break;
+ }
+ if (is_untranslated)
+ {
+ /* Change mp->msgstr_len consecutive empty strings into
+ nplurals consecutive empty strings. */
+ if (nplurals > mp->msgstr_len)
+ mp->msgstr = untranslated_plural_msgstr;
+ mp->msgstr_len = nplurals;
+ }
+ }
+ }
+ }
+ return mdlp;
+}
diff --git a/gettext-tools/src/msgl-ascii.c b/gettext-tools/src/msgl-ascii.c
new file mode 100644
index 0000000..e3ca953
--- /dev/null
+++ b/gettext-tools/src/msgl-ascii.c
@@ -0,0 +1,112 @@
+/* Message list test for ASCII character set.
+ Copyright (C) 2001-2002, 2005-2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "msgl-ascii.h"
+
+#include "c-ctype.h"
+
+
+/* This file's structure parallels msgl-iconv.c. */
+
+
+bool
+is_ascii_string (const char *string)
+{
+ for (; *string; string++)
+ if (!c_isascii ((unsigned char) *string))
+ return false;
+ return true;
+}
+
+bool
+is_ascii_string_list (string_list_ty *slp)
+{
+ size_t i;
+
+ if (slp != NULL)
+ for (i = 0; i < slp->nitems; i++)
+ if (!is_ascii_string (slp->item[i]))
+ return false;
+ return true;
+}
+
+bool
+is_ascii_message (message_ty *mp)
+{
+ const char *p = mp->msgstr;
+ const char *p_end = p + mp->msgstr_len;
+
+ for (; p < p_end; p++)
+ if (!c_isascii ((unsigned char) *p))
+ return false;
+
+ if (!is_ascii_string_list (mp->comment))
+ return false;
+ if (!is_ascii_string_list (mp->comment_dot))
+ return false;
+
+ /* msgid and msgid_plural are normally ASCII, so why checking?
+ Because in complete UTF-8 environments they can be UTF-8, not ASCII. */
+ if (!is_ascii_string (mp->msgid))
+ return false;
+ if (mp->msgid_plural != NULL && !is_ascii_string (mp->msgid_plural))
+ return false;
+
+ /* Likewise for msgctxt. */
+ if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt))
+ return false;
+
+ /* Likewise for the prev_* fields. */
+ if (mp->prev_msgctxt != NULL && !is_ascii_string (mp->prev_msgctxt))
+ return false;
+ if (mp->prev_msgid != NULL && !is_ascii_string (mp->prev_msgid))
+ return false;
+ if (mp->prev_msgid_plural != NULL && !is_ascii_string (mp->prev_msgid_plural))
+ return false;
+
+ return true;
+}
+
+bool
+is_ascii_message_list (message_list_ty *mlp)
+{
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (!is_ascii_message (mlp->item[j]))
+ return false;
+
+ return true;
+}
+
+bool
+is_ascii_msgdomain_list (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ if (!is_ascii_message_list (mdlp->item[k]->messages))
+ return false;
+
+ return true;
+}
diff --git a/gettext-tools/src/msgl-ascii.h b/gettext-tools/src/msgl-ascii.h
new file mode 100644
index 0000000..fee41a9
--- /dev/null
+++ b/gettext-tools/src/msgl-ascii.h
@@ -0,0 +1,48 @@
+/* Message list test for ASCII character set.
+ Copyright (C) 2001-2003, 2005 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_ASCII_H
+#define _MSGL_ASCII_H
+
+#include "message.h"
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern bool
+ is_ascii_string (const char *string);
+extern bool
+ is_ascii_string_list (string_list_ty *slp);
+extern bool
+ is_ascii_message (message_ty *mp);
+extern bool
+ is_ascii_message_list (message_list_ty *mlp);
+extern bool
+ is_ascii_msgdomain_list (msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_ASCII_H */
diff --git a/gettext-tools/src/msgl-cat.c b/gettext-tools/src/msgl-cat.c
new file mode 100644
index 0000000..0bd58d4
--- /dev/null
+++ b/gettext-tools/src/msgl-cat.c
@@ -0,0 +1,799 @@
+/* Message list concatenation and duplicate handling.
+ Copyright (C) 2001-2003, 2005-2008, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "msgl-cat.h"
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "po-charset.h"
+#include "msgl-ascii.h"
+#include "msgl-equal.h"
+#include "msgl-iconv.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "c-strstr.h"
+#include "basename.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* These variables control which messages are selected. */
+int more_than;
+int less_than;
+
+/* If true, use the first available translation.
+ If false, merge all available translations into one and fuzzy it. */
+bool use_first;
+
+/* If true, merge like msgcomm.
+ If false, merge like msgcat and msguniq. */
+bool msgcomm_mode = false;
+
+/* If true, omit the header entry.
+ If false, keep the header entry present in the input. */
+bool omit_header = false;
+
+
+static bool
+is_message_selected (const message_ty *tmp)
+{
+ int used = (tmp->used >= 0 ? tmp->used : - tmp->used);
+
+ return (is_header (tmp)
+ ? !omit_header /* keep the header entry */
+ : (used > more_than && used < less_than));
+}
+
+
+static bool
+is_message_needed (const message_ty *mp)
+{
+ if (!msgcomm_mode
+ && ((!is_header (mp) && mp->is_fuzzy) || mp->msgstr[0] == '\0'))
+ /* Weak translation. Needed if there are only weak translations. */
+ return mp->tmp->used < 0 && is_message_selected (mp->tmp);
+ else
+ /* Good translation. */
+ return is_message_selected (mp->tmp);
+}
+
+
+/* The use_first logic. */
+static bool
+is_message_first_needed (const message_ty *mp)
+{
+ if (mp->tmp->obsolete && is_message_needed (mp))
+ {
+ mp->tmp->obsolete = false;
+ return true;
+ }
+ else
+ return false;
+}
+
+
+msgdomain_list_ty *
+catenate_msgdomain_list (string_list_ty *file_list,
+ catalog_input_format_ty input_syntax,
+ const char *to_code)
+{
+ const char * const *files = file_list->item;
+ size_t nfiles = file_list->nitems;
+ msgdomain_list_ty **mdlps;
+ const char ***canon_charsets;
+ const char ***identifications;
+ msgdomain_list_ty *total_mdlp;
+ const char *canon_to_code;
+ size_t n, j;
+
+ /* Read input files. */
+ mdlps = XNMALLOC (nfiles, msgdomain_list_ty *);
+ for (n = 0; n < nfiles; n++)
+ mdlps[n] = read_catalog_file (files[n], input_syntax);
+
+ /* Determine the canonical name of each input file's encoding. */
+ canon_charsets = XNMALLOC (nfiles, const char **);
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ canon_charsets[n] = XNMALLOC (mdlp->nitems, const char *);
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ const char *canon_from_code = NULL;
+
+ if (mlp->nitems > 0)
+ {
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+ const char *canon_charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ canon_charset = po_charset_canonicalize (charset);
+ if (canon_charset == NULL)
+ {
+ /* Don't give an error for POT files, because
+ POT files usually contain only ASCII
+ msgids. */
+ const char *filename = files[n];
+ size_t filenamelen = strlen (filename);
+
+ if (filenamelen >= 4
+ && memcmp (filename + filenamelen - 4,
+ ".pot", 4) == 0
+ && strcmp (charset, "CHARSET") == 0)
+ canon_charset = po_charset_ascii;
+ else
+ error (EXIT_FAILURE, 0,
+ _("\
+present charset \"%s\" is not a portable encoding name"),
+ charset);
+ }
+
+ freea (charset);
+
+ if (canon_from_code == NULL)
+ canon_from_code = canon_charset;
+ else if (canon_from_code != canon_charset)
+ error (EXIT_FAILURE, 0,
+ _("\
+two different charsets \"%s\" and \"%s\" in input file"),
+ canon_from_code, canon_charset);
+ }
+ }
+ }
+ if (canon_from_code == NULL)
+ {
+ if (is_ascii_message_list (mlp))
+ canon_from_code = po_charset_ascii;
+ else if (mdlp->encoding != NULL)
+ canon_from_code = mdlp->encoding;
+ else
+ {
+ if (k == 0)
+ error (EXIT_FAILURE, 0, _("\
+input file '%s' doesn't contain a header entry with a charset specification"),
+ files[n]);
+ else
+ error (EXIT_FAILURE, 0, _("\
+domain \"%s\" in input file '%s' doesn't contain a header entry with a charset specification"),
+ mdlp->item[k]->domain, files[n]);
+ }
+ }
+ }
+ canon_charsets[n][k] = canon_from_code;
+ }
+ }
+
+ /* Determine textual identifications of each file/domain combination. */
+ identifications = XNMALLOC (nfiles, const char **);
+ for (n = 0; n < nfiles; n++)
+ {
+ const char *filename = basename (files[n]);
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ identifications[n] = XNMALLOC (mdlp->nitems, const char *);
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ const char *domain = mdlp->item[k]->domain;
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ char *project_id = NULL;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *cp = c_strstr (header, "Project-Id-Version:");
+
+ if (cp != NULL)
+ {
+ const char *endp;
+
+ cp += sizeof ("Project-Id-Version:") - 1;
+
+ endp = strchr (cp, '\n');
+ if (endp == NULL)
+ endp = cp + strlen (cp);
+
+ while (cp < endp && *cp == ' ')
+ cp++;
+
+ if (cp < endp)
+ {
+ size_t len = endp - cp;
+ project_id = XNMALLOC (len + 1, char);
+ memcpy (project_id, cp, len);
+ project_id[len] = '\0';
+ }
+ break;
+ }
+ }
+ }
+
+ identifications[n][k] =
+ (project_id != NULL
+ ? (k > 0 ? xasprintf ("%s:%s (%s)", filename, domain, project_id)
+ : xasprintf ("%s (%s)", filename, project_id))
+ : (k > 0 ? xasprintf ("%s:%s", filename, domain)
+ : xasprintf ("%s", filename)));
+ }
+ }
+
+ /* Create list of resulting messages, but don't fill it. Only count
+ the number of translations for each message.
+ If for a message, there is at least one non-fuzzy, non-empty translation,
+ use only the non-fuzzy, non-empty translations. Otherwise use the
+ fuzzy or empty translations as well. */
+ total_mdlp = msgdomain_list_alloc (true);
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ const char *domain = mdlp->item[k]->domain;
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ message_list_ty *total_mlp;
+
+ total_mlp = msgdomain_list_sublist (total_mdlp, domain, true);
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+ message_ty *tmp;
+ size_t i;
+
+ tmp = message_list_search (total_mlp, mp->msgctxt, mp->msgid);
+ if (tmp == NULL)
+ {
+ tmp = message_alloc (mp->msgctxt, mp->msgid, mp->msgid_plural,
+ NULL, 0, &mp->pos);
+ tmp->is_fuzzy = true; /* may be set to false later */
+ for (i = 0; i < NFORMATS; i++)
+ tmp->is_format[i] = undecided; /* may be set to yes/no later */
+ tmp->range.min = - INT_MAX;
+ tmp->range.max = - INT_MAX;
+ tmp->do_wrap = yes; /* may be set to no later */
+ tmp->obsolete = true; /* may be set to false later */
+ tmp->alternative_count = 0;
+ tmp->alternative = NULL;
+ message_list_append (total_mlp, tmp);
+ }
+
+ if (!msgcomm_mode
+ && ((!is_header (mp) && mp->is_fuzzy)
+ || mp->msgstr[0] == '\0'))
+ /* Weak translation. Counted as negative tmp->used. */
+ {
+ if (tmp->used <= 0)
+ tmp->used--;
+ }
+ else
+ /* Good translation. Counted as positive tmp->used. */
+ {
+ if (tmp->used < 0)
+ tmp->used = 0;
+ tmp->used++;
+ }
+ mp->tmp = tmp;
+ }
+ }
+ }
+
+ /* Remove messages that are not used and need not be converted. */
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ message_list_remove_if_not (mlp,
+ use_first
+ ? is_message_first_needed
+ : is_message_needed);
+
+ /* If no messages are remaining, drop the charset. */
+ if (mlp->nitems == 0)
+ canon_charsets[n][k] = NULL;
+ }
+ }
+ {
+ size_t k;
+
+ for (k = 0; k < total_mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = total_mdlp->item[k]->messages;
+
+ message_list_remove_if_not (mlp, is_message_selected);
+ }
+ }
+
+ /* Determine the common known a-priori encoding, if any. */
+ if (nfiles > 0)
+ {
+ bool all_same_encoding = true;
+
+ for (n = 1; n < nfiles; n++)
+ if (mdlps[n]->encoding != mdlps[0]->encoding)
+ {
+ all_same_encoding = false;
+ break;
+ }
+
+ if (all_same_encoding)
+ total_mdlp->encoding = mdlps[0]->encoding;
+ }
+
+ /* Determine the target encoding for the remaining messages. */
+ if (to_code != NULL)
+ {
+ /* Canonicalize target encoding. */
+ canon_to_code = po_charset_canonicalize (to_code);
+ if (canon_to_code == NULL)
+ error (EXIT_FAILURE, 0,
+ _("target charset \"%s\" is not a portable encoding name."),
+ to_code);
+ }
+ else
+ {
+ /* No target encoding was specified. Test whether the messages are
+ all in a single encoding. If so, conversion is not needed. */
+ const char *first = NULL;
+ const char *second = NULL;
+ bool with_ASCII = false;
+ bool with_UTF8 = false;
+ bool all_ASCII_compatible = true;
+
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ if (canon_charsets[n][k] != NULL)
+ {
+ if (canon_charsets[n][k] == po_charset_ascii)
+ with_ASCII = true;
+ else
+ {
+ if (first == NULL)
+ first = canon_charsets[n][k];
+ else if (canon_charsets[n][k] != first && second == NULL)
+ second = canon_charsets[n][k];
+
+ if (strcmp (canon_charsets[n][k], "UTF-8") == 0)
+ with_UTF8 = true;
+
+ if (!po_charset_ascii_compatible (canon_charsets[n][k]))
+ all_ASCII_compatible = false;
+ }
+ }
+ }
+
+ if (with_ASCII && !all_ASCII_compatible)
+ {
+ /* assert (first != NULL); */
+ if (second == NULL)
+ second = po_charset_ascii;
+ }
+
+ if (second != NULL)
+ {
+ /* A conversion is needed. Warn the user since he hasn't asked
+ for it and might be surprised. */
+ if (with_UTF8)
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+Input files contain messages in different encodings, UTF-8 among others.\n\
+Converting the output to UTF-8.\n\
+")));
+ else
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+Input files contain messages in different encodings, %s and %s among others.\n\
+Converting the output to UTF-8.\n\
+To select a different output encoding, use the --to-code option.\n\
+"), first, second));
+ canon_to_code = po_charset_utf8;
+ }
+ else if (first != NULL && with_ASCII && all_ASCII_compatible)
+ {
+ /* The conversion is a no-op conversion. Don't warn the user,
+ but still perform the conversion, in order to check that the
+ input was really ASCII. */
+ canon_to_code = first;
+ }
+ else
+ {
+ /* No conversion needed. */
+ canon_to_code = NULL;
+ }
+ }
+
+ /* Now convert the remaining messages to to_code. */
+ if (canon_to_code != NULL)
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ if (canon_charsets[n][k] != NULL)
+ /* If the user hasn't given a to_code, don't bother doing a noop
+ conversion that would only replace the charset name in the
+ header entry with its canonical equivalent. */
+ if (!(to_code == NULL && canon_charsets[n][k] == canon_to_code))
+ if (iconv_message_list (mdlp->item[k]->messages,
+ canon_charsets[n][k], canon_to_code,
+ files[n]))
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+Conversion of file %s from %s encoding to %s encoding\n\
+changes some msgids or msgctxts.\n\
+Either change all msgids and msgctxts to be pure ASCII, or ensure they are\n\
+UTF-8 encoded from the beginning, i.e. already in your source code files.\n"),
+ files[n], canon_charsets[n][k],
+ canon_to_code));
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Fill the resulting messages. */
+ for (n = 0; n < nfiles; n++)
+ {
+ msgdomain_list_ty *mdlp = mdlps[n];
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+ message_ty *tmp = mp->tmp;
+ size_t i;
+
+ /* No need to discard unneeded weak translations here;
+ they have already been filtered out above. */
+ if (use_first || tmp->used == 1 || tmp->used == -1)
+ {
+ /* Copy mp, as only message, into tmp. */
+ tmp->msgstr = mp->msgstr;
+ tmp->msgstr_len = mp->msgstr_len;
+ tmp->pos = mp->pos;
+ if (mp->comment)
+ for (i = 0; i < mp->comment->nitems; i++)
+ message_comment_append (tmp, mp->comment->item[i]);
+ if (mp->comment_dot)
+ for (i = 0; i < mp->comment_dot->nitems; i++)
+ message_comment_dot_append (tmp,
+ mp->comment_dot->item[i]);
+ for (i = 0; i < mp->filepos_count; i++)
+ message_comment_filepos (tmp, mp->filepos[i].file_name,
+ mp->filepos[i].line_number);
+ tmp->is_fuzzy = mp->is_fuzzy;
+ for (i = 0; i < NFORMATS; i++)
+ tmp->is_format[i] = mp->is_format[i];
+ tmp->range = mp->range;
+ tmp->do_wrap = mp->do_wrap;
+ tmp->prev_msgctxt = mp->prev_msgctxt;
+ tmp->prev_msgid = mp->prev_msgid;
+ tmp->prev_msgid_plural = mp->prev_msgid_plural;
+ tmp->obsolete = mp->obsolete;
+ }
+ else if (msgcomm_mode)
+ {
+ /* Copy mp, as only message, into tmp. */
+ if (tmp->msgstr == NULL)
+ {
+ tmp->msgstr = mp->msgstr;
+ tmp->msgstr_len = mp->msgstr_len;
+ tmp->pos = mp->pos;
+ tmp->is_fuzzy = mp->is_fuzzy;
+ tmp->prev_msgctxt = mp->prev_msgctxt;
+ tmp->prev_msgid = mp->prev_msgid;
+ tmp->prev_msgid_plural = mp->prev_msgid_plural;
+ }
+ if (mp->comment && tmp->comment == NULL)
+ for (i = 0; i < mp->comment->nitems; i++)
+ message_comment_append (tmp, mp->comment->item[i]);
+ if (mp->comment_dot && tmp->comment_dot == NULL)
+ for (i = 0; i < mp->comment_dot->nitems; i++)
+ message_comment_dot_append (tmp,
+ mp->comment_dot->item[i]);
+ for (i = 0; i < mp->filepos_count; i++)
+ message_comment_filepos (tmp, mp->filepos[i].file_name,
+ mp->filepos[i].line_number);
+ for (i = 0; i < NFORMATS; i++)
+ if (tmp->is_format[i] == undecided)
+ tmp->is_format[i] = mp->is_format[i];
+ if (tmp->range.min == - INT_MAX
+ && tmp->range.max == - INT_MAX)
+ tmp->range = mp->range;
+ else if (has_range_p (mp->range) && has_range_p (tmp->range))
+ {
+ if (mp->range.min < tmp->range.min)
+ tmp->range.min = mp->range.min;
+ if (mp->range.max > tmp->range.max)
+ tmp->range.max = mp->range.max;
+ }
+ else
+ {
+ tmp->range.min = -1;
+ tmp->range.max = -1;
+ }
+ if (tmp->do_wrap == undecided)
+ tmp->do_wrap = mp->do_wrap;
+ tmp->obsolete = false;
+ }
+ else
+ {
+ /* Copy mp, among others, into tmp. */
+ char *id = xasprintf ("#-#-#-#-# %s #-#-#-#-#",
+ identifications[n][k]);
+ size_t nbytes;
+
+ if (tmp->alternative_count == 0)
+ tmp->pos = mp->pos;
+
+ i = tmp->alternative_count;
+ nbytes = (i + 1) * sizeof (struct altstr);
+ tmp->alternative = xrealloc (tmp->alternative, nbytes);
+ tmp->alternative[i].msgstr = mp->msgstr;
+ tmp->alternative[i].msgstr_len = mp->msgstr_len;
+ tmp->alternative[i].msgstr_end =
+ tmp->alternative[i].msgstr + tmp->alternative[i].msgstr_len;
+ tmp->alternative[i].comment = mp->comment;
+ tmp->alternative[i].comment_dot = mp->comment_dot;
+ tmp->alternative[i].id = id;
+ tmp->alternative_count = i + 1;
+
+ for (i = 0; i < mp->filepos_count; i++)
+ message_comment_filepos (tmp, mp->filepos[i].file_name,
+ mp->filepos[i].line_number);
+ if (!mp->is_fuzzy)
+ tmp->is_fuzzy = false;
+ for (i = 0; i < NFORMATS; i++)
+ if (mp->is_format[i] == yes)
+ tmp->is_format[i] = yes;
+ else if (mp->is_format[i] == no
+ && tmp->is_format[i] == undecided)
+ tmp->is_format[i] = no;
+ if (tmp->range.min == - INT_MAX
+ && tmp->range.max == - INT_MAX)
+ tmp->range = mp->range;
+ else if (has_range_p (mp->range) && has_range_p (tmp->range))
+ {
+ if (mp->range.min < tmp->range.min)
+ tmp->range.min = mp->range.min;
+ if (mp->range.max > tmp->range.max)
+ tmp->range.max = mp->range.max;
+ }
+ else
+ {
+ tmp->range.min = -1;
+ tmp->range.max = -1;
+ }
+ if (mp->do_wrap == no)
+ tmp->do_wrap = no;
+ /* Don't fill tmp->prev_msgid in this case. */
+ if (!mp->obsolete)
+ tmp->obsolete = false;
+ }
+ }
+ }
+ }
+ {
+ size_t k;
+
+ for (k = 0; k < total_mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = total_mdlp->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *tmp = mlp->item[j];
+
+ if (tmp->alternative_count > 0)
+ {
+ /* Test whether all alternative translations are equal. */
+ struct altstr *first = &tmp->alternative[0];
+ size_t i;
+
+ for (i = 0; i < tmp->alternative_count; i++)
+ if (!(tmp->alternative[i].msgstr_len == first->msgstr_len
+ && memcmp (tmp->alternative[i].msgstr, first->msgstr,
+ first->msgstr_len) == 0))
+ break;
+
+ if (i == tmp->alternative_count)
+ {
+ /* All alternatives are equal. */
+ tmp->msgstr = first->msgstr;
+ tmp->msgstr_len = first->msgstr_len;
+ }
+ else
+ {
+ /* Concatenate the alternative msgstrs into a single one,
+ separated by markers. */
+ size_t len;
+ const char *p;
+ const char *p_end;
+ char *new_msgstr;
+ char *np;
+
+ len = 0;
+ for (i = 0; i < tmp->alternative_count; i++)
+ {
+ size_t id_len = strlen (tmp->alternative[i].id);
+
+ len += tmp->alternative[i].msgstr_len;
+
+ p = tmp->alternative[i].msgstr;
+ p_end = tmp->alternative[i].msgstr_end;
+ for (; p < p_end; p += strlen (p) + 1)
+ len += id_len + 2;
+ }
+
+ new_msgstr = XNMALLOC (len, char);
+ np = new_msgstr;
+ for (;;)
+ {
+ /* Test whether there's one more plural form to
+ process. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ if (tmp->alternative[i].msgstr
+ < tmp->alternative[i].msgstr_end)
+ break;
+ if (i == tmp->alternative_count)
+ break;
+
+ /* Process next plural form. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ if (tmp->alternative[i].msgstr
+ < tmp->alternative[i].msgstr_end)
+ {
+ if (np > new_msgstr && np[-1] != '\0'
+ && np[-1] != '\n')
+ *np++ = '\n';
+
+ len = strlen (tmp->alternative[i].id);
+ memcpy (np, tmp->alternative[i].id, len);
+ np += len;
+ *np++ = '\n';
+
+ len = strlen (tmp->alternative[i].msgstr);
+ memcpy (np, tmp->alternative[i].msgstr, len);
+ np += len;
+ tmp->alternative[i].msgstr += len + 1;
+ }
+
+ /* Plural forms are separated by NUL bytes. */
+ *np++ = '\0';
+ }
+ tmp->msgstr = new_msgstr;
+ tmp->msgstr_len = np - new_msgstr;
+
+ tmp->is_fuzzy = true;
+ }
+
+ /* Test whether all alternative comments are equal. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ if (tmp->alternative[i].comment == NULL
+ || !string_list_equal (tmp->alternative[i].comment,
+ first->comment))
+ break;
+
+ if (i == tmp->alternative_count)
+ /* All alternatives are equal. */
+ tmp->comment = first->comment;
+ else
+ /* Concatenate the alternative comments into a single one,
+ separated by markers. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ {
+ string_list_ty *slp = tmp->alternative[i].comment;
+
+ if (slp != NULL)
+ {
+ size_t l;
+
+ message_comment_append (tmp, tmp->alternative[i].id);
+ for (l = 0; l < slp->nitems; l++)
+ message_comment_append (tmp, slp->item[l]);
+ }
+ }
+
+ /* Test whether all alternative dot comments are equal. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ if (tmp->alternative[i].comment_dot == NULL
+ || !string_list_equal (tmp->alternative[i].comment_dot,
+ first->comment_dot))
+ break;
+
+ if (i == tmp->alternative_count)
+ /* All alternatives are equal. */
+ tmp->comment_dot = first->comment_dot;
+ else
+ /* Concatenate the alternative dot comments into a single one,
+ separated by markers. */
+ for (i = 0; i < tmp->alternative_count; i++)
+ {
+ string_list_ty *slp = tmp->alternative[i].comment_dot;
+
+ if (slp != NULL)
+ {
+ size_t l;
+
+ message_comment_dot_append (tmp,
+ tmp->alternative[i].id);
+ for (l = 0; l < slp->nitems; l++)
+ message_comment_dot_append (tmp, slp->item[l]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return total_mdlp;
+}
diff --git a/gettext-tools/src/msgl-cat.h b/gettext-tools/src/msgl-cat.h
new file mode 100644
index 0000000..4de35d5
--- /dev/null
+++ b/gettext-tools/src/msgl-cat.h
@@ -0,0 +1,60 @@
+/* Message list concatenation and duplicate handling.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_CAT_H
+#define _MSGL_CAT_H
+
+#include <stdbool.h>
+
+#include "message.h"
+#include "str-list.h"
+#include "read-catalog-abstract.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* These variables control which messages are selected. */
+extern DLL_VARIABLE int more_than;
+extern DLL_VARIABLE int less_than;
+
+/* If true, use the first available translation.
+ If false, merge all available translations into one and fuzzy it. */
+extern DLL_VARIABLE bool use_first;
+
+/* If true, merge like msgcomm.
+ If false, merge like msgcat and msguniq. */
+extern DLL_VARIABLE bool msgcomm_mode;
+
+/* If true, omit the header entry.
+ If false, keep the header entry present in the input. */
+extern DLL_VARIABLE bool omit_header;
+
+extern msgdomain_list_ty *
+ catenate_msgdomain_list (string_list_ty *file_list,
+ catalog_input_format_ty input_syntax,
+ const char *to_code);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_CAT_H */
diff --git a/gettext-tools/src/msgl-charset.c b/gettext-tools/src/msgl-charset.c
new file mode 100644
index 0000000..e8deb58
--- /dev/null
+++ b/gettext-tools/src/msgl-charset.c
@@ -0,0 +1,133 @@
+/* Message list charset and locale charset handling.
+ Copyright (C) 2001-2003, 2005-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "msgl-charset.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "po-charset.h"
+#include "localcharset.h"
+#include "error.h"
+#include "progname.h"
+#include "basename.h"
+#include "xmalloca.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "message.h"
+#include "c-strstr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+void
+compare_po_locale_charsets (const msgdomain_list_ty *mdlp)
+{
+ const char *locale_code;
+ const char *canon_locale_code;
+ bool warned;
+ size_t j, k;
+
+ /* Check whether the locale encoding and the PO file's encoding are the
+ same. Otherwise emit a warning. */
+ locale_code = locale_charset ();
+ canon_locale_code = po_charset_canonicalize (locale_code);
+ warned = false;
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ const message_list_ty *mlp = mdlp->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+ const char *canon_charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ canon_charset = po_charset_canonicalize (charset);
+ if (canon_charset == NULL)
+ error (EXIT_FAILURE, 0,
+ _("\
+present charset \"%s\" is not a portable encoding name"),
+ charset);
+ freea (charset);
+ if (canon_locale_code != canon_charset)
+ {
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+Locale charset \"%s\" is different from\n\
+input file charset \"%s\".\n\
+Output of '%s' might be incorrect.\n\
+Possible workarounds are:\n\
+"), locale_code, canon_charset, basename (program_name)));
+ multiline_warning (NULL,
+ xasprintf (_("\
+- Set LC_ALL to a locale with encoding %s.\n\
+"), canon_charset));
+ if (canon_locale_code != NULL)
+ multiline_warning (NULL,
+ xasprintf (_("\
+- Convert the translation catalog to %s using 'msgconv',\n\
+ then apply '%s',\n\
+ then convert back to %s using 'msgconv'.\n\
+"), canon_locale_code, basename (program_name), canon_charset));
+ if (strcmp (canon_charset, "UTF-8") != 0
+ && (canon_locale_code == NULL
+ || strcmp (canon_locale_code, "UTF-8") != 0))
+ multiline_warning (NULL,
+ xasprintf (_("\
+- Set LC_ALL to a locale with encoding %s,\n\
+ convert the translation catalog to %s using 'msgconv',\n\
+ then apply '%s',\n\
+ then convert back to %s using 'msgconv'.\n\
+"), "UTF-8", "UTF-8", basename (program_name), canon_charset));
+ warned = true;
+ }
+ }
+ }
+ }
+ }
+ if (canon_locale_code == NULL && !warned)
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+Locale charset \"%s\" is not a portable encoding name.\n\
+Output of '%s' might be incorrect.\n\
+A possible workaround is to set LC_ALL=C.\n\
+"), locale_code, basename (program_name)));
+}
diff --git a/gettext-tools/src/msgl-charset.h b/gettext-tools/src/msgl-charset.h
new file mode 100644
index 0000000..647a394
--- /dev/null
+++ b/gettext-tools/src/msgl-charset.h
@@ -0,0 +1,38 @@
+/* Message list charset and locale charset handling.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_CHARSET_H
+#define _MSGL_CHARSET_H
+
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern void
+ compare_po_locale_charsets (const msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_CHARSET_H */
diff --git a/gettext-tools/src/msgl-check.c b/gettext-tools/src/msgl-check.c
new file mode 100644
index 0000000..d6f4a3d
--- /dev/null
+++ b/gettext-tools/src/msgl-check.c
@@ -0,0 +1,914 @@
+/* Checking of messages in PO files.
+ Copyright (C) 1995-1998, 2000-2008, 2010-2012 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "msgl-check.h"
+
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "c-ctype.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "format.h"
+#include "plural-exp.h"
+#include "plural-eval.h"
+#include "plural-table.h"
+#include "c-strstr.h"
+#include "message.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* Evaluates the plural formula for min <= n <= max
+ and returns the estimated number of times the value j was assumed. */
+static unsigned int
+plural_expression_histogram (const struct plural_distribution *self,
+ int min, int max, unsigned long j)
+{
+ if (min < 0)
+ min = 0;
+ /* Limit the number of evaluations. Nothing interesting happens beyond
+ 1000. */
+ if (max - min > 1000)
+ max = min + 1000;
+ if (min <= max)
+ {
+ const struct expression *expr = self->expr;
+ unsigned long n;
+ unsigned int count;
+
+ /* Protect against arithmetic exceptions. */
+ install_sigfpe_handler ();
+
+ count = 0;
+ for (n = min; n <= max; n++)
+ {
+ unsigned long val = plural_eval (expr, n);
+
+ if (val == j)
+ count++;
+ }
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ return count;
+ }
+ else
+ return 0;
+}
+
+
+/* Check the values returned by plural_eval.
+ Signals the errors through po_xerror.
+ Return the number of errors that were seen.
+ If no errors, returns in *DISTRIBUTION information about the plural_eval
+ values distribution. */
+int
+check_plural_eval (const struct expression *plural_expr,
+ unsigned long nplurals_value,
+ const message_ty *header,
+ struct plural_distribution *distribution)
+{
+ /* Do as if the plural formula assumes a value N infinitely often if it
+ assumes it at least 5 times. */
+#define OFTEN 5
+ unsigned char * volatile array;
+
+ /* Allocate a distribution array. */
+ if (nplurals_value <= 100)
+ array = XCALLOC (nplurals_value, unsigned char);
+ else
+ /* nplurals_value is nonsense. Don't risk an out-of-memory. */
+ array = NULL;
+
+ if (sigsetjmp (sigfpe_exit, 1) == 0)
+ {
+ unsigned long n;
+
+ /* Protect against arithmetic exceptions. */
+ install_sigfpe_handler ();
+
+ for (n = 0; n <= 1000; n++)
+ {
+ unsigned long val = plural_eval (plural_expr, n);
+
+ if ((long) val < 0)
+ {
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false,
+ _("plural expression can produce negative values"));
+ free (array);
+ return 1;
+ }
+ else if (val >= nplurals_value)
+ {
+ char *msg;
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ msg = xasprintf (_("nplurals = %lu but plural expression can produce values as large as %lu"),
+ nplurals_value, val);
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
+ free (msg);
+ free (array);
+ return 1;
+ }
+
+ if (array != NULL && array[val] < OFTEN)
+ array[val]++;
+ }
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+ /* Normalize the array[val] statistics. */
+ if (array != NULL)
+ {
+ unsigned long val;
+
+ for (val = 0; val < nplurals_value; val++)
+ array[val] = (array[val] == OFTEN ? 1 : 0);
+ }
+
+ distribution->expr = plural_expr;
+ distribution->often = array;
+ distribution->often_length = (array != NULL ? nplurals_value : 0);
+ distribution->histogram = plural_expression_histogram;
+
+ return 0;
+ }
+ else
+ {
+ /* Caught an arithmetic exception. */
+ const char *msg;
+
+ /* End of protection against arithmetic exceptions. */
+ uninstall_sigfpe_handler ();
+
+#if USE_SIGINFO
+ switch (sigfpe_code)
+#endif
+ {
+#if USE_SIGINFO
+# ifdef FPE_INTDIV
+ case FPE_INTDIV:
+ msg = _("plural expression can produce division by zero");
+ break;
+# endif
+# ifdef FPE_INTOVF
+ case FPE_INTOVF:
+ msg = _("plural expression can produce integer overflow");
+ break;
+# endif
+ default:
+#endif
+ msg = _("plural expression can produce arithmetic exceptions, possibly division by zero");
+ }
+
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
+
+ free (array);
+
+ return 1;
+ }
+#undef OFTEN
+}
+
+
+/* Try to help the translator by looking up the right plural formula for her.
+ Return a freshly allocated multiline help string, or NULL. */
+static char *
+plural_help (const char *nullentry)
+{
+ struct plural_table_entry *ptentry = NULL;
+
+ {
+ const char *language;
+
+ language = c_strstr (nullentry, "Language: ");
+ if (language != NULL)
+ {
+ size_t len;
+
+ language += 10;
+ len = strcspn (language, " \t\n");
+ if (len > 0)
+ {
+ size_t j;
+
+ for (j = 0; j < plural_table_size; j++)
+ if (len == strlen (plural_table[j].lang)
+ && strncmp (language, plural_table[j].lang, len) == 0)
+ {
+ ptentry = &plural_table[j];
+ break;
+ }
+ }
+ }
+ }
+
+ if (ptentry == NULL)
+ {
+ const char *language;
+
+ language = c_strstr (nullentry, "Language-Team: ");
+ if (language != NULL)
+ {
+ size_t j;
+
+ language += 15;
+ for (j = 0; j < plural_table_size; j++)
+ if (strncmp (language,
+ plural_table[j].language,
+ strlen (plural_table[j].language)) == 0)
+ {
+ ptentry = &plural_table[j];
+ break;
+ }
+ }
+ }
+
+ if (ptentry != NULL)
+ {
+ char *helpline1 =
+ xasprintf (_("Try using the following, valid for %s:"),
+ ptentry->language);
+ char *help =
+ xasprintf ("%s\n\"Plural-Forms: %s\\n\"\n",
+ helpline1, ptentry->value);
+ free (helpline1);
+ return help;
+ }
+ return NULL;
+}
+
+
+/* Perform plural expression checking.
+ Return the number of errors that were seen.
+ If no errors, returns in *DISTRIBUTION information about the plural_eval
+ values distribution. */
+static int
+check_plural (message_list_ty *mlp,
+ int ignore_untranslated_messages,
+ int ignore_fuzzy_messages,
+ struct plural_distribution *distributionp)
+{
+ int seen_errors = 0;
+ const message_ty *has_plural;
+ unsigned long min_nplurals;
+ const message_ty *min_pos;
+ unsigned long max_nplurals;
+ const message_ty *max_pos;
+ struct plural_distribution distribution;
+ size_t j;
+ message_ty *header;
+
+ /* Determine whether mlp has plural entries. */
+ has_plural = NULL;
+ min_nplurals = ULONG_MAX;
+ min_pos = NULL;
+ max_nplurals = 0;
+ max_pos = NULL;
+ distribution.expr = NULL;
+ distribution.often = NULL;
+ distribution.often_length = 0;
+ distribution.histogram = NULL;
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (!mp->obsolete
+ && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
+ && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp)))
+ && mp->msgid_plural != NULL)
+ {
+ const char *p;
+ const char *p_end;
+ unsigned long n;
+
+ if (has_plural == NULL)
+ has_plural = mp;
+
+ n = 0;
+ for (p = mp->msgstr, p_end = p + mp->msgstr_len;
+ p < p_end;
+ p += strlen (p) + 1)
+ n++;
+ if (min_nplurals > n)
+ {
+ min_nplurals = n;
+ min_pos = mp;
+ }
+ if (max_nplurals < n)
+ {
+ max_nplurals = n;
+ max_pos = mp;
+ }
+ }
+ }
+
+ /* Look at the plural entry for this domain.
+ Cf, function extract_plural_expression. */
+ header = message_list_search (mlp, NULL, "");
+ if (header != NULL && !header->obsolete)
+ {
+ const char *nullentry;
+ const char *plural;
+ const char *nplurals;
+
+ nullentry = header->msgstr;
+
+ plural = c_strstr (nullentry, "plural=");
+ nplurals = c_strstr (nullentry, "nplurals=");
+ if (plural == NULL && has_plural != NULL)
+ {
+ const char *msg1 =
+ _("message catalog has plural form translations");
+ const char *msg2 =
+ _("but header entry lacks a \"plural=EXPRESSION\" attribute");
+ char *help = plural_help (nullentry);
+
+ if (help != NULL)
+ {
+ char *msg2ext = xasprintf ("%s\n%s", msg2, help);
+ po_xerror2 (PO_SEVERITY_ERROR,
+ has_plural, NULL, 0, 0, false, msg1,
+ header, NULL, 0, 0, true, msg2ext);
+ free (msg2ext);
+ free (help);
+ }
+ else
+ po_xerror2 (PO_SEVERITY_ERROR,
+ has_plural, NULL, 0, 0, false, msg1,
+ header, NULL, 0, 0, false, msg2);
+
+ seen_errors++;
+ }
+ if (nplurals == NULL && has_plural != NULL)
+ {
+ const char *msg1 =
+ _("message catalog has plural form translations");
+ const char *msg2 =
+ _("but header entry lacks a \"nplurals=INTEGER\" attribute");
+ char *help = plural_help (nullentry);
+
+ if (help != NULL)
+ {
+ char *msg2ext = xasprintf ("%s\n%s", msg2, help);
+ po_xerror2 (PO_SEVERITY_ERROR,
+ has_plural, NULL, 0, 0, false, msg1,
+ header, NULL, 0, 0, true, msg2ext);
+ free (msg2ext);
+ free (help);
+ }
+ else
+ po_xerror2 (PO_SEVERITY_ERROR,
+ has_plural, NULL, 0, 0, false, msg1,
+ header, NULL, 0, 0, false, msg2);
+
+ seen_errors++;
+ }
+ if (plural != NULL && nplurals != NULL)
+ {
+ const char *endp;
+ unsigned long int nplurals_value;
+ struct parse_args args;
+ const struct expression *plural_expr;
+
+ /* First check the number. */
+ nplurals += 9;
+ while (*nplurals != '\0' && c_isspace ((unsigned char) *nplurals))
+ ++nplurals;
+ endp = nplurals;
+ nplurals_value = 0;
+ if (*nplurals >= '0' && *nplurals <= '9')
+ nplurals_value = strtoul (nplurals, (char **) &endp, 10);
+ if (nplurals == endp)
+ {
+ const char *msg = _("invalid nplurals value");
+ char *help = plural_help (nullentry);
+
+ if (help != NULL)
+ {
+ char *msgext = xasprintf ("%s\n%s", msg, help);
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
+ msgext);
+ free (msgext);
+ free (help);
+ }
+ else
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
+
+ seen_errors++;
+ }
+
+ /* Then check the expression. */
+ plural += 7;
+ args.cp = plural;
+ if (parse_plural_expression (&args) != 0)
+ {
+ const char *msg = _("invalid plural expression");
+ char *help = plural_help (nullentry);
+
+ if (help != NULL)
+ {
+ char *msgext = xasprintf ("%s\n%s", msg, help);
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, true,
+ msgext);
+ free (msgext);
+ free (help);
+ }
+ else
+ po_xerror (PO_SEVERITY_ERROR, header, NULL, 0, 0, false, msg);
+
+ seen_errors++;
+ }
+ plural_expr = args.res;
+
+ /* See whether nplurals and plural fit together. */
+ if (!seen_errors)
+ seen_errors =
+ check_plural_eval (plural_expr, nplurals_value, header,
+ &distribution);
+
+ /* Check the number of plurals of the translations. */
+ if (!seen_errors)
+ {
+ if (min_nplurals < nplurals_value)
+ {
+ char *msg1 =
+ xasprintf (_("nplurals = %lu"), nplurals_value);
+ char *msg2 =
+ xasprintf (ngettext ("but some messages have only one plural form",
+ "but some messages have only %lu plural forms",
+ min_nplurals),
+ min_nplurals);
+ po_xerror2 (PO_SEVERITY_ERROR,
+ header, NULL, 0, 0, false, msg1,
+ min_pos, NULL, 0, 0, false, msg2);
+ free (msg2);
+ free (msg1);
+ seen_errors++;
+ }
+ else if (max_nplurals > nplurals_value)
+ {
+ char *msg1 =
+ xasprintf (_("nplurals = %lu"), nplurals_value);
+ char *msg2 =
+ xasprintf (ngettext ("but some messages have one plural form",
+ "but some messages have %lu plural forms",
+ max_nplurals),
+ max_nplurals);
+ po_xerror2 (PO_SEVERITY_ERROR,
+ header, NULL, 0, 0, false, msg1,
+ max_pos, NULL, 0, 0, false, msg2);
+ free (msg2);
+ free (msg1);
+ seen_errors++;
+ }
+ /* The only valid case is max_nplurals <= n <= min_nplurals,
+ which means either has_plural == NULL or
+ max_nplurals = n = min_nplurals. */
+ }
+ }
+ else
+ goto no_plural;
+ }
+ else
+ {
+ if (has_plural != NULL)
+ {
+ po_xerror (PO_SEVERITY_ERROR, has_plural, NULL, 0, 0, false,
+ _("message catalog has plural form translations, but lacks a header entry with \"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\""));
+ seen_errors++;
+ }
+ no_plural:
+ /* By default, the Germanic formula (n != 1) is used. */
+ distribution.expr = &germanic_plural;
+ {
+ unsigned char *array = XCALLOC (2, unsigned char);
+ array[1] = 1;
+ distribution.often = array;
+ }
+ distribution.often_length = 2;
+ distribution.histogram = plural_expression_histogram;
+ }
+
+ /* distribution is not needed if we report errors.
+ Also, if there was an error due to max_nplurals > nplurals_value,
+ we must not use distribution because we would be doing out-of-bounds
+ array accesses. */
+ if (seen_errors > 0)
+ free ((unsigned char *) distribution.often);
+ else
+ *distributionp = distribution;
+
+ return seen_errors;
+}
+
+
+/* Signal an error when checking format strings. */
+static const message_ty *curr_mp;
+static lex_pos_ty curr_msgid_pos;
+static void
+formatstring_error_logger (const char *format, ...)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ > 2)
+ __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+;
+static void
+formatstring_error_logger (const char *format, ...)
+{
+ va_list args;
+ char *msg;
+
+ va_start (args, format);
+ if (vasprintf (&msg, format, args) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ va_end (args);
+ po_xerror (PO_SEVERITY_ERROR,
+ curr_mp, curr_msgid_pos.file_name, curr_msgid_pos.line_number,
+ (size_t)(-1), false, msg);
+ free (msg);
+}
+
+
+/* Perform miscellaneous checks on a message.
+ PLURAL_DISTRIBUTION is either NULL or an array of nplurals elements,
+ PLURAL_DISTRIBUTION[j] being true if the value j appears to be assumed
+ infinitely often by the plural formula.
+ PLURAL_DISTRIBUTION_LENGTH is the length of the PLURAL_DISTRIBUTION
+ array. */
+static int
+check_pair (const message_ty *mp,
+ const char *msgid,
+ const lex_pos_ty *msgid_pos,
+ const char *msgid_plural,
+ const char *msgstr, size_t msgstr_len,
+ const enum is_format is_format[NFORMATS],
+ int check_newlines,
+ int check_format_strings,
+ const struct plural_distribution *distribution,
+ int check_compatibility,
+ int check_accelerators, char accelerator_char)
+{
+ int seen_errors;
+ int has_newline;
+ unsigned int j;
+
+ /* If the msgid string is empty we have the special entry reserved for
+ information about the translation. */
+ if (msgid[0] == '\0')
+ return 0;
+
+ seen_errors = 0;
+
+ if (check_newlines)
+ {
+ /* Test 1: check whether all or none of the strings begin with a '\n'. */
+ has_newline = (msgid[0] == '\n');
+#define TEST_NEWLINE(p) (p[0] == '\n')
+ if (msgid_plural != NULL)
+ {
+ const char *p;
+
+ if (TEST_NEWLINE(msgid_plural) != has_newline)
+ {
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("\
+'msgid' and 'msgid_plural' entries do not both begin with '\\n'"));
+ seen_errors++;
+ }
+ for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
+ if (TEST_NEWLINE(p) != has_newline)
+ {
+ char *msg =
+ xasprintf (_("\
+'msgid' and 'msgstr[%u]' entries do not both begin with '\\n'"), j);
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, msg);
+ free (msg);
+ seen_errors++;
+ }
+ }
+ else
+ {
+ if (TEST_NEWLINE(msgstr) != has_newline)
+ {
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("\
+'msgid' and 'msgstr' entries do not both begin with '\\n'"));
+ seen_errors++;
+ }
+ }
+#undef TEST_NEWLINE
+
+ /* Test 2: check whether all or none of the strings end with a '\n'. */
+ has_newline = (msgid[strlen (msgid) - 1] == '\n');
+#define TEST_NEWLINE(p) (p[0] != '\0' && p[strlen (p) - 1] == '\n')
+ if (msgid_plural != NULL)
+ {
+ const char *p;
+
+ if (TEST_NEWLINE(msgid_plural) != has_newline)
+ {
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("\
+'msgid' and 'msgid_plural' entries do not both end with '\\n'"));
+ seen_errors++;
+ }
+ for (p = msgstr, j = 0; p < msgstr + msgstr_len; p += strlen (p) + 1, j++)
+ if (TEST_NEWLINE(p) != has_newline)
+ {
+ char *msg =
+ xasprintf (_("\
+'msgid' and 'msgstr[%u]' entries do not both end with '\\n'"), j);
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, msg);
+ free (msg);
+ seen_errors++;
+ }
+ }
+ else
+ {
+ if (TEST_NEWLINE(msgstr) != has_newline)
+ {
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("\
+'msgid' and 'msgstr' entries do not both end with '\\n'"));
+ seen_errors++;
+ }
+ }
+#undef TEST_NEWLINE
+ }
+
+ if (check_compatibility && msgid_plural != NULL)
+ {
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("\
+plural handling is a GNU gettext extension"));
+ seen_errors++;
+ }
+
+ if (check_format_strings)
+ /* Test 3: Check whether both formats strings contain the same number
+ of format specifications. */
+ {
+ curr_mp = mp;
+ curr_msgid_pos = *msgid_pos;
+ seen_errors +=
+ check_msgid_msgstr_format (msgid, msgid_plural, msgstr, msgstr_len,
+ is_format, mp->range, distribution,
+ formatstring_error_logger);
+ }
+
+ if (check_accelerators && msgid_plural == NULL)
+ /* Test 4: Check that if msgid is a menu item with a keyboard accelerator,
+ the msgstr has an accelerator as well. A keyboard accelerator is
+ designated by an immediately preceding '&'. We cannot check whether
+ two accelerators collide, only whether the translator has bothered
+ thinking about them. */
+ {
+ const char *p;
+
+ /* We are only interested in msgids that contain exactly one '&'. */
+ p = strchr (msgid, accelerator_char);
+ if (p != NULL && strchr (p + 1, accelerator_char) == NULL)
+ {
+ /* Count the number of '&' in msgstr, but ignore '&&'. */
+ unsigned int count = 0;
+
+ for (p = msgstr; (p = strchr (p, accelerator_char)) != NULL; p++)
+ if (p[1] == accelerator_char)
+ p++;
+ else
+ count++;
+
+ if (count == 0)
+ {
+ char *msg =
+ xasprintf (_("msgstr lacks the keyboard accelerator mark '%c'"),
+ accelerator_char);
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, msg);
+ free (msg);
+ seen_errors++;
+ }
+ else if (count > 1)
+ {
+ char *msg =
+ xasprintf (_("msgstr has too many keyboard accelerator marks '%c'"),
+ accelerator_char);
+ po_xerror (PO_SEVERITY_ERROR,
+ mp, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, msg);
+ free (msg);
+ seen_errors++;
+ }
+ }
+ }
+
+ return seen_errors;
+}
+
+
+/* Perform miscellaneous checks on a header entry. */
+static int
+check_header_entry (const message_ty *mp, const char *msgstr_string)
+{
+ static const char *required_fields[] =
+ {
+ "Project-Id-Version", "PO-Revision-Date", "Last-Translator",
+ "Language-Team", "MIME-Version", "Content-Type",
+ "Content-Transfer-Encoding",
+ /* These are recommended but not yet required. */
+ "Language"
+ };
+ static const char *default_values[] =
+ {
+ "PACKAGE VERSION", "YEAR-MO-DA HO:MI+ZONE", "FULL NAME <EMAIL@ADDRESS>", "LANGUAGE <LL@li.org>", NULL,
+ "text/plain; charset=CHARSET", "ENCODING",
+ ""
+ };
+ const size_t nfields = SIZEOF (required_fields);
+ /* FIXME: We could check if a required header field is missing and
+ report it as error. However, it's could be too rigorous and
+ break backward compatibility. */
+#if 0
+ const size_t nrequiredfields = nfields - 1;
+#endif
+ int seen_errors = 0;
+ int cnt;
+
+ for (cnt = 0; cnt < nfields; ++cnt)
+ {
+#if 0
+ int severity =
+ (cnt < nrequiredfields ? PO_SEVERITY_ERROR : PO_SEVERITY_WARNING);
+#else
+ int severity =
+ PO_SEVERITY_WARNING;
+#endif
+ const char *field = required_fields[cnt];
+ size_t len = strlen (field);
+ const char *line;
+
+ for (line = msgstr_string; *line != '\0'; )
+ {
+ if (strncmp (line, field, len) == 0 && line[len] == ':')
+ {
+ const char *p = line + len + 1;
+
+ /* Test whether the field's value, starting at p, is the default
+ value. */
+ if (*p == ' ')
+ p++;
+ if (default_values[cnt] != NULL
+ && strncmp (p, default_values[cnt],
+ strlen (default_values[cnt])) == 0)
+ {
+ p += strlen (default_values[cnt]);
+ if (*p == '\0' || *p == '\n')
+ {
+ char *msg =
+ xasprintf (_("header field '%s' still has the initial default value\n"),
+ field);
+ po_xerror (severity, mp, NULL, 0, 0, true, msg);
+ free (msg);
+ if (severity == PO_SEVERITY_ERROR)
+ seen_errors++;
+ }
+ }
+ break;
+ }
+ line = strchrnul (line, '\n');
+ if (*line == '\n')
+ line++;
+ }
+ if (*line == '\0')
+ {
+ char *msg =
+ xasprintf (_("header field '%s' missing in header\n"),
+ field);
+ po_xerror (severity, mp, NULL, 0, 0, true, msg);
+ free (msg);
+ if (severity == PO_SEVERITY_ERROR)
+ seen_errors++;
+ }
+ }
+ return seen_errors;
+}
+
+
+/* Perform all checks on a non-obsolete message.
+ Return the number of errors that were seen. */
+int
+check_message (const message_ty *mp,
+ const lex_pos_ty *msgid_pos,
+ int check_newlines,
+ int check_format_strings,
+ const struct plural_distribution *distribution,
+ int check_header,
+ int check_compatibility,
+ int check_accelerators, char accelerator_char)
+{
+ int seen_errors = 0;
+
+ if (check_header && is_header (mp))
+ seen_errors += check_header_entry (mp, mp->msgstr);
+
+ seen_errors += check_pair (mp,
+ mp->msgid, msgid_pos, mp->msgid_plural,
+ mp->msgstr, mp->msgstr_len,
+ mp->is_format,
+ check_newlines,
+ check_format_strings,
+ distribution,
+ check_compatibility,
+ check_accelerators, accelerator_char);
+ return seen_errors;
+}
+
+
+/* Perform all checks on a message list.
+ Return the number of errors that were seen. */
+int
+check_message_list (message_list_ty *mlp,
+ int ignore_untranslated_messages,
+ int ignore_fuzzy_messages,
+ int check_newlines,
+ int check_format_strings,
+ int check_header,
+ int check_compatibility,
+ int check_accelerators, char accelerator_char)
+{
+ int seen_errors = 0;
+ struct plural_distribution distribution;
+ size_t j;
+
+ distribution.expr = NULL;
+ distribution.often = NULL;
+ distribution.often_length = 0;
+ distribution.histogram = NULL;
+
+ if (check_header)
+ seen_errors += check_plural (mlp, ignore_untranslated_messages,
+ ignore_fuzzy_messages, &distribution);
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (!mp->obsolete
+ && !(ignore_untranslated_messages && mp->msgstr[0] == '\0')
+ && !(ignore_fuzzy_messages && (mp->is_fuzzy && !is_header (mp))))
+ seen_errors += check_message (mp, &mp->pos,
+ check_newlines,
+ check_format_strings,
+ &distribution,
+ check_header, check_compatibility,
+ check_accelerators, accelerator_char);
+ }
+
+ return seen_errors;
+}
diff --git a/gettext-tools/src/msgl-check.h b/gettext-tools/src/msgl-check.h
new file mode 100644
index 0000000..f03300c
--- /dev/null
+++ b/gettext-tools/src/msgl-check.h
@@ -0,0 +1,68 @@
+/* Checking of messages in PO files.
+ Copyright (C) 2005, 2008, 2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_CHECK_H
+#define _MSGL_CHECK_H 1
+
+#include "message.h"
+#include "pos.h"
+#include "plural-eval.h"
+#include "plural-distrib.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Check the values returned by plural_eval.
+ Signals the errors through po_xerror.
+ Return the number of errors that were seen.
+ If no errors, returns in *DISTRIBUTION information about the plural_eval
+ values distribution. */
+extern int check_plural_eval (const struct expression *plural_expr,
+ unsigned long nplurals_value,
+ const message_ty *header,
+ struct plural_distribution *distribution);
+
+/* Perform all checks on a non-obsolete message. */
+extern int check_message (const message_ty *mp,
+ const lex_pos_ty *msgid_pos,
+ int check_newlines,
+ int check_format_strings,
+ const struct plural_distribution *distribution,
+ int check_header,
+ int check_compatibility,
+ int check_accelerators, char accelerator_char);
+
+/* Perform all checks on a message list.
+ Return the number of errors that were seen. */
+extern int check_message_list (message_list_ty *mlp,
+ int ignore_untranslated_messages,
+ int ignore_fuzzy_messages,
+ int check_newlines,
+ int check_format_strings,
+ int check_header,
+ int check_compatibility,
+ int check_accelerators, char accelerator_char);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGL_CHECK_H */
diff --git a/gettext-tools/src/msgl-english.c b/gettext-tools/src/msgl-english.c
new file mode 100644
index 0000000..69d95a9
--- /dev/null
+++ b/gettext-tools/src/msgl-english.c
@@ -0,0 +1,70 @@
+/* Message translation initialization for English.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "msgl-english.h"
+
+#include <string.h>
+
+#include "xalloc.h"
+
+
+msgdomain_list_ty *
+msgdomain_list_english (msgdomain_list_ty *mdlp)
+{
+ size_t j, k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural == NULL)
+ {
+ if (mp->msgstr_len == 1 && mp->msgstr[0] == '\0')
+ {
+ mp->msgstr = mp->msgid; /* no need for xstrdup */
+ mp->msgstr_len = strlen (mp->msgid) + 1;
+ }
+ }
+ else
+ {
+ if (mp->msgstr_len == 2
+ && mp->msgstr[0] == '\0' && mp->msgstr[1] == '\0')
+ {
+ size_t len0 = strlen (mp->msgid) + 1;
+ size_t len1 = strlen (mp->msgid_plural) + 1;
+ char *cp = XNMALLOC (len0 + len1, char);
+ memcpy (cp, mp->msgid, len0);
+ memcpy (cp + len0, mp->msgid_plural, len1);
+ mp->msgstr = cp;
+ mp->msgstr_len = len0 + len1;
+ }
+ }
+ }
+ }
+
+ return mdlp;
+}
diff --git a/gettext-tools/src/msgl-english.h b/gettext-tools/src/msgl-english.h
new file mode 100644
index 0000000..ec77d2a
--- /dev/null
+++ b/gettext-tools/src/msgl-english.h
@@ -0,0 +1,38 @@
+/* Message translation initialization for English.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_ENGLISH_H
+#define _MSGL_ENGLISH_H
+
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern msgdomain_list_ty *
+ msgdomain_list_english (msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_ENGLISH_H */
diff --git a/gettext-tools/src/msgl-equal.c b/gettext-tools/src/msgl-equal.c
new file mode 100644
index 0000000..3c9efad
--- /dev/null
+++ b/gettext-tools/src/msgl-equal.c
@@ -0,0 +1,250 @@
+/* Message list test for equality.
+ Copyright (C) 2001-2002, 2005-2006, 2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "msgl-equal.h"
+
+#include <stddef.h>
+#include <string.h>
+
+
+static inline bool
+msgstr_equal (const char *msgstr1, size_t msgstr1_len,
+ const char *msgstr2, size_t msgstr2_len)
+{
+ return (msgstr1_len == msgstr2_len
+ && memcmp (msgstr1, msgstr2, msgstr1_len) == 0);
+}
+
+static bool
+msgstr_equal_ignoring_potcdate (const char *msgstr1, size_t msgstr1_len,
+ const char *msgstr2, size_t msgstr2_len)
+{
+ const char *msgstr1_end = msgstr1 + msgstr1_len;
+ const char *msgstr2_end = msgstr2 + msgstr2_len;
+ const char *ptr1;
+ const char *ptr2;
+ const char *const field = "POT-Creation-Date:";
+ const ptrdiff_t fieldlen = sizeof ("POT-Creation-Date:") - 1;
+
+ /* Search for the occurrence of field in msgstr1. */
+ for (ptr1 = msgstr1;;)
+ {
+ if (msgstr1_end - ptr1 < fieldlen)
+ {
+ ptr1 = NULL;
+ break;
+ }
+ if (memcmp (ptr1, field, fieldlen) == 0)
+ break;
+ ptr1 = (const char *) memchr (ptr1, '\n', msgstr1_end - ptr1);
+ if (ptr1 == NULL)
+ break;
+ ptr1++;
+ }
+
+ /* Search for the occurrence of field in msgstr2. */
+ for (ptr2 = msgstr2;;)
+ {
+ if (msgstr2_end - ptr2 < fieldlen)
+ {
+ ptr2 = NULL;
+ break;
+ }
+ if (memcmp (ptr2, field, fieldlen) == 0)
+ break;
+ ptr2 = (const char *) memchr (ptr2, '\n', msgstr2_end - ptr2);
+ if (ptr2 == NULL)
+ break;
+ ptr2++;
+ }
+
+ if (ptr1 == NULL)
+ {
+ if (ptr2 == NULL)
+ return msgstr_equal (msgstr1, msgstr1_len, msgstr2, msgstr2_len);
+ }
+ else
+ {
+ if (ptr2 != NULL)
+ {
+ /* Compare, ignoring the lines starting at ptr1 and ptr2. */
+ if (msgstr_equal (msgstr1, ptr1 - msgstr1, msgstr2, ptr2 - msgstr2))
+ {
+ ptr1 = (const char *) memchr (ptr1, '\n', msgstr1_end - ptr1);
+ if (ptr1 == NULL)
+ ptr1 = msgstr1_end;
+
+ ptr2 = (const char *) memchr (ptr2, '\n', msgstr2_end - ptr2);
+ if (ptr2 == NULL)
+ ptr2 = msgstr2_end;
+
+ return msgstr_equal (ptr1, msgstr1_end - ptr1,
+ ptr2, msgstr2_end - ptr2);
+ }
+ }
+ }
+ return false;
+}
+
+static inline bool
+pos_equal (const lex_pos_ty *pos1, const lex_pos_ty *pos2)
+{
+ return ((pos1->file_name == pos2->file_name
+ || strcmp (pos1->file_name, pos2->file_name) == 0)
+ && pos1->line_number == pos2->line_number);
+}
+
+bool
+string_list_equal (const string_list_ty *slp1, const string_list_ty *slp2)
+{
+ size_t i, i1, i2;
+
+ i1 = (slp1 != NULL ? slp1->nitems : 0);
+ i2 = (slp2 != NULL ? slp2->nitems : 0);
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (strcmp (slp1->item[i], slp2->item[i]) != 0)
+ return false;
+ return true;
+}
+
+bool
+message_equal (const message_ty *mp1, const message_ty *mp2,
+ bool ignore_potcdate)
+{
+ size_t i, i1, i2;
+
+ if (!(mp1->msgctxt != NULL
+ ? mp2->msgctxt != NULL && strcmp (mp1->msgctxt, mp2->msgctxt) == 0
+ : mp2->msgctxt == NULL))
+ return false;
+
+ if (strcmp (mp1->msgid, mp2->msgid) != 0)
+ return false;
+
+ if (!(mp1->msgid_plural != NULL
+ ? mp2->msgid_plural != NULL
+ && strcmp (mp1->msgid_plural, mp2->msgid_plural) == 0
+ : mp2->msgid_plural == NULL))
+ return false;
+
+ if (is_header (mp1) && ignore_potcdate
+ ? !msgstr_equal_ignoring_potcdate (mp1->msgstr, mp1->msgstr_len,
+ mp2->msgstr, mp2->msgstr_len)
+ : !msgstr_equal (mp1->msgstr, mp1->msgstr_len,
+ mp2->msgstr, mp2->msgstr_len))
+ return false;
+
+ if (!pos_equal (&mp1->pos, &mp2->pos))
+ return false;
+
+ if (!string_list_equal (mp1->comment, mp2->comment))
+ return false;
+
+ if (!string_list_equal (mp1->comment_dot, mp2->comment_dot))
+ return false;
+
+ i1 = mp1->filepos_count;
+ i2 = mp2->filepos_count;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!pos_equal (&mp1->filepos[i], &mp2->filepos[i]))
+ return false;
+
+ if (mp1->is_fuzzy != mp2->is_fuzzy)
+ return false;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (mp1->is_format[i] != mp2->is_format[i])
+ return false;
+
+ if (!(mp1->range.min == mp2->range.min && mp1->range.max == mp2->range.max))
+ return false;
+
+ if (!(mp1->prev_msgctxt != NULL
+ ? mp2->prev_msgctxt != NULL
+ && strcmp (mp1->prev_msgctxt, mp2->prev_msgctxt) == 0
+ : mp2->prev_msgctxt == NULL))
+ return false;
+
+ if (!(mp1->prev_msgid != NULL
+ ? mp2->prev_msgid != NULL
+ && strcmp (mp1->prev_msgid, mp2->prev_msgid) == 0
+ : mp2->prev_msgid == NULL))
+ return false;
+
+ if (!(mp1->prev_msgid_plural != NULL
+ ? mp2->prev_msgid_plural != NULL
+ && strcmp (mp1->prev_msgid_plural, mp2->prev_msgid_plural) == 0
+ : mp2->prev_msgid_plural == NULL))
+ return false;
+
+ if (mp1->obsolete != mp2->obsolete)
+ return false;
+
+ return true;
+}
+
+bool
+message_list_equal (const message_list_ty *mlp1, const message_list_ty *mlp2,
+ bool ignore_potcdate)
+{
+ size_t i, i1, i2;
+
+ i1 = mlp1->nitems;
+ i2 = mlp2->nitems;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!message_equal (mlp1->item[i], mlp2->item[i], ignore_potcdate))
+ return false;
+ return true;
+}
+
+static inline bool
+msgdomain_equal (const msgdomain_ty *mdp1, const msgdomain_ty *mdp2,
+ bool ignore_potcdate)
+{
+ return (strcmp (mdp1->domain, mdp2->domain) == 0
+ && message_list_equal (mdp1->messages, mdp2->messages,
+ ignore_potcdate));
+}
+
+bool
+msgdomain_list_equal (const msgdomain_list_ty *mdlp1,
+ const msgdomain_list_ty *mdlp2,
+ bool ignore_potcdate)
+{
+ size_t i, i1, i2;
+
+ i1 = mdlp1->nitems;
+ i2 = mdlp2->nitems;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!msgdomain_equal (mdlp1->item[i], mdlp2->item[i], ignore_potcdate))
+ return false;
+ return true;
+}
diff --git a/gettext-tools/src/msgl-equal.h b/gettext-tools/src/msgl-equal.h
new file mode 100644
index 0000000..6497a7f
--- /dev/null
+++ b/gettext-tools/src/msgl-equal.h
@@ -0,0 +1,56 @@
+/* Message list test for equality.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_EQUAL_H
+#define _MSGL_EQUAL_H
+
+#include <stdbool.h>
+
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern bool
+ string_list_equal (const string_list_ty *slp1,
+ const string_list_ty *slp2);
+
+/* Test whether the written representation of two messages / message lists
+ would be the same. */
+
+extern bool
+ message_equal (const message_ty *mp1, const message_ty *mp2,
+ bool ignore_potcdate);
+extern bool
+ message_list_equal (const message_list_ty *mlp1,
+ const message_list_ty *mlp2,
+ bool ignore_potcdate);
+extern bool
+ msgdomain_list_equal (const msgdomain_list_ty *mdlp1,
+ const msgdomain_list_ty *mdlp2,
+ bool ignore_potcdate);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_EQUAL_H */
diff --git a/gettext-tools/src/msgl-fsearch.c b/gettext-tools/src/msgl-fsearch.c
new file mode 100644
index 0000000..d2aa865
--- /dev/null
+++ b/gettext-tools/src/msgl-fsearch.c
@@ -0,0 +1,668 @@
+/* Fast fuzzy searching among messages.
+ Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "msgl-fsearch.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "xalloc.h"
+#include "po-charset.h"
+
+
+/* Fuzzy searching of L strings in a large set of N messages (assuming
+ they have all the same small size) takes O(L * N) when using repeated
+ fstrcmp() calls. When for example L = 800 and N = 69000, this is slow.
+
+ So we preprocess the set of N messages, yielding a data structure
+ that allows to select the similar messages for any given string, and
+ apply fstrcmp() only to the search result. This allows to reduce the
+ time to something between O(L * 1) and O(L * N) - depending on the
+ structure of the strings. In the example with L = 800 and N = 69000,
+ memory use increases by a factor of 2 and the time decreases from
+ 9068 s to 230 s.
+
+ The data structure is a hash table mapping each n-gram (here with n=4)
+ occurring in the message strings to the list of messages that contains
+ it. For example, if the message list is
+ [0] "close"
+ [1] "losers"
+ the index is a hash table mapping
+ "clos" -> { 0 }
+ "lose" -> { 0, 1 }
+ "oser" -> { 1 }
+ "sers" -> { 1 }
+ Searching the similar messages to, say, "closest", is done by looking up
+ all its 4-grams in the hash table and summing up the results:
+ "clos" -> { 0 }
+ "lose" -> { 0, 1 }
+ "oses" -> {}
+ "sest" -> {}
+ => total: { 2x0, 1x1 }
+ The list is sorted according to decreasing frequency: { 0, 1, ... }
+ and only the first few messages from this frequency list are passed to
+ fstrcmp().
+
+ The n-gram similarity and the fstrcmp() similarity are quite different
+ metrics. For example, "close" and "c l o s e" have no n-grams in common
+ (even for n as small as n = 2); however, fstrcmp() will find that they
+ are quite similar (= 0.71). Conversely, "AAAA BBBB ... ZZZZ" and
+ "ZZZZ ... BBBB AAAA" have many 4-grams in common, but their fstrcmp() is
+ only 0.02. Also, repeated n-grams don't have the same effect on the
+ two similarity measures. But all this doesn't matter much in practice.
+
+ We chose n = 4 because for alphabetic languages, with n = 3 the occurrence
+ lists are likely too long. (Well, with ideographic languages like Chinese,
+ n = 3 might actually be quite good. Anyway, n = 4 is not bad in this case
+ either.)
+
+ The units are characters in the current encoding. Not just bytes,
+ because 4 consecutive bytes in UTF-8 or GB18030 don't mean much. */
+
+/* Each message is represented by its index in the message list. */
+typedef unsigned int index_ty;
+
+/* An index list has its allocated size and length tacked at the front.
+ The indices are sorted in ascending order. */
+typedef index_ty *index_list_ty;
+#define IL_ALLOCATED 0
+#define IL_LENGTH 1
+
+/* Create a new index list containing only a given index. */
+static inline index_list_ty
+new_index (index_ty idx)
+{
+ index_ty *list = XNMALLOC (2 + 1, index_ty);
+ list[IL_ALLOCATED] = 1;
+ list[IL_LENGTH] = 1;
+ list[2] = idx;
+
+ return list;
+}
+
+/* Add a given index, greater or equal than all previous indices, to an
+ index list.
+ Return the new index list, if it had to be reallocated, or NULL if it
+ didn't change. */
+static inline index_list_ty
+addlast_index (index_list_ty list, index_ty idx)
+{
+ index_list_ty result;
+ size_t length = list[IL_LENGTH];
+
+ /* Look whether it should be inserted. */
+ if (length > 0 && list[2 + (length - 1)] == idx)
+ return NULL;
+
+ /* Now make room for one more list element. */
+ result = NULL;
+ if (length == list[IL_ALLOCATED])
+ {
+ size_t new_allocated = 2 * length - (length >> 6);
+ list = (index_ty *) xrealloc (list, (2 + new_allocated) * sizeof (index_ty));
+ list[IL_ALLOCATED] = new_allocated;
+ result = list;
+ }
+ list[2 + length] = idx;
+ list[IL_LENGTH] = length + 1;
+ return result;
+}
+
+/* Add a given index to an index list.
+ Return the new index list, if it had to be reallocated, or NULL if it
+ didn't change. */
+static inline index_list_ty
+add_index (index_list_ty list, index_ty idx)
+{
+ index_list_ty result;
+ size_t length = list[IL_LENGTH];
+
+ /* Look where it should be inserted. */
+ size_t lo = 0;
+ size_t hi = length;
+ while (lo < hi)
+ {
+ /* Here we know that idx must be inserted at a position >= lo, <= hi. */
+ size_t mid = (lo + hi) / 2; /* lo <= mid < hi */
+ index_ty val = list[2 + mid];
+ if (val < idx)
+ lo = mid + 1;
+ else if (val > idx)
+ hi = mid;
+ else
+ return NULL;
+ }
+
+ /* Now make room for one more list element. */
+ result = NULL;
+ if (length == list[IL_ALLOCATED])
+ {
+ size_t new_allocated = 2 * length - (length >> 6);
+ list = (index_ty *) xrealloc (list, (2 + new_allocated) * sizeof (index_ty));
+ list[IL_ALLOCATED] = new_allocated;
+ result = list;
+ }
+ list[IL_LENGTH] = length + 1;
+ for (; length > hi; length--)
+ list[2 + length] = list[1 + length];
+ list[2 + length] = idx;
+ return result;
+}
+
+/* We use 4-grams, therefore strings with less than 4 characters cannot be
+ handled through the 4-grams table and need to be handled specially.
+ Since every character occupies at most 4 bytes (see po-charset.c),
+ this means the size of such short strings is bounded by: */
+#define SHORT_STRING_MAX_CHARACTERS (4 - 1)
+#define SHORT_STRING_MAX_BYTES (SHORT_STRING_MAX_CHARACTERS * 4)
+
+/* Such short strings are handled by direct comparison with all messages
+ of appropriate size. Note that for two strings of length 0 <= l1 <= l2,
+ fstrcmp() is <= 2 * l1 / (l1 + l2). Since we are only interested in
+ fstrcmp() values >= FUZZY_THRESHOLD, we can for a given string of length l
+ limit the search to lengths l' in the range
+ l / (2 / FUZZY_THRESHOLD - 1) <= l' <= l * (2 / FUZZY_THRESHOLD - 1)
+ Thus we need the list of the short strings up to length: */
+#if !defined __SUNPRO_C
+# define SHORT_MSG_MAX (int) (SHORT_STRING_MAX_BYTES * (2 / FUZZY_THRESHOLD - 1))
+#else
+/* Sun C on Solaris 8 cannot compute this constant expression. */
+# define SHORT_MSG_MAX 28
+#endif
+
+/* A fuzzy index contains a hash table mapping all n-grams to their
+ occurrences list. */
+struct message_fuzzy_index_ty
+{
+ message_ty **messages;
+ character_iterator_t iterator;
+ hash_table gram4;
+ size_t firstfew;
+ message_list_ty **short_messages;
+};
+
+/* Allocate a fuzzy index corresponding to a given list of messages.
+ The list of messages and the msgctxt and msgid fields of the messages
+ inside it must not be modified while the returned fuzzy index is in use. */
+message_fuzzy_index_ty *
+message_fuzzy_index_alloc (const message_list_ty *mlp,
+ const char *canon_charset)
+{
+ message_fuzzy_index_ty *findex = XMALLOC (message_fuzzy_index_ty);
+ size_t count = mlp->nitems;
+ size_t j;
+ size_t l;
+
+ findex->messages = mlp->item;
+ findex->iterator = po_charset_character_iterator (canon_charset);
+
+ /* Setup hash table. */
+ if (hash_init (&findex->gram4, 10 * count) < 0)
+ xalloc_die ();
+ for (j = 0; j < count; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
+ {
+ const char *str = mp->msgid;
+
+ /* Let p0 < p1 < p2 < p3 < p4 walk through the string. */
+ const char *p0 = str;
+ if (*p0 != '\0')
+ {
+ const char *p1 = p0 + findex->iterator (p0);
+ if (*p1 != '\0')
+ {
+ const char *p2 = p1 + findex->iterator (p1);
+ if (*p2 != '\0')
+ {
+ const char *p3 = p2 + findex->iterator (p2);
+ if (*p3 != '\0')
+ {
+ const char *p4 = p3 + findex->iterator (p3);
+ for (;;)
+ {
+ /* The segment from p0 to p4 is a 4-gram of
+ characters. Add a hash table entry that maps
+ it to the index j, or extend the existing
+ hash table entry accordingly. */
+ void *found;
+
+ if (hash_find_entry (&findex->gram4, p0, p4 - p0,
+ &found) == 0)
+ {
+ index_list_ty list = (index_list_ty) found;
+ list = addlast_index (list, j);
+ if (list != NULL)
+ hash_set_value (&findex->gram4, p0, p4 - p0,
+ list);
+ }
+ else
+ hash_insert_entry (&findex->gram4, p0, p4 - p0,
+ new_index (j));
+
+ /* Advance. */
+ if (*p4 == '\0')
+ break;
+ p0 = p1;
+ p1 = p2;
+ p2 = p3;
+ p3 = p4;
+ p4 = p4 + findex->iterator (p4);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Shrink memory used by the hash table. */
+ {
+ void *iter;
+ const void *key;
+ size_t keylen;
+ void **valuep;
+
+ iter = NULL;
+ while (hash_iterate_modify (&findex->gram4, &iter, &key, &keylen, &valuep)
+ == 0)
+ {
+ index_list_ty list = (index_list_ty) *valuep;
+ index_ty length = list[IL_LENGTH];
+
+ if (length < list[IL_ALLOCATED])
+ {
+ list[IL_ALLOCATED] = length;
+ *valuep = xrealloc (list, (2 + length) * sizeof (index_ty));
+ }
+ }
+ }
+
+ findex->firstfew = (int) sqrt ((double) count);
+ if (findex->firstfew < 10)
+ findex->firstfew = 10;
+
+ /* Setup lists of short messages. */
+ findex->short_messages = XNMALLOC (SHORT_MSG_MAX + 1, message_list_ty *);
+ for (l = 0; l <= SHORT_MSG_MAX; l++)
+ findex->short_messages[l] = message_list_alloc (false);
+ for (j = 0; j < count; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
+ {
+ const char *str = mp->msgid;
+ size_t len = strlen (str);
+
+ if (len <= SHORT_MSG_MAX)
+ message_list_append (findex->short_messages[len], mp);
+ }
+ }
+
+ /* Shrink memory used by the lists of short messages. */
+ for (l = 0; l <= SHORT_MSG_MAX; l++)
+ {
+ message_list_ty *mlp = findex->short_messages[l];
+
+ if (mlp->nitems < mlp->nitems_max)
+ {
+ mlp->nitems_max = mlp->nitems;
+ mlp->item =
+ (message_ty **)
+ xrealloc (mlp->item, mlp->nitems_max * sizeof (message_ty *));
+ }
+ }
+
+ return findex;
+}
+
+/* An index with multiplicity. */
+struct mult_index
+{
+ index_ty index;
+ unsigned int count;
+};
+
+/* A list of indices with multiplicity.
+ The indices are sorted in ascending order. */
+struct mult_index_list
+{
+ struct mult_index *item;
+ size_t nitems;
+ size_t nitems_max;
+ /* Work area. */
+ struct mult_index *item2;
+ size_t nitems2_max;
+};
+
+/* Initialize an empty list of indices with multiplicity. */
+static inline void
+mult_index_list_init (struct mult_index_list *accu)
+{
+ accu->nitems = 0;
+ accu->nitems_max = 0;
+ accu->item = NULL;
+ accu->nitems2_max = 0;
+ accu->item2 = NULL;
+}
+
+/* Add an index list to a list of indices with multiplicity. */
+static inline void
+mult_index_list_accumulate (struct mult_index_list *accu, index_list_ty list)
+{
+ size_t len1 = accu->nitems;
+ size_t len2 = list[IL_LENGTH];
+ size_t need = len1 + len2;
+ struct mult_index *ptr1;
+ struct mult_index *ptr1_end;
+ index_ty *ptr2;
+ index_ty *ptr2_end;
+ struct mult_index *destptr;
+
+ /* Make the work area large enough. */
+ if (accu->nitems2_max < need)
+ {
+ size_t new_max = 2 * accu->nitems2_max + 1;
+
+ if (new_max < need)
+ new_max = need;
+ if (accu->item2 != NULL)
+ free (accu->item2);
+ accu->item2 = XNMALLOC (new_max, struct mult_index);
+ accu->nitems2_max = new_max;
+ }
+
+ /* Make a linear pass through accu and list simultaneously. */
+ ptr1 = accu->item;
+ ptr1_end = ptr1 + len1;
+ ptr2 = list + 2;
+ ptr2_end = ptr2 + len2;
+ destptr = accu->item2;
+ while (ptr1 < ptr1_end && ptr2 < ptr2_end)
+ {
+ if (ptr1->index < *ptr2)
+ {
+ *destptr = *ptr1;
+ ptr1++;
+ }
+ else if (ptr1->index > *ptr2)
+ {
+ destptr->index = *ptr2;
+ destptr->count = 1;
+ ptr2++;
+ }
+ else /* ptr1->index == list[2 + i2] */
+ {
+ destptr->index = ptr1->index;
+ destptr->count = ptr1->count + 1;
+ ptr1++;
+ ptr2++;
+ }
+ destptr++;
+ }
+ while (ptr1 < ptr1_end)
+ {
+ *destptr = *ptr1;
+ ptr1++;
+ destptr++;
+ }
+ while (ptr2 < ptr2_end)
+ {
+ destptr->index = *ptr2;
+ destptr->count = 1;
+ ptr2++;
+ destptr++;
+ }
+
+ /* Swap accu->item and accu->item2. */
+ {
+ struct mult_index *dest = accu->item2;
+ size_t dest_max = accu->nitems2_max;
+
+ accu->item2 = accu->item;
+ accu->nitems2_max = accu->nitems_max;
+
+ accu->item = dest;
+ accu->nitems = destptr - dest;
+ accu->nitems_max = dest_max;
+ }
+}
+
+/* Compares two indices with multiplicity, according to their multiplicity. */
+static int
+mult_index_compare (const void *p1, const void *p2)
+{
+ const struct mult_index *ptr1 = (const struct mult_index *) p1;
+ const struct mult_index *ptr2 = (const struct mult_index *) p2;
+
+ if (ptr1->count < ptr2->count)
+ return 1;
+ if (ptr1->count > ptr2->count)
+ return -1;
+ /* For reproduceable results, also take into account the indices. */
+ if (ptr1->index > ptr2->index)
+ return 1;
+ if (ptr1->index < ptr2->index)
+ return -1;
+ return 0;
+}
+
+/* Sort a list of indices with multiplicity according to decreasing
+ multiplicity. */
+static inline void
+mult_index_list_sort (struct mult_index_list *accu)
+{
+ if (accu->nitems > 1)
+ qsort (accu->item, accu->nitems, sizeof (struct mult_index),
+ mult_index_compare);
+}
+
+/* Frees a list of indices with multiplicity. */
+static inline void
+mult_index_list_free (struct mult_index_list *accu)
+{
+ if (accu->item != NULL)
+ free (accu->item);
+ if (accu->item2 != NULL)
+ free (accu->item2);
+}
+
+/* Find a good match for the given msgctxt and msgid in the given fuzzy index.
+ The match does not need to be optimal.
+ Ignore matches for which the fuzzy_search_goal_function is < LOWER_BOUND.
+ LOWER_BOUND must be >= FUZZY_THRESHOLD.
+ If HEURISTIC is true, only the few best messages among the list - according
+ to a certain heuristic - are considered. If HEURISTIC is false, all
+ messages with a fuzzy_search_goal_function > FUZZY_THRESHOLD are considered,
+ like in message_list_search_fuzzy (except that in ambiguous cases where
+ several best matches exist, message_list_search_fuzzy chooses the one with
+ the smallest index whereas message_fuzzy_index_search makes a better
+ choice). */
+message_ty *
+message_fuzzy_index_search (message_fuzzy_index_ty *findex,
+ const char *msgctxt, const char *msgid,
+ double lower_bound,
+ bool heuristic)
+{
+ const char *str = msgid;
+
+ /* Let p0 < p1 < p2 < p3 < p4 walk through the string. */
+ const char *p0 = str;
+ if (*p0 != '\0')
+ {
+ const char *p1 = p0 + findex->iterator (p0);
+ if (*p1 != '\0')
+ {
+ const char *p2 = p1 + findex->iterator (p1);
+ if (*p2 != '\0')
+ {
+ const char *p3 = p2 + findex->iterator (p2);
+ if (*p3 != '\0')
+ {
+ const char *p4 = p3 + findex->iterator (p3);
+ struct mult_index_list accu;
+
+ mult_index_list_init (&accu);
+ for (;;)
+ {
+ /* The segment from p0 to p4 is a 4-gram of
+ characters. Get the hash table entry containing
+ a list of indices, and add it to the accu. */
+ void *found;
+
+ if (hash_find_entry (&findex->gram4, p0, p4 - p0,
+ &found) == 0)
+ {
+ index_list_ty list = (index_list_ty) found;
+ mult_index_list_accumulate (&accu, list);
+ }
+
+ /* Advance. */
+ if (*p4 == '\0')
+ break;
+ p0 = p1;
+ p1 = p2;
+ p2 = p3;
+ p3 = p4;
+ p4 = p4 + findex->iterator (p4);
+ }
+
+ /* Sort in decreasing count order. */
+ mult_index_list_sort (&accu);
+
+ /* Iterate over this sorted list, and maximize the
+ fuzzy_search_goal_function() result.
+ If HEURISTIC is true, take only the first few messages.
+ If HEURISTIC is false, consider all messages - to match
+ the behaviour of message_list_search_fuzzy -, but process
+ them in the order of the sorted list. This increases
+ the chances that the later calls to fstrcmp_bounded() (via
+ fuzzy_search_goal_function()) terminate quickly, thanks
+ to the best_weight which will be quite high already after
+ the first few messages. */
+ {
+ size_t count;
+ struct mult_index *ptr;
+ message_ty *best_mp;
+ double best_weight;
+
+ count = accu.nitems;
+ if (heuristic)
+ {
+ if (count > findex->firstfew)
+ count = findex->firstfew;
+ }
+
+ best_weight = lower_bound;
+ best_mp = NULL;
+ for (ptr = accu.item; count > 0; ptr++, count--)
+ {
+ message_ty *mp = findex->messages[ptr->index];
+ double weight =
+ fuzzy_search_goal_function (mp, msgctxt, msgid,
+ best_weight);
+
+ if (weight > best_weight)
+ {
+ best_weight = weight;
+ best_mp = mp;
+ }
+ }
+
+ mult_index_list_free (&accu);
+
+ return best_mp;
+ }
+ }
+ }
+ }
+ }
+
+ /* The string had less than 4 characters. */
+ {
+ size_t l = strlen (str);
+ size_t lmin, lmax;
+ message_ty *best_mp;
+ double best_weight;
+
+ if (!(l <= SHORT_STRING_MAX_BYTES))
+ abort ();
+
+ /* Walk through those short messages which have an appropriate length.
+ See the comment before SHORT_MSG_MAX. */
+ lmin = (int) ceil (l / (2 / FUZZY_THRESHOLD - 1));
+ lmax = (int) (l * (2 / FUZZY_THRESHOLD - 1));
+ if (!(lmax <= SHORT_MSG_MAX))
+ abort ();
+
+ best_weight = lower_bound;
+ best_mp = NULL;
+ for (l = lmin; l <= lmax; l++)
+ {
+ message_list_ty *mlp = findex->short_messages[l];
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+ double weight =
+ fuzzy_search_goal_function (mp, msgctxt, msgid, best_weight);
+
+ if (weight > best_weight)
+ {
+ best_weight = weight;
+ best_mp = mp;
+ }
+ }
+ }
+
+ return best_mp;
+ }
+}
+
+/* Free a fuzzy index. */
+void
+message_fuzzy_index_free (message_fuzzy_index_ty *findex)
+{
+ size_t l;
+ void *iter;
+ const void *key;
+ size_t keylen;
+ void *data;
+
+ /* Free the short lists. */
+ for (l = 0; l <= SHORT_MSG_MAX; l++)
+ message_list_free (findex->short_messages[l], 1);
+ free (findex->short_messages);
+
+ /* Free the index lists occurring as values in the hash tables. */
+ iter = NULL;
+ while (hash_iterate (&findex->gram4, &iter, &key, &keylen, &data) == 0)
+ free ((index_list_ty *) data);
+ /* Free the hash table itself. */
+ hash_destroy (&findex->gram4);
+
+ free (findex);
+}
diff --git a/gettext-tools/src/msgl-fsearch.h b/gettext-tools/src/msgl-fsearch.h
new file mode 100644
index 0000000..c523d93
--- /dev/null
+++ b/gettext-tools/src/msgl-fsearch.h
@@ -0,0 +1,69 @@
+/* Fast fuzzy searching among messages.
+ Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_FSEARCH_H
+#define _MSGL_FSEARCH_H 1
+
+#include "message.h"
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* A fuzzy index is a data structure that corresponds to a set of messages,
+ allowing for fuzzy searching of a message. It is optimized for large sets
+ of messages. */
+typedef struct message_fuzzy_index_ty message_fuzzy_index_ty;
+
+/* Allocate a fuzzy index corresponding to a given list of messages.
+ The list of messages and the msgctxt and msgid fields of the messages
+ inside it must not be modified while the returned fuzzy index is in use. */
+extern message_fuzzy_index_ty *
+ message_fuzzy_index_alloc (const message_list_ty *mlp,
+ const char *canon_charset);
+
+/* Find a good match for the given msgctxt and msgid in the given fuzzy index.
+ The match does not need to be optimal.
+ Ignore matches for which the fuzzy_search_goal_function is < LOWER_BOUND.
+ LOWER_BOUND must be >= FUZZY_THRESHOLD.
+ If HEURISTIC is true, only the few best messages among the list - according
+ to a certain heuristic - are considered. If HEURISTIC is false, all
+ messages with a fuzzy_search_goal_function > FUZZY_THRESHOLD are considered,
+ like in message_list_search_fuzzy (except that in ambiguous cases where
+ several best matches exist, message_list_search_fuzzy chooses the one with
+ the smallest index whereas message_fuzzy_index_search makes a better
+ choice). */
+extern message_ty *
+ message_fuzzy_index_search (message_fuzzy_index_ty *findex,
+ const char *msgctxt, const char *msgid,
+ double lower_bound,
+ bool heuristic);
+
+/* Free a fuzzy index. */
+extern void
+ message_fuzzy_index_free (message_fuzzy_index_ty *findex);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGL_FSEARCH_H */
diff --git a/gettext-tools/src/msgl-header.c b/gettext-tools/src/msgl-header.c
new file mode 100644
index 0000000..d1dafdb
--- /dev/null
+++ b/gettext-tools/src/msgl-header.c
@@ -0,0 +1,170 @@
+/* Message list header manipulation.
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2007.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "msgl-header.h"
+
+#include <string.h>
+
+#include "xalloc.h"
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+void
+msgdomain_list_set_header_field (msgdomain_list_ty *mdlp,
+ const char *field, const char *value)
+{
+ /* The known fields in their usual order. */
+ static const struct
+ {
+ const char *name;
+ size_t len;
+ }
+ known_fields[] =
+ {
+ { "Project-Id-Version:", sizeof ("Project-Id-Version:") - 1 },
+ { "Report-Msgid-Bugs-To:", sizeof ("Report-Msgid-Bugs-To:") - 1 },
+ { "POT-Creation-Date:", sizeof ("POT-Creation-Date:") - 1 },
+ { "PO-Revision-Date:", sizeof ("PO-Revision-Date:") - 1 },
+ { "Last-Translator:", sizeof ("Last-Translator:") - 1 },
+ { "Language-Team:", sizeof ("Language-Team:") - 1 },
+ { "Language:", sizeof ("Language:") - 1 },
+ { "MIME-Version:", sizeof ("MIME-Version:") - 1 },
+ { "Content-Type:", sizeof ("Content-Type:") - 1 },
+ { "Content-Transfer-Encoding:",
+ sizeof ("Content-Transfer-Encoding:") - 1 }
+ };
+ size_t field_len;
+ int field_index;
+ size_t k, i;
+
+ field_len = strlen (field);
+
+ /* Search the field in known_fields[]. */
+ field_index = -1;
+ for (k = 0; k < SIZEOF (known_fields); k++)
+ if (strcmp (known_fields[k].name, field) == 0)
+ {
+ field_index = k;
+ break;
+ }
+
+ for (i = 0; i < mdlp->nitems; i++)
+ {
+ message_list_ty *mlp = mdlp->item[i]->messages;
+ size_t j;
+
+ /* Search the header entry. */
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ message_ty *mp = mlp->item[j];
+
+ /* Modify the header entry. */
+ const char *header = mp->msgstr;
+ char *new_header =
+ XNMALLOC (strlen (header) + 1
+ + strlen (field) + 1 + strlen (value) + 1 + 1,
+ char);
+
+ /* Test whether the field already occurs in the header entry. */
+ const char *h;
+
+ for (h = header; *h != '\0'; )
+ {
+ if (strncmp (h, field, field_len) == 0)
+ break;
+ h = strchr (h, '\n');
+ if (h == NULL)
+ break;
+ h++;
+ }
+ if (h != NULL && *h != '\0')
+ {
+ /* Replace the field. */
+ char *p = new_header;
+ memcpy (p, header, h - header);
+ p += h - header;
+ p = stpcpy (p, field);
+ p = stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
+ h = strchr (h, '\n');
+ if (h != NULL)
+ {
+ h++;
+ stpcpy (p, h);
+ }
+ }
+ else if (field_index < 0)
+ {
+ /* An unknown field. Append it at the end. */
+ char *p = new_header;
+ p = stpcpy (p, header);
+ if (p > new_header && p[-1] != '\n')
+ *p++ = '\n';
+ p = stpcpy (p, field);
+ stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
+ }
+ else
+ {
+ /* Find the appropriate position for inserting the field. */
+ for (h = header; *h != '\0'; )
+ {
+ /* Test whether h starts with a field name whose index is
+ > field_index. */
+ for (k = field_index + 1; k < SIZEOF (known_fields); k++)
+ if (strncmp (h, known_fields[k].name, known_fields[k].len)
+ == 0)
+ break;
+ if (k < SIZEOF (known_fields))
+ break;
+ h = strchr (h, '\n');
+ if (h == NULL)
+ break;
+ h++;
+ }
+ if (h != NULL && *h != '\0')
+ {
+ /* Insert the field at position h. */
+ char *p = new_header;
+ memcpy (p, header, h - header);
+ p += h - header;
+ p = stpcpy (p, field);
+ p = stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
+ stpcpy (p, h);
+ }
+ else
+ {
+ /* Append it at the end. */
+ char *p = new_header;
+ p = stpcpy (p, header);
+ if (p > new_header && p[-1] != '\n')
+ *p++ = '\n';
+ p = stpcpy (p, field);
+ stpcpy (stpcpy (stpcpy (p, " "), value), "\n");
+ }
+ }
+
+ mp->msgstr = new_header;
+ }
+ }
+}
diff --git a/gettext-tools/src/msgl-header.h b/gettext-tools/src/msgl-header.h
new file mode 100644
index 0000000..f26b1a8
--- /dev/null
+++ b/gettext-tools/src/msgl-header.h
@@ -0,0 +1,43 @@
+/* Message list header manipulation.
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2007.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_HEADER_H
+#define _MSGL_HEADER_H
+
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Set the given field to the given value.
+ The FIELD name ends in a colon.
+ The VALUE will have a space prepended and a newline appended by this
+ function. */
+extern void
+ msgdomain_list_set_header_field (msgdomain_list_ty *mdlp,
+ const char *field, const char *value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_HEADER_H */
diff --git a/gettext-tools/src/msgl-iconv.c b/gettext-tools/src/msgl-iconv.c
new file mode 100644
index 0000000..958455b
--- /dev/null
+++ b/gettext-tools/src/msgl-iconv.c
@@ -0,0 +1,597 @@
+/* Message list charset and locale charset handling.
+ Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "msgl-iconv.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_ICONV
+# include <iconv.h>
+#endif
+
+#include "progname.h"
+#include "basename.h"
+#include "message.h"
+#include "po-charset.h"
+#include "xstriconv.h"
+#include "xstriconveh.h"
+#include "msgl-ascii.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "c-strstr.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+#if HAVE_ICONV
+
+static void conversion_error (const struct conversion_context* context)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void
+conversion_error (const struct conversion_context* context)
+{
+ if (context->to_code == po_charset_utf8)
+ /* If a conversion to UTF-8 fails, the problem lies in the input. */
+ po_xerror (PO_SEVERITY_FATAL_ERROR, context->message, NULL, 0, 0, false,
+ xasprintf (_("%s: input is not valid in \"%s\" encoding"),
+ context->from_filename, context->from_code));
+ else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, context->message, NULL, 0, 0, false,
+ xasprintf (_("\
+%s: error while converting from \"%s\" encoding to \"%s\" encoding"),
+ context->from_filename, context->from_code,
+ context->to_code));
+ /* NOTREACHED */
+ abort ();
+}
+
+char *
+convert_string_directly (iconv_t cd, const char *string,
+ const struct conversion_context* context)
+{
+ size_t len = strlen (string) + 1;
+ char *result = NULL;
+ size_t resultlen = 0;
+
+ if (xmem_cd_iconv (string, len, cd, &result, &resultlen) == 0)
+ /* Verify the result has exactly one NUL byte, at the end. */
+ if (resultlen > 0 && result[resultlen - 1] == '\0'
+ && strlen (result) == resultlen - 1)
+ return result;
+
+ conversion_error (context);
+ /* NOTREACHED */
+ return NULL;
+}
+
+static char *
+convert_string (const iconveh_t *cd, const char *string,
+ const struct conversion_context* context)
+{
+ size_t len = strlen (string) + 1;
+ char *result = NULL;
+ size_t resultlen = 0;
+
+ if (xmem_cd_iconveh (string, len, cd, iconveh_error, NULL,
+ &result, &resultlen) == 0)
+ /* Verify the result has exactly one NUL byte, at the end. */
+ if (resultlen > 0 && result[resultlen - 1] == '\0'
+ && strlen (result) == resultlen - 1)
+ return result;
+
+ conversion_error (context);
+ /* NOTREACHED */
+ return NULL;
+}
+
+static void
+convert_string_list (const iconveh_t *cd, string_list_ty *slp,
+ const struct conversion_context* context)
+{
+ size_t i;
+
+ if (slp != NULL)
+ for (i = 0; i < slp->nitems; i++)
+ slp->item[i] = convert_string (cd, slp->item[i], context);
+}
+
+static void
+convert_prev_msgid (const iconveh_t *cd, message_ty *mp,
+ const struct conversion_context* context)
+{
+ if (mp->prev_msgctxt != NULL)
+ mp->prev_msgctxt = convert_string (cd, mp->prev_msgctxt, context);
+ if (mp->prev_msgid != NULL)
+ mp->prev_msgid = convert_string (cd, mp->prev_msgid, context);
+ if (mp->prev_msgid_plural != NULL)
+ mp->prev_msgid_plural = convert_string (cd, mp->prev_msgid_plural, context);
+}
+
+static void
+convert_msgid (const iconveh_t *cd, message_ty *mp,
+ const struct conversion_context* context)
+{
+ if (mp->msgctxt != NULL)
+ mp->msgctxt = convert_string (cd, mp->msgctxt, context);
+ mp->msgid = convert_string (cd, mp->msgid, context);
+ if (mp->msgid_plural != NULL)
+ mp->msgid_plural = convert_string (cd, mp->msgid_plural, context);
+}
+
+static void
+convert_msgstr (const iconveh_t *cd, message_ty *mp,
+ const struct conversion_context* context)
+{
+ char *result = NULL;
+ size_t resultlen = 0;
+
+ if (!(mp->msgstr_len > 0 && mp->msgstr[mp->msgstr_len - 1] == '\0'))
+ abort ();
+
+ if (xmem_cd_iconveh (mp->msgstr, mp->msgstr_len, cd, iconveh_error, NULL,
+ &result, &resultlen) == 0)
+ /* Verify the result has a NUL byte at the end. */
+ if (resultlen > 0 && result[resultlen - 1] == '\0')
+ /* Verify the result has the same number of NUL bytes. */
+ {
+ const char *p;
+ const char *pend;
+ int nulcount1;
+ int nulcount2;
+
+ for (p = mp->msgstr, pend = p + mp->msgstr_len, nulcount1 = 0;
+ p < pend;
+ p += strlen (p) + 1, nulcount1++);
+ for (p = result, pend = p + resultlen, nulcount2 = 0;
+ p < pend;
+ p += strlen (p) + 1, nulcount2++);
+
+ if (nulcount1 == nulcount2)
+ {
+ mp->msgstr = result;
+ mp->msgstr_len = resultlen;
+ return;
+ }
+ }
+
+ conversion_error (context);
+}
+
+#endif
+
+
+static bool
+iconv_message_list_internal (message_list_ty *mlp,
+ const char *canon_from_code,
+ const char *canon_to_code,
+ bool update_header,
+ const char *from_filename)
+{
+ bool canon_from_code_overridden = (canon_from_code != NULL);
+ bool msgids_changed;
+ size_t j;
+
+ /* If the list is empty, nothing to do. */
+ if (mlp->nitems == 0)
+ return false;
+
+ /* Search the header entry, and extract and replace the charset name. */
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+ const char *canon_charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ canon_charset = po_charset_canonicalize (charset);
+ if (canon_charset == NULL)
+ {
+ if (!canon_from_code_overridden)
+ {
+ /* Don't give an error for POT files, because POT
+ files usually contain only ASCII msgids. */
+ const char *filename = from_filename;
+ size_t filenamelen;
+
+ if (filename != NULL
+ && (filenamelen = strlen (filename)) >= 4
+ && memcmp (filename + filenamelen - 4, ".pot", 4)
+ == 0
+ && strcmp (charset, "CHARSET") == 0)
+ canon_charset = po_charset_ascii;
+ else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0,
+ false, xasprintf (_("\
+present charset \"%s\" is not a portable encoding name"),
+ charset));
+ }
+ }
+ else
+ {
+ if (canon_from_code == NULL)
+ canon_from_code = canon_charset;
+ else if (canon_from_code != canon_charset)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0,
+ false,
+ xasprintf (_("\
+two different charsets \"%s\" and \"%s\" in input file"),
+ canon_from_code, canon_charset));
+ }
+ freea (charset);
+
+ if (update_header)
+ {
+ size_t len1, len2, len3;
+ char *new_header;
+
+ len1 = charsetstr - header;
+ len2 = strlen (canon_to_code);
+ len3 = (header + strlen (header)) - (charsetstr + len);
+ new_header = XNMALLOC (len1 + len2 + len3 + 1, char);
+ memcpy (new_header, header, len1);
+ memcpy (new_header + len1, canon_to_code, len2);
+ memcpy (new_header + len1 + len2, charsetstr + len,
+ len3 + 1);
+ mlp->item[j]->msgstr = new_header;
+ mlp->item[j]->msgstr_len = len1 + len2 + len3 + 1;
+ }
+ }
+ }
+ }
+ if (canon_from_code == NULL)
+ {
+ if (is_ascii_message_list (mlp))
+ canon_from_code = po_charset_ascii;
+ else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ _("\
+input file doesn't contain a header entry with a charset specification"));
+ }
+
+ msgids_changed = false;
+
+ /* If the two encodings are the same, nothing to do. */
+ if (canon_from_code != canon_to_code)
+ {
+#if HAVE_ICONV
+ iconveh_t cd;
+ struct conversion_context context;
+
+ if (iconveh_open (canon_to_code, canon_from_code, &cd) < 0)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf (_("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
+and iconv() does not support this conversion."),
+ canon_from_code, canon_to_code,
+ basename (program_name)));
+
+ context.from_code = canon_from_code;
+ context.to_code = canon_to_code;
+ context.from_filename = from_filename;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if ((mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt))
+ || !is_ascii_string (mp->msgid))
+ msgids_changed = true;
+ context.message = mp;
+ convert_string_list (&cd, mp->comment, &context);
+ convert_string_list (&cd, mp->comment_dot, &context);
+ convert_prev_msgid (&cd, mp, &context);
+ convert_msgid (&cd, mp, &context);
+ convert_msgstr (&cd, mp, &context);
+ }
+
+ iconveh_close (&cd);
+
+ if (msgids_changed)
+ if (message_list_msgids_changed (mlp))
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf (_("\
+Conversion from \"%s\" to \"%s\" introduces duplicates: \
+some different msgids become equal."),
+ canon_from_code, canon_to_code));
+#else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf (_("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \
+This version was built without iconv()."),
+ canon_from_code, canon_to_code,
+ basename (program_name)));
+#endif
+ }
+
+ return msgids_changed;
+}
+
+bool
+iconv_message_list (message_list_ty *mlp,
+ const char *canon_from_code, const char *canon_to_code,
+ const char *from_filename)
+{
+ return iconv_message_list_internal (mlp,
+ canon_from_code, canon_to_code, true,
+ from_filename);
+}
+
+msgdomain_list_ty *
+iconv_msgdomain_list (msgdomain_list_ty *mdlp,
+ const char *to_code,
+ bool update_header,
+ const char *from_filename)
+{
+ const char *canon_to_code;
+ size_t k;
+
+ /* Canonicalize target encoding. */
+ canon_to_code = po_charset_canonicalize (to_code);
+ if (canon_to_code == NULL)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf (_("\
+target charset \"%s\" is not a portable encoding name."),
+ to_code));
+
+ for (k = 0; k < mdlp->nitems; k++)
+ iconv_message_list_internal (mdlp->item[k]->messages,
+ mdlp->encoding, canon_to_code, update_header,
+ from_filename);
+
+ mdlp->encoding = canon_to_code;
+ return mdlp;
+}
+
+#if HAVE_ICONV
+
+static bool
+iconvable_string (const iconveh_t *cd, const char *string)
+{
+ size_t len = strlen (string) + 1;
+ char *result = NULL;
+ size_t resultlen = 0;
+
+ if (xmem_cd_iconveh (string, len, cd, iconveh_error, NULL,
+ &result, &resultlen) == 0)
+ {
+ /* Test if the result has exactly one NUL byte, at the end. */
+ bool ok = (resultlen > 0 && result[resultlen - 1] == '\0'
+ && strlen (result) == resultlen - 1);
+ free (result);
+ return ok;
+ }
+ return false;
+}
+
+static bool
+iconvable_string_list (const iconveh_t *cd, string_list_ty *slp)
+{
+ size_t i;
+
+ if (slp != NULL)
+ for (i = 0; i < slp->nitems; i++)
+ if (!iconvable_string (cd, slp->item[i]))
+ return false;
+ return true;
+}
+
+static bool
+iconvable_prev_msgid (const iconveh_t *cd, message_ty *mp)
+{
+ if (mp->prev_msgctxt != NULL)
+ if (!iconvable_string (cd, mp->prev_msgctxt))
+ return false;
+ if (mp->prev_msgid != NULL)
+ if (!iconvable_string (cd, mp->prev_msgid))
+ return false;
+ if (mp->prev_msgid_plural != NULL)
+ if (!iconvable_string (cd, mp->prev_msgid_plural))
+ return false;
+ return true;
+}
+
+static bool
+iconvable_msgid (const iconveh_t *cd, message_ty *mp)
+{
+ if (mp->msgctxt != NULL)
+ if (!iconvable_string (cd, mp->msgctxt))
+ return false;
+ if (!iconvable_string (cd, mp->msgid))
+ return false;
+ if (mp->msgid_plural != NULL)
+ if (!iconvable_string (cd, mp->msgid_plural))
+ return false;
+ return true;
+}
+
+static bool
+iconvable_msgstr (const iconveh_t *cd, message_ty *mp)
+{
+ char *result = NULL;
+ size_t resultlen = 0;
+
+ if (!(mp->msgstr_len > 0 && mp->msgstr[mp->msgstr_len - 1] == '\0'))
+ abort ();
+
+ if (xmem_cd_iconveh (mp->msgstr, mp->msgstr_len, cd, iconveh_error, NULL,
+ &result, &resultlen) == 0)
+ {
+ bool ok = false;
+
+ /* Test if the result has a NUL byte at the end. */
+ if (resultlen > 0 && result[resultlen - 1] == '\0')
+ /* Test if the result has the same number of NUL bytes. */
+ {
+ const char *p;
+ const char *pend;
+ int nulcount1;
+ int nulcount2;
+
+ for (p = mp->msgstr, pend = p + mp->msgstr_len, nulcount1 = 0;
+ p < pend;
+ p += strlen (p) + 1, nulcount1++);
+ for (p = result, pend = p + resultlen, nulcount2 = 0;
+ p < pend;
+ p += strlen (p) + 1, nulcount2++);
+
+ if (nulcount1 == nulcount2)
+ ok = true;
+ }
+
+ free (result);
+ return ok;
+ }
+ return false;
+}
+
+#endif
+
+bool
+is_message_list_iconvable (message_list_ty *mlp,
+ const char *canon_from_code,
+ const char *canon_to_code)
+{
+ bool canon_from_code_overridden = (canon_from_code != NULL);
+ size_t j;
+
+ /* If the list is empty, nothing to check. */
+ if (mlp->nitems == 0)
+ return true;
+
+ /* Search the header entry, and extract the charset name. */
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+ const char *canon_charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ canon_charset = po_charset_canonicalize (charset);
+ if (canon_charset == NULL)
+ {
+ if (!canon_from_code_overridden)
+ {
+ /* Don't give an error for POT files, because POT
+ files usually contain only ASCII msgids. */
+ if (strcmp (charset, "CHARSET") == 0)
+ canon_charset = po_charset_ascii;
+ else
+ {
+ /* charset is not a portable encoding name. */
+ freea (charset);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (canon_from_code == NULL)
+ canon_from_code = canon_charset;
+ else if (canon_from_code != canon_charset)
+ {
+ /* Two different charsets in input file. */
+ freea (charset);
+ return false;
+ }
+ }
+ freea (charset);
+ }
+ }
+ }
+ if (canon_from_code == NULL)
+ {
+ if (is_ascii_message_list (mlp))
+ canon_from_code = po_charset_ascii;
+ else
+ /* Input file lacks a header entry with a charset specification. */
+ return false;
+ }
+
+ /* If the two encodings are the same, nothing to check. */
+ if (canon_from_code != canon_to_code)
+ {
+#if HAVE_ICONV
+ iconveh_t cd;
+
+ if (iconveh_open (canon_to_code, canon_from_code, &cd) < 0)
+ /* iconv() doesn't support this conversion. */
+ return false;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (!(iconvable_string_list (&cd, mp->comment)
+ && iconvable_string_list (&cd, mp->comment_dot)
+ && iconvable_prev_msgid (&cd, mp)
+ && iconvable_msgid (&cd, mp)
+ && iconvable_msgstr (&cd, mp)))
+ return false;
+ }
+
+ iconveh_close (&cd);
+#else
+ /* This version was built without iconv(). */
+ return false;
+#endif
+ }
+
+ return true;
+}
diff --git a/gettext-tools/src/msgl-iconv.h b/gettext-tools/src/msgl-iconv.h
new file mode 100644
index 0000000..5a7fc00
--- /dev/null
+++ b/gettext-tools/src/msgl-iconv.h
@@ -0,0 +1,87 @@
+/* Message list character set conversion.
+ Copyright (C) 2001-2003, 2005-2006, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_ICONV_H
+#define _MSGL_ICONV_H
+
+#include <stdbool.h>
+#if HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if HAVE_ICONV
+
+/* A context, used for accurate error messages. */
+struct conversion_context
+{
+ const char *from_code; /* canonicalized encoding name for input */
+ const char *to_code; /* canonicalized encoding name for output */
+ const char *from_filename; /* file name where the input comes from */
+ const message_ty *message; /* message being converted, or NULL */
+};
+
+/* Converts the STRING through the conversion descriptor CD.
+ Assumes that either FROM_CODE or TO_CODE is UTF-8. */
+extern char *convert_string_directly (iconv_t cd, const char *string,
+ const struct conversion_context* context);
+
+#endif
+
+/* Converts the message list MLP to the (already canonicalized) encoding
+ CANON_TO_CODE. The (already canonicalized) encoding before conversion
+ can be passed as CANON_FROM_CODE; if NULL is passed instead, the
+ encoding is looked up in the header entry. Returns true if and only if
+ some msgctxt or msgid changed due to the conversion. */
+extern bool
+ iconv_message_list (message_list_ty *mlp,
+ const char *canon_from_code,
+ const char *canon_to_code,
+ const char *from_filename);
+
+/* Converts all the message lists in MDLP to the encoding TO_CODE.
+ UPDATE_HEADER specifies whether to update the "charset=..." specification
+ in the header; it should normally be true. */
+extern msgdomain_list_ty *
+ iconv_msgdomain_list (msgdomain_list_ty *mdlp,
+ const char *to_code,
+ bool update_header,
+ const char *from_filename);
+
+/* Tests whether the message list MLP could be converted to CANON_TO_CODE.
+ The (already canonicalized) encoding before conversion can be passed as
+ CANON_FROM_CODE; if NULL is passed instead, the encoding is looked up
+ in the header entry. */
+extern bool
+ is_message_list_iconvable (message_list_ty *mlp,
+ const char *canon_from_code,
+ const char *canon_to_code);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _MSGL_ICONV_H */
diff --git a/gettext-tools/src/msgmerge.c b/gettext-tools/src/msgmerge.c
new file mode 100644
index 0000000..d8e02ae
--- /dev/null
+++ b/gettext-tools/src/msgmerge.c
@@ -0,0 +1,2061 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2010, 2012 Free Software Foundation, Inc.
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "format.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "obstack.h"
+#include "c-strstr.h"
+#include "c-strcase.h"
+#include "po-charset.h"
+#include "msgl-iconv.h"
+#include "msgl-equal.h"
+#include "msgl-fsearch.h"
+#include "glthread/lock.h"
+#include "lang-table.h"
+#include "plural-exp.h"
+#include "plural-count.h"
+#include "msgl-check.h"
+#include "po-xerror.h"
+#include "backupfile.h"
+#include "copy-file.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+
+/* If true do not print unneeded messages. */
+static bool quiet;
+
+/* Verbosity level. */
+static int verbosity_level;
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Apply the .pot file to each of the domains in the PO file. */
+static bool multi_domain_mode = false;
+
+/* Determines whether to use fuzzy matching. */
+static bool use_fuzzy_matching = true;
+
+/* Determines whether to keep old msgids as previous msgids. */
+static bool keep_previous = false;
+
+/* Language (ISO-639 code) and optional territory (ISO-3166 code). */
+static const char *catalogname = NULL;
+
+/* List of user-specified compendiums. */
+static message_list_list_ty *compendiums;
+
+/* List of corresponding filenames. */
+static string_list_ty *compendium_filenames;
+
+/* Update mode. */
+static bool update_mode = false;
+static const char *version_control_string;
+static const char *backup_suffix_string;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "backup", required_argument, NULL, CHAR_MAX + 1 },
+ { "color", optional_argument, NULL, CHAR_MAX + 9 },
+ { "compendium", required_argument, NULL, 'C', },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "lang", required_argument, NULL, CHAR_MAX + 8 },
+ { "multi-domain", no_argument, NULL, 'm' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-fuzzy-matching", no_argument, NULL, 'N' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 11 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 4 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "previous", no_argument, NULL, CHAR_MAX + 7 },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "silent", no_argument, NULL, 'q' },
+ { "strict", no_argument, NULL, CHAR_MAX + 2 },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 5 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 6 },
+ { "style", required_argument, NULL, CHAR_MAX + 10 },
+ { "suffix", required_argument, NULL, CHAR_MAX + 3 },
+ { "update", no_argument, NULL, 'U' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+struct statistics
+{
+ size_t merged;
+ size_t fuzzied;
+ size_t missing;
+ size_t obsolete;
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void compendium (const char *filename);
+static void msgdomain_list_stablesort_by_obsolete (msgdomain_list_ty *mdlp);
+static msgdomain_list_ty *merge (const char *fn1, const char *fn2,
+ catalog_input_format_ty input_syntax,
+ msgdomain_list_ty **defp);
+
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ char *color;
+ msgdomain_list_ty *def;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_filepos = false;
+ bool sort_by_msgid = false;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+ verbosity_level = 0;
+ quiet = false;
+ gram_max_allowed_errors = UINT_MAX;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ color = NULL;
+
+ while ((opt = getopt_long (argc, argv, "C:D:eEFhimn:No:pPqsUvVw:",
+ long_options, NULL))
+ != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'C':
+ compendium (optarg);
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'm':
+ multi_domain_mode = true;
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'N':
+ use_fuzzy_matching = false;
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 'q':
+ quiet = true;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'U':
+ update_mode = true;
+ break;
+
+ case 'v':
+ ++verbosity_level;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1: /* --backup */
+ version_control_string = optarg;
+ break;
+
+ case CHAR_MAX + 2: /* --strict */
+ message_print_style_uniforum ();
+ break;
+
+ case CHAR_MAX + 3: /* --suffix */
+ backup_suffix_string = optarg;
+ break;
+
+ case CHAR_MAX + 4: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 5: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 6: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 7: /* --previous */
+ keep_previous = true;
+ break;
+
+ case CHAR_MAX + 8: /* --lang */
+ catalogname = optarg;
+ break;
+
+ case CHAR_MAX + 9: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ color = optarg;
+ break;
+
+ case CHAR_MAX + 10: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 11: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2010");
+ printf (_("Written by %s.\n"), proper_name ("Peter Miller"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind >= argc)
+ {
+ error (EXIT_SUCCESS, 0, _("no input files given"));
+ usage (EXIT_FAILURE);
+ }
+ if (optind + 2 != argc)
+ {
+ error (EXIT_SUCCESS, 0, _("exactly 2 input files required"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (update_mode)
+ {
+ if (output_file != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--update", "--output-file");
+ }
+ if (color != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--update", "--color");
+ }
+ if (style_file_name != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--update", "--style");
+ }
+ }
+ else
+ {
+ if (version_control_string != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
+ "--backup", "--update");
+ usage (EXIT_FAILURE);
+ }
+ if (backup_suffix_string != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
+ "--suffix", "--update");
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* In update mode, --properties-input implies --properties-output. */
+ if (update_mode && input_syntax == &input_format_properties)
+ output_syntax = &output_format_properties;
+ /* In update mode, --stringtable-input implies --stringtable-output. */
+ if (update_mode && input_syntax == &input_format_stringtable)
+ output_syntax = &output_format_stringtable;
+
+ /* Merge the two files. */
+ result = merge (argv[optind], argv[optind + 1], input_syntax, &def);
+
+ /* Sort the results. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ if (update_mode)
+ {
+ /* Before comparing result with def, sort the result into the same order
+ as would be done implicitly by output_syntax->print. */
+ if (output_syntax->sorts_obsoletes_to_end)
+ msgdomain_list_stablesort_by_obsolete (result);
+
+ /* Do nothing if the original file and the result are equal. Also do
+ nothing if the original file and the result differ only by the
+ POT-Creation-Date in the header entry; this is needed for projects
+ which don't put the .pot file under CVS. */
+ if (!msgdomain_list_equal (def, result, true))
+ {
+ /* Back up def.po. */
+ enum backup_type backup_type;
+ char *backup_file;
+
+ output_file = argv[optind];
+
+ if (backup_suffix_string == NULL)
+ {
+ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
+ if (backup_suffix_string != NULL
+ && backup_suffix_string[0] == '\0')
+ backup_suffix_string = NULL;
+ }
+ if (backup_suffix_string != NULL)
+ simple_backup_suffix = backup_suffix_string;
+
+ backup_type = xget_version (_("backup type"), version_control_string);
+ if (backup_type != none)
+ {
+ backup_file = find_backup_file_name (output_file, backup_type);
+ copy_file_preserving (output_file, backup_file);
+ }
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, true,
+ false);
+ }
+ }
+ else
+ {
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po,
+ false);
+ }
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] def.po ref.pot\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Merges two Uniforum style .po files together. The def.po file is an\n\
+existing PO file with translations which will be taken over to the newly\n\
+created file as long as they still match; comments will be preserved,\n\
+but extracted comments and file positions will be discarded. The ref.pot\n\
+file is the last created PO file with up-to-date source references but\n\
+old translations, or a PO Template file (generally created by xgettext);\n\
+any translations or comments in the file will be discarded, however dot\n\
+comments and file positions will be preserved. Where an exact match\n\
+cannot be found, fuzzy matching is used to produce better results.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ def.po translations referring to old sources\n"));
+ printf (_("\
+ ref.pot references to new sources\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+ -C, --compendium=FILE additional library of message translations,\n\
+ may be specified more than once\n"));
+ printf ("\n");
+ printf (_("\
+Operation mode:\n"));
+ printf (_("\
+ -U, --update update def.po,\n\
+ do nothing if def.po already up to date\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location in update mode:\n"));
+ printf (_("\
+The result is written back to def.po.\n"));
+ printf (_("\
+ --backup=CONTROL make a backup of def.po\n"));
+ printf (_("\
+ --suffix=SUFFIX override the usual backup suffix\n"));
+ printf (_("\
+The version control method may be selected via the --backup option or through\n\
+the VERSION_CONTROL environment variable. Here are the values:\n\
+ none, off never make backups (even if --backup is given)\n\
+ numbered, t make numbered backups\n\
+ existing, nil numbered if numbered backups exist, simple otherwise\n\
+ simple, never always make simple backups\n"));
+ printf (_("\
+The backup suffix is '~', unless set with --suffix or the SIMPLE_BACKUP_SUFFIX\n\
+environment variable.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Operation modifiers:\n"));
+ printf (_("\
+ -m, --multi-domain apply ref.pot to each of the domains in def.po\n"));
+ printf (_("\
+ -N, --no-fuzzy-matching do not use fuzzy matching\n"));
+ printf (_("\
+ --previous keep previous msgids of translated messages\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input files are in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
+ syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --lang=CATALOGNAME set 'Language' field in the header entry\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent indented output style\n"));
+ printf (_("\
+ --no-location suppress '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location preserve '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict strict Uniforum output style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf (_("\
+ -v, --verbose increase verbosity level\n"));
+ printf (_("\
+ -q, --quiet, --silent suppress progress indicators\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+static void
+compendium (const char *filename)
+{
+ msgdomain_list_ty *mdlp;
+ size_t k;
+
+ mdlp = read_catalog_file (filename, &input_format_po);
+ if (compendiums == NULL)
+ {
+ compendiums = message_list_list_alloc ();
+ compendium_filenames = string_list_alloc ();
+ }
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_list_append (compendiums, mdlp->item[k]->messages);
+ string_list_append (compendium_filenames, filename);
+ }
+}
+
+
+/* Sorts obsolete messages to the end, for every domain. */
+static void
+msgdomain_list_stablesort_by_obsolete (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ /* Sort obsolete messages to the end. */
+ if (mlp->nitems > 0)
+ {
+ message_ty **l1 = XNMALLOC (mlp->nitems, message_ty *);
+ size_t n1;
+ message_ty **l2 = XNMALLOC (mlp->nitems, message_ty *);
+ size_t n2;
+ size_t j;
+
+ /* Sort the non-obsolete messages into l1 and the obsolete messages
+ into l2. */
+ n1 = 0;
+ n2 = 0;
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->obsolete)
+ l2[n2++] = mp;
+ else
+ l1[n1++] = mp;
+ }
+ if (n1 > 0 && n2 > 0)
+ {
+ memcpy (mlp->item, l1, n1 * sizeof (message_ty *));
+ memcpy (mlp->item + n1, l2, n2 * sizeof (message_ty *));
+ }
+ free (l2);
+ free (l1);
+ }
+ }
+}
+
+
+/* Data structure representing the messages with known translations.
+ They are composed of
+ - A message list from def.po,
+ - The compendiums.
+ The data structure is optimized for exact and fuzzy searches. */
+typedef struct definitions_ty definitions_ty;
+struct definitions_ty
+{
+ /* A list of message lists. The first comes from def.po, the other ones
+ from the compendiums. Each message list has a built-in hash table,
+ for speed when doing the exact searches. */
+ message_list_list_ty *lists;
+
+ /* A fuzzy index of the current list of non-compendium messages, for speed
+ when doing fuzzy searches. Used only if use_fuzzy_matching is true. */
+ message_fuzzy_index_ty *curr_findex;
+ /* A once-only execution guard for the initialization of the fuzzy index.
+ Needed for OpenMP. */
+ gl_lock_define(, curr_findex_init_lock)
+
+ /* A fuzzy index of the compendiums, for speed when doing fuzzy searches.
+ Used only if use_fuzzy_matching is true and compendiums != NULL. */
+ message_fuzzy_index_ty *comp_findex;
+ /* A once-only execution guard for the initialization of the fuzzy index.
+ Needed for OpenMP. */
+ gl_lock_define(, comp_findex_init_lock)
+
+ /* The canonical encoding of the definitions and the compendiums.
+ Only used for fuzzy matching. */
+ const char *canon_charset;
+};
+
+static inline void
+definitions_init (definitions_ty *definitions, const char *canon_charset)
+{
+ definitions->lists = message_list_list_alloc ();
+ message_list_list_append (definitions->lists, NULL);
+ if (compendiums != NULL)
+ message_list_list_append_list (definitions->lists, compendiums);
+ definitions->curr_findex = NULL;
+ gl_lock_init (definitions->curr_findex_init_lock);
+ definitions->comp_findex = NULL;
+ gl_lock_init (definitions->comp_findex_init_lock);
+ definitions->canon_charset = canon_charset;
+}
+
+/* Return the current list of non-compendium messages. */
+static inline message_list_ty *
+definitions_current_list (const definitions_ty *definitions)
+{
+ return definitions->lists->item[0];
+}
+
+/* Set the current list of non-compendium messages. */
+static inline void
+definitions_set_current_list (definitions_ty *definitions, message_list_ty *mlp)
+{
+ definitions->lists->item[0] = mlp;
+ if (definitions->curr_findex != NULL)
+ {
+ message_fuzzy_index_free (definitions->curr_findex);
+ definitions->curr_findex = NULL;
+ }
+}
+
+/* Create the fuzzy index for the current list of non-compendium messages.
+ Used only if use_fuzzy_matching is true. */
+static inline void
+definitions_init_curr_findex (definitions_ty *definitions)
+{
+ /* Protect against concurrent execution. */
+ gl_lock_lock (definitions->curr_findex_init_lock);
+ if (definitions->curr_findex == NULL)
+ definitions->curr_findex =
+ message_fuzzy_index_alloc (definitions_current_list (definitions),
+ definitions->canon_charset);
+ gl_lock_unlock (definitions->curr_findex_init_lock);
+}
+
+/* Create the fuzzy index for the compendium messages.
+ Used only if use_fuzzy_matching is true and compendiums != NULL. */
+static inline void
+definitions_init_comp_findex (definitions_ty *definitions)
+{
+ /* Protect against concurrent execution. */
+ gl_lock_lock (definitions->comp_findex_init_lock);
+ if (definitions->comp_findex == NULL)
+ {
+ /* Combine all the compendium message lists into a single one. Don't
+ bother checking for duplicates. */
+ message_list_ty *all_compendium;
+ size_t i;
+
+ all_compendium = message_list_alloc (false);
+ for (i = 0; i < compendiums->nitems; i++)
+ {
+ message_list_ty *mlp = compendiums->item[i];
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ message_list_append (all_compendium, mlp->item[j]);
+ }
+
+ /* Create the fuzzy index from it. */
+ definitions->comp_findex =
+ message_fuzzy_index_alloc (all_compendium, definitions->canon_charset);
+ }
+ gl_lock_unlock (definitions->comp_findex_init_lock);
+}
+
+/* Exact search. */
+static inline message_ty *
+definitions_search (const definitions_ty *definitions,
+ const char *msgctxt, const char *msgid)
+{
+ return message_list_list_search (definitions->lists, msgctxt, msgid);
+}
+
+/* Fuzzy search.
+ Used only if use_fuzzy_matching is true. */
+static inline message_ty *
+definitions_search_fuzzy (definitions_ty *definitions,
+ const char *msgctxt, const char *msgid)
+{
+ message_ty *mp1;
+
+ if (false)
+ {
+ /* Old, slow code. */
+ mp1 =
+ message_list_search_fuzzy (definitions_current_list (definitions),
+ msgctxt, msgid);
+ }
+ else
+ {
+ /* Speedup through early abort in fstrcmp(), combined with pre-sorting
+ of the messages through a hashed index. */
+ /* Create the fuzzy index lazily. */
+ if (definitions->curr_findex == NULL)
+ definitions_init_curr_findex (definitions);
+ mp1 = message_fuzzy_index_search (definitions->curr_findex,
+ msgctxt, msgid,
+ FUZZY_THRESHOLD, false);
+ }
+
+ if (compendiums != NULL)
+ {
+ double lower_bound_for_mp2;
+ message_ty *mp2;
+
+ lower_bound_for_mp2 =
+ (mp1 != NULL
+ ? fuzzy_search_goal_function (mp1, msgctxt, msgid, 0.0)
+ : FUZZY_THRESHOLD);
+ /* This lower bound must be >= FUZZY_THRESHOLD. */
+ if (!(lower_bound_for_mp2 >= FUZZY_THRESHOLD))
+ abort ();
+
+ /* Create the fuzzy index lazily. */
+ if (definitions->comp_findex == NULL)
+ definitions_init_comp_findex (definitions);
+
+ mp2 = message_fuzzy_index_search (definitions->comp_findex,
+ msgctxt, msgid,
+ lower_bound_for_mp2, true);
+
+ /* Choose the best among mp1, mp2. */
+ if (mp1 == NULL
+ || (mp2 != NULL
+ && (fuzzy_search_goal_function (mp2, msgctxt, msgid,
+ lower_bound_for_mp2)
+ > lower_bound_for_mp2)))
+ mp1 = mp2;
+ }
+
+ return mp1;
+}
+
+static inline void
+definitions_destroy (definitions_ty *definitions)
+{
+ message_list_list_free (definitions->lists, 2);
+ if (definitions->curr_findex != NULL)
+ message_fuzzy_index_free (definitions->curr_findex);
+ if (definitions->comp_findex != NULL)
+ message_fuzzy_index_free (definitions->comp_findex);
+}
+
+
+/* A silent error logger. We are only interested in knowing whether errors
+ occurred at all. */
+static void
+silent_error_logger (const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+static void
+silent_error_logger (const char *format, ...)
+{
+}
+
+
+/* Another silent error logger. */
+static void
+silent_xerror (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+{
+}
+
+
+static message_ty *
+message_merge (message_ty *def, message_ty *ref, bool force_fuzzy,
+ const struct plural_distribution *distribution)
+{
+ const char *msgstr;
+ size_t msgstr_len;
+ const char *prev_msgctxt;
+ const char *prev_msgid;
+ const char *prev_msgid_plural;
+ message_ty *result;
+ size_t j, i;
+
+ /* Take the msgid from the reference. When fuzzy matches are made,
+ the definition will not be unique, but the reference will be -
+ usually because it has only been slightly changed. */
+
+ /* Take the msgstr from the definition. The msgstr of the reference
+ is usually empty, as it was generated by xgettext. If we currently
+ process the header entry we have to merge the msgstr by using the
+ Report-Msgid-Bugs-To and POT-Creation-Date fields from the reference. */
+ if (is_header (ref))
+ {
+ /* Oh, oh. The header entry and we have something to fill in. */
+ static const struct
+ {
+ const char *name;
+ size_t len;
+ } known_fields[] =
+ {
+ { "Project-Id-Version:", sizeof ("Project-Id-Version:") - 1 },
+#define PROJECT_ID 0
+ { "Report-Msgid-Bugs-To:", sizeof ("Report-Msgid-Bugs-To:") - 1 },
+#define REPORT_MSGID_BUGS_TO 1
+ { "POT-Creation-Date:", sizeof ("POT-Creation-Date:") - 1 },
+#define POT_CREATION_DATE 2
+ { "PO-Revision-Date:", sizeof ("PO-Revision-Date:") - 1 },
+#define PO_REVISION_DATE 3
+ { "Last-Translator:", sizeof ("Last-Translator:") - 1 },
+#define LAST_TRANSLATOR 4
+ { "Language-Team:", sizeof ("Language-Team:") - 1 },
+#define LANGUAGE_TEAM 5
+ { "Language:", sizeof ("Language:") - 1 },
+#define LANGUAGE 6
+ { "MIME-Version:", sizeof ("MIME-Version:") - 1 },
+#define MIME_VERSION 7
+ { "Content-Type:", sizeof ("Content-Type:") - 1 },
+#define CONTENT_TYPE 8
+ { "Content-Transfer-Encoding:",
+ sizeof ("Content-Transfer-Encoding:") - 1 }
+#define CONTENT_TRANSFER 9
+ };
+#define UNKNOWN 10
+ struct
+ {
+ const char *string;
+ size_t len;
+ } header_fields[UNKNOWN + 1];
+ struct obstack pool;
+ const char *cp;
+ char *newp;
+ size_t len, cnt;
+
+ /* Clear all fields. */
+ memset (header_fields, '\0', sizeof (header_fields));
+
+ /* Prepare a temporary memory pool. */
+ obstack_init (&pool);
+
+ cp = def->msgstr;
+ while (*cp != '\0')
+ {
+ const char *endp = strchr (cp, '\n');
+ int terminated = endp != NULL;
+
+ if (!terminated)
+ {
+ /* Add a trailing newline. */
+ char *copy;
+ endp = strchr (cp, '\0');
+
+ len = endp - cp + 1;
+
+ copy = (char *) obstack_alloc (&pool, len + 1);
+ stpcpy (stpcpy (copy, cp), "\n");
+ cp = copy;
+ }
+ else
+ {
+ len = (endp - cp) + 1;
+ ++endp;
+ }
+
+ /* Compare with any of the known fields. */
+ for (cnt = 0;
+ cnt < sizeof (known_fields) / sizeof (known_fields[0]);
+ ++cnt)
+ if (c_strncasecmp (cp, known_fields[cnt].name, known_fields[cnt].len)
+ == 0)
+ break;
+
+ if (cnt < sizeof (known_fields) / sizeof (known_fields[0]))
+ {
+ header_fields[cnt].string = &cp[known_fields[cnt].len];
+ header_fields[cnt].len = len - known_fields[cnt].len;
+ }
+ else
+ {
+ /* It's an unknown field. Append content to what is already
+ known. */
+ char *extended =
+ (char *) obstack_alloc (&pool,
+ header_fields[UNKNOWN].len + len + 1);
+ memcpy (extended, header_fields[UNKNOWN].string,
+ header_fields[UNKNOWN].len);
+ memcpy (&extended[header_fields[UNKNOWN].len], cp, len);
+ extended[header_fields[UNKNOWN].len + len] = '\0';
+ header_fields[UNKNOWN].string = extended;
+ header_fields[UNKNOWN].len += len;
+ }
+
+ cp = endp;
+ }
+
+ /* Set the Language field if specified on the command line. */
+ if (catalogname != NULL)
+ {
+ /* Prepend a space and append a newline. */
+ size_t len = strlen (catalogname);
+ char *copy = (char *) obstack_alloc (&pool, 1 + len + 1 + 1);
+ stpcpy (stpcpy (stpcpy (copy, " "), catalogname), "\n");
+ header_fields[LANGUAGE].string = copy;
+ header_fields[LANGUAGE].len = strlen (header_fields[LANGUAGE].string);
+ }
+ /* Add a Language field to PO files that don't have one. The Language
+ field was introduced in gettext-0.18. */
+ else if (header_fields[LANGUAGE].string == NULL)
+ {
+ const char *language_team_ptr = header_fields[LANGUAGE_TEAM].string;
+
+ if (language_team_ptr != NULL)
+ {
+ size_t language_team_len = header_fields[LANGUAGE_TEAM].len;
+
+ /* Trim leading blanks. */
+ while (language_team_len > 0
+ && (*language_team_ptr == ' '
+ || *language_team_ptr == '\t'))
+ {
+ language_team_ptr++;
+ language_team_len--;
+ }
+
+ /* Trim trailing blanks. */
+ while (language_team_len > 0
+ && (language_team_ptr[language_team_len - 1] == ' '
+ || language_team_ptr[language_team_len - 1] == '\t'))
+ language_team_len--;
+
+ /* Trim last word, if it looks like an URL or email address. */
+ {
+ size_t i;
+
+ for (i = language_team_len; i > 0; i--)
+ if (language_team_ptr[i - 1] == ' '
+ || language_team_ptr[i - 1] == '\t')
+ break;
+ /* The last word: language_team_ptr[i..language_team_len-1]. */
+ if (i < language_team_len
+ && (language_team_ptr[i] == '<'
+ || language_team_ptr[language_team_len - 1] == '>'
+ || memchr (language_team_ptr, '@', language_team_len)
+ != NULL
+ || memchr (language_team_ptr, '/', language_team_len)
+ != NULL))
+ {
+ /* Trim last word and blanks before it. */
+ while (i > 0
+ && (language_team_ptr[i - 1] == ' '
+ || language_team_ptr[i - 1] == '\t'))
+ i--;
+ language_team_len = i;
+ }
+ }
+
+ /* The rest of the Language-Team field should be the english name
+ of the languge. Convert to ISO 639 and ISO 3166 syntax. */
+ {
+ size_t i;
+
+ for (i = 0; i < language_variant_table_size; i++)
+ if (strlen (language_variant_table[i].english)
+ == language_team_len
+ && memcmp (language_variant_table[i].english,
+ language_team_ptr, language_team_len) == 0)
+ {
+ header_fields[LANGUAGE].string =
+ language_variant_table[i].code;
+ break;
+ }
+ }
+ if (header_fields[LANGUAGE].string == NULL)
+ {
+ size_t i;
+
+ for (i = 0; i < language_table_size; i++)
+ if (strlen (language_table[i].english) == language_team_len
+ && memcmp (language_table[i].english,
+ language_team_ptr, language_team_len) == 0)
+ {
+ header_fields[LANGUAGE].string = language_table[i].code;
+ break;
+ }
+ }
+ if (header_fields[LANGUAGE].string != NULL)
+ {
+ /* Prepend a space and append a newline. */
+ const char *str = header_fields[LANGUAGE].string;
+ size_t len = strlen (str);
+ char *copy = (char *) obstack_alloc (&pool, 1 + len + 1 + 1);
+ stpcpy (stpcpy (stpcpy (copy, " "), str), "\n");
+ header_fields[LANGUAGE].string = copy;
+ }
+ else
+ header_fields[LANGUAGE].string = " \n";
+ header_fields[LANGUAGE].len =
+ strlen (header_fields[LANGUAGE].string);
+ }
+ }
+
+ {
+ const char *msgid_bugs_ptr;
+
+ msgid_bugs_ptr = c_strstr (ref->msgstr, "Report-Msgid-Bugs-To:");
+ if (msgid_bugs_ptr != NULL)
+ {
+ size_t msgid_bugs_len;
+ const char *endp;
+
+ msgid_bugs_ptr += sizeof ("Report-Msgid-Bugs-To:") - 1;
+
+ endp = strchr (msgid_bugs_ptr, '\n');
+ if (endp == NULL)
+ {
+ /* Add a trailing newline. */
+ char *extended;
+ endp = strchr (msgid_bugs_ptr, '\0');
+ msgid_bugs_len = (endp - msgid_bugs_ptr) + 1;
+ extended = (char *) obstack_alloc (&pool, msgid_bugs_len + 1);
+ stpcpy (stpcpy (extended, msgid_bugs_ptr), "\n");
+ msgid_bugs_ptr = extended;
+ }
+ else
+ msgid_bugs_len = (endp - msgid_bugs_ptr) + 1;
+
+ header_fields[REPORT_MSGID_BUGS_TO].string = msgid_bugs_ptr;
+ header_fields[REPORT_MSGID_BUGS_TO].len = msgid_bugs_len;
+ }
+ }
+
+ {
+ const char *pot_date_ptr;
+
+ pot_date_ptr = c_strstr (ref->msgstr, "POT-Creation-Date:");
+ if (pot_date_ptr != NULL)
+ {
+ size_t pot_date_len;
+ const char *endp;
+
+ pot_date_ptr += sizeof ("POT-Creation-Date:") - 1;
+
+ endp = strchr (pot_date_ptr, '\n');
+ if (endp == NULL)
+ {
+ /* Add a trailing newline. */
+ char *extended;
+ endp = strchr (pot_date_ptr, '\0');
+ pot_date_len = (endp - pot_date_ptr) + 1;
+ extended = (char *) obstack_alloc (&pool, pot_date_len + 1);
+ stpcpy (stpcpy (extended, pot_date_ptr), "\n");
+ pot_date_ptr = extended;
+ }
+ else
+ pot_date_len = (endp - pot_date_ptr) + 1;
+
+ header_fields[POT_CREATION_DATE].string = pot_date_ptr;
+ header_fields[POT_CREATION_DATE].len = pot_date_len;
+ }
+ }
+
+ /* Concatenate all the various fields. */
+ len = 0;
+ for (cnt = 0; cnt < UNKNOWN; ++cnt)
+ if (header_fields[cnt].string != NULL)
+ len += known_fields[cnt].len + header_fields[cnt].len;
+ len += header_fields[UNKNOWN].len;
+
+ cp = newp = XNMALLOC (len + 1, char);
+ newp[len] = '\0';
+
+#define IF_FILLED(idx) \
+ if (header_fields[idx].string) \
+ newp = stpncpy (stpcpy (newp, known_fields[idx].name), \
+ header_fields[idx].string, header_fields[idx].len)
+
+ IF_FILLED (PROJECT_ID);
+ IF_FILLED (REPORT_MSGID_BUGS_TO);
+ IF_FILLED (POT_CREATION_DATE);
+ IF_FILLED (PO_REVISION_DATE);
+ IF_FILLED (LAST_TRANSLATOR);
+ IF_FILLED (LANGUAGE_TEAM);
+ IF_FILLED (LANGUAGE);
+ IF_FILLED (MIME_VERSION);
+ IF_FILLED (CONTENT_TYPE);
+ IF_FILLED (CONTENT_TRANSFER);
+ if (header_fields[UNKNOWN].string != NULL)
+ stpcpy (newp, header_fields[UNKNOWN].string);
+
+#undef IF_FILLED
+
+ /* Free the temporary memory pool. */
+ obstack_free (&pool, NULL);
+
+ msgstr = cp;
+ msgstr_len = strlen (cp) + 1;
+
+ prev_msgctxt = NULL;
+ prev_msgid = NULL;
+ prev_msgid_plural = NULL;
+ }
+ else
+ {
+ msgstr = def->msgstr;
+ msgstr_len = def->msgstr_len;
+
+ if (def->is_fuzzy)
+ {
+ prev_msgctxt = def->prev_msgctxt;
+ prev_msgid = def->prev_msgid;
+ prev_msgid_plural = def->prev_msgid_plural;
+ }
+ else
+ {
+ prev_msgctxt = def->msgctxt;
+ prev_msgid = def->msgid;
+ prev_msgid_plural = def->msgid_plural;
+ }
+ }
+
+ result = message_alloc (ref->msgctxt != NULL ? xstrdup (ref->msgctxt) : NULL,
+ xstrdup (ref->msgid), ref->msgid_plural,
+ msgstr, msgstr_len, &def->pos);
+
+ /* Take the comments from the definition file. There will be none at
+ all in the reference file, as it was generated by xgettext. */
+ if (def->comment)
+ for (j = 0; j < def->comment->nitems; ++j)
+ message_comment_append (result, def->comment->item[j]);
+
+ /* Take the dot comments from the reference file, as they are
+ generated by xgettext. Any in the definition file are old ones
+ collected by previous runs of xgettext and msgmerge. */
+ if (ref->comment_dot)
+ for (j = 0; j < ref->comment_dot->nitems; ++j)
+ message_comment_dot_append (result, ref->comment_dot->item[j]);
+
+ /* The flags are mixed in a special way. Some informations come
+ from the reference message (such as format/no-format), others
+ come from the definition file (fuzzy or not). */
+ result->is_fuzzy = def->is_fuzzy | force_fuzzy;
+
+ /* If ref and def have the same msgid but different msgid_plural, it's
+ a reason to mark the result fuzzy. */
+ if (!result->is_fuzzy
+ && (ref->msgid_plural != NULL
+ ? def->msgid_plural == NULL
+ || strcmp (ref->msgid_plural, def->msgid_plural) != 0
+ : def->msgid_plural != NULL))
+ result->is_fuzzy = true;
+
+ for (i = 0; i < NFORMATS; i++)
+ {
+ result->is_format[i] = ref->is_format[i];
+
+ /* If the reference message is marked as being a format specifier,
+ but the definition message is not, we check if the resulting
+ message would pass "msgfmt -c". If yes, then all is fine. If
+ not, we add a fuzzy marker, because
+ 1. the message needs the translator's attention,
+ 2. msgmerge must not transform a PO file which passes "msgfmt -c"
+ into a PO file which doesn't. */
+ if (!result->is_fuzzy
+ && possible_format_p (ref->is_format[i])
+ && !possible_format_p (def->is_format[i])
+ && check_msgid_msgstr_format_i (ref->msgid, ref->msgid_plural,
+ msgstr, msgstr_len, i, ref->range,
+ distribution, silent_error_logger)
+ > 0)
+ result->is_fuzzy = true;
+ }
+
+ result->range = ref->range;
+ /* If the definition message was assuming a certain range, but the reference
+ message does not specify a range any more or specifies a range that is
+ not the same or a subset, we add a fuzzy marker, because
+ 1. the message needs the translator's attention,
+ 2. msgmerge must not transform a PO file which passes "msgfmt -c"
+ into a PO file which doesn't. */
+ if (!result->is_fuzzy
+ && has_range_p (def->range)
+ && !(has_range_p (ref->range)
+ && ref->range.min >= def->range.min
+ && ref->range.max <= def->range.max))
+ result->is_fuzzy = true;
+
+ result->do_wrap = ref->do_wrap;
+
+ /* Insert previous msgid, commented out with "#|".
+ Do so only when --previous is specified, for backward compatibility.
+ Since the "previous msgid" represents the original msgid that led to
+ the current msgstr,
+ - we can omit it if the resulting message is not fuzzy or is
+ untranslated (but do this in a later pass, since result->is_fuzzy
+ is not finalized at this point),
+ - otherwise, if the corresponding message from the definition file
+ was translated (not fuzzy), we use that message's msgid,
+ - otherwise, we use that message's prev_msgid. */
+ if (keep_previous)
+ {
+ result->prev_msgctxt = prev_msgctxt;
+ result->prev_msgid = prev_msgid;
+ result->prev_msgid_plural = prev_msgid_plural;
+ }
+
+ /* If the reference message was obsolete, make the resulting message
+ obsolete. This case doesn't occur for POT files, but users sometimes
+ use PO files that are themselves the result of msgmerge instead of POT
+ files. */
+ result->obsolete = ref->obsolete;
+
+ /* Take the file position comments from the reference file, as they
+ are generated by xgettext. Any in the definition file are old ones
+ collected by previous runs of xgettext and msgmerge. */
+ for (j = 0; j < ref->filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &ref->filepos[j];
+ message_comment_filepos (result, pp->file_name, pp->line_number);
+ }
+
+ /* Special postprocessing is needed if the reference message is a
+ plural form and the definition message isn't, or vice versa. */
+ if (ref->msgid_plural != NULL)
+ {
+ if (def->msgid_plural == NULL)
+ result->used = 1;
+ }
+ else
+ {
+ if (def->msgid_plural != NULL)
+ result->used = 2;
+ }
+
+ /* All done, return the merged message to the caller. */
+ return result;
+}
+
+
+#define DOT_FREQUENCY 10
+
+static void
+match_domain (const char *fn1, const char *fn2,
+ definitions_ty *definitions, message_list_ty *refmlp,
+ message_list_ty *resultmlp,
+ struct statistics *stats, unsigned int *processed)
+{
+ message_ty *header_entry;
+ unsigned long int nplurals;
+ const struct expression *plural_expr;
+ char *untranslated_plural_msgstr;
+ struct plural_distribution distribution;
+ struct search_result { message_ty *found; bool fuzzy; } *search_results;
+ size_t j;
+
+ header_entry =
+ message_list_search (definitions_current_list (definitions), NULL, "");
+ extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
+ &plural_expr, &nplurals);
+ untranslated_plural_msgstr = XNMALLOC (nplurals, char);
+ memset (untranslated_plural_msgstr, '\0', nplurals);
+
+ /* Determine the plural distribution of the plural_expr formula. */
+ {
+ /* Disable error output temporarily. */
+ void (*old_po_xerror) (int, const struct message_ty *, const char *, size_t,
+ size_t, int, const char *)
+ = po_xerror;
+ po_xerror = silent_xerror;
+
+ if (check_plural_eval (plural_expr, nplurals, header_entry,
+ &distribution) > 0)
+ {
+ distribution.expr = NULL;
+ distribution.often = NULL;
+ distribution.often_length = 0;
+ distribution.histogram = NULL;
+ }
+
+ po_xerror = old_po_xerror;
+ }
+
+ /* Most of the time is spent in definitions_search_fuzzy.
+ Perform it in a separate loop that can be parallelized by an OpenMP
+ capable compiler. */
+ search_results = XNMALLOC (refmlp->nitems, struct search_result);
+ {
+ long int nn = refmlp->nitems;
+ long int jj;
+
+ /* Tell the OpenMP capable compiler to distribute this loop across
+ several threads. The schedule is dynamic, because for some messages
+ the loop body can be executed very quickly, whereas for others it takes
+ a long time.
+ Note: The Sun Workshop 6.2 C compiler does not allow a space between
+ '#' and 'pragma'. */
+ #ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic)
+ #endif
+ for (jj = 0; jj < nn; jj++)
+ {
+ message_ty *refmsg = refmlp->item[jj];
+ message_ty *defmsg;
+
+ /* Because merging can take a while we print something to signal
+ we are not dead. */
+ if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCY == 0)
+ fputc ('.', stderr);
+ #ifdef _OPENMP
+ #pragma omp atomic
+ #endif
+ (*processed)++;
+
+ /* See if it is in the other file. */
+ defmsg =
+ definitions_search (definitions, refmsg->msgctxt, refmsg->msgid);
+ if (defmsg != NULL)
+ {
+ search_results[jj].found = defmsg;
+ search_results[jj].fuzzy = false;
+ }
+ else if (!is_header (refmsg)
+ /* If the message was not defined at all, try to find a very
+ similar message, it could be a typo, or the suggestion may
+ help. */
+ && use_fuzzy_matching
+ && ((defmsg =
+ definitions_search_fuzzy (definitions,
+ refmsg->msgctxt,
+ refmsg->msgid)) != NULL))
+ {
+ search_results[jj].found = defmsg;
+ search_results[jj].fuzzy = true;
+ }
+ else
+ search_results[jj].found = NULL;
+ }
+ }
+
+ for (j = 0; j < refmlp->nitems; j++)
+ {
+ message_ty *refmsg = refmlp->item[j];
+
+ /* See if it is in the other file.
+ This used definitions_search. */
+ if (search_results[j].found != NULL && !search_results[j].fuzzy)
+ {
+ message_ty *defmsg = search_results[j].found;
+ /* Merge the reference with the definition: take the #. and
+ #: comments from the reference, take the # comments from
+ the definition, take the msgstr from the definition. Add
+ this merged entry to the output message list. */
+ message_ty *mp =
+ message_merge (defmsg, refmsg, false, &distribution);
+
+ message_list_append (resultmlp, mp);
+
+ /* Remember that this message has been used, when we scan
+ later to see if anything was omitted. */
+ defmsg->used = 1;
+ stats->merged++;
+ }
+ else if (!is_header (refmsg))
+ {
+ /* If the message was not defined at all, try to find a very
+ similar message, it could be a typo, or the suggestion may
+ help. This search assumed use_fuzzy_matching and used
+ definitions_search_fuzzy. */
+ if (search_results[j].found != NULL && search_results[j].fuzzy)
+ {
+ message_ty *defmsg = search_results[j].found;
+ message_ty *mp;
+
+ if (verbosity_level > 1)
+ {
+ po_gram_error_at_line (&refmsg->pos, _("\
+this message is used but not defined..."));
+ error_message_count--;
+ po_gram_error_at_line (&defmsg->pos, _("\
+...but this definition is similar"));
+ }
+
+ /* Merge the reference with the definition: take the #. and
+ #: comments from the reference, take the # comments from
+ the definition, take the msgstr from the definition. Add
+ this merged entry to the output message list. */
+ mp = message_merge (defmsg, refmsg, true, &distribution);
+
+ message_list_append (resultmlp, mp);
+
+ /* Remember that this message has been used, when we scan
+ later to see if anything was omitted. */
+ defmsg->used = 1;
+ stats->fuzzied++;
+ if (!quiet && verbosity_level <= 1)
+ /* Always print a dot if we handled a fuzzy match. */
+ fputc ('.', stderr);
+ }
+ else
+ {
+ message_ty *mp;
+ bool is_untranslated;
+ const char *p;
+ const char *pend;
+
+ if (verbosity_level > 1)
+ po_gram_error_at_line (&refmsg->pos, _("\
+this message is used but not defined in %s"), fn1);
+
+ mp = message_copy (refmsg);
+
+ if (mp->msgid_plural != NULL)
+ {
+ /* Test if mp is untranslated. (It most likely is.) */
+ is_untranslated = true;
+ for (p = mp->msgstr, pend = p + mp->msgstr_len; p < pend; p++)
+ if (*p != '\0')
+ {
+ is_untranslated = false;
+ break;
+ }
+ if (is_untranslated)
+ {
+ /* Change mp->msgstr_len consecutive empty strings into
+ nplurals consecutive empty strings. */
+ if (nplurals > mp->msgstr_len)
+ mp->msgstr = untranslated_plural_msgstr;
+ mp->msgstr_len = nplurals;
+ }
+ }
+
+ message_list_append (resultmlp, mp);
+ stats->missing++;
+ }
+ }
+ }
+
+ free (search_results);
+
+ /* Now postprocess the problematic merges. This is needed because we
+ want the result to pass the "msgfmt -c -v" check. */
+ {
+ /* message_merge sets mp->used to 1 or 2, depending on the problem.
+ Compute the bitwise OR of all these. */
+ int problematic = 0;
+
+ for (j = 0; j < resultmlp->nitems; j++)
+ problematic |= resultmlp->item[j]->used;
+
+ if (problematic)
+ {
+ unsigned long int nplurals = 0;
+
+ if (problematic & 1)
+ {
+ /* Need to know nplurals of the result domain. */
+ message_ty *header_entry =
+ message_list_search (resultmlp, NULL, "");
+
+ nplurals = get_plural_count (header_entry
+ ? header_entry->msgstr
+ : NULL);
+ }
+
+ for (j = 0; j < resultmlp->nitems; j++)
+ {
+ message_ty *mp = resultmlp->item[j];
+
+ if ((mp->used & 1) && (nplurals > 0))
+ {
+ /* ref->msgid_plural != NULL but def->msgid_plural == NULL.
+ Use a copy of def->msgstr for each possible plural form. */
+ size_t new_msgstr_len;
+ char *new_msgstr;
+ char *p;
+ unsigned long i;
+
+ if (verbosity_level > 1)
+ {
+ po_gram_error_at_line (&mp->pos, _("\
+this message should define plural forms"));
+ }
+
+ new_msgstr_len = nplurals * mp->msgstr_len;
+ new_msgstr = XNMALLOC (new_msgstr_len, char);
+ for (i = 0, p = new_msgstr; i < nplurals; i++)
+ {
+ memcpy (p, mp->msgstr, mp->msgstr_len);
+ p += mp->msgstr_len;
+ }
+ mp->msgstr = new_msgstr;
+ mp->msgstr_len = new_msgstr_len;
+ mp->is_fuzzy = true;
+ }
+
+ if ((mp->used & 2) && (mp->msgstr_len > strlen (mp->msgstr) + 1))
+ {
+ /* ref->msgid_plural == NULL but def->msgid_plural != NULL.
+ Use only the first among the plural forms. */
+
+ if (verbosity_level > 1)
+ {
+ po_gram_error_at_line (&mp->pos, _("\
+this message should not define plural forms"));
+ }
+
+ mp->msgstr_len = strlen (mp->msgstr) + 1;
+ mp->is_fuzzy = true;
+ }
+
+ /* Postprocessing of this message is done. */
+ mp->used = 0;
+ }
+ }
+ }
+
+ /* Now that mp->is_fuzzy is finalized for all messages, remove the
+ "previous msgid" information from all messages that are not fuzzy or
+ are untranslated. */
+ for (j = 0; j < resultmlp->nitems; j++)
+ {
+ message_ty *mp = resultmlp->item[j];
+
+ if (!mp->is_fuzzy || mp->msgstr[0] == '\0')
+ {
+ mp->prev_msgctxt = NULL;
+ mp->prev_msgid = NULL;
+ mp->prev_msgid_plural = NULL;
+ }
+ }
+}
+
+static msgdomain_list_ty *
+merge (const char *fn1, const char *fn2, catalog_input_format_ty input_syntax,
+ msgdomain_list_ty **defp)
+{
+ msgdomain_list_ty *def;
+ msgdomain_list_ty *ref;
+ size_t j, k;
+ unsigned int processed;
+ struct statistics stats;
+ msgdomain_list_ty *result;
+ const char *def_canon_charset;
+ definitions_ty definitions;
+ message_list_ty *empty_list;
+
+ stats.merged = stats.fuzzied = stats.missing = stats.obsolete = 0;
+
+ /* This is the definitions file, created by a human. */
+ def = read_catalog_file (fn1, input_syntax);
+
+ /* This is the references file, created by groping the sources with
+ the xgettext program. */
+ ref = read_catalog_file (fn2, input_syntax);
+ /* Add a dummy header entry, if the references file contains none. */
+ for (k = 0; k < ref->nitems; k++)
+ if (message_list_search (ref->item[k]->messages, NULL, "") == NULL)
+ {
+ static lex_pos_ty pos = { __FILE__, __LINE__ };
+ message_ty *refheader = message_alloc (NULL, "", NULL, "", 1, &pos);
+
+ message_list_prepend (ref->item[k]->messages, refheader);
+ }
+
+ /* The references file can be either in ASCII or in UTF-8. If it is
+ in UTF-8, we have to convert the definitions and the compendiums to
+ UTF-8 as well. */
+ {
+ bool was_utf8 = false;
+ for (k = 0; k < ref->nitems; k++)
+ {
+ message_list_ty *mlp = ref->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ if (len == strlen ("UTF-8")
+ && c_strncasecmp (charsetstr, "UTF-8", len) == 0)
+ was_utf8 = true;
+ }
+ }
+ }
+ }
+ if (was_utf8)
+ {
+ def = iconv_msgdomain_list (def, "UTF-8", true, fn1);
+ if (compendiums != NULL)
+ for (k = 0; k < compendiums->nitems; k++)
+ iconv_message_list (compendiums->item[k], NULL, po_charset_utf8,
+ compendium_filenames->item[k]);
+ }
+ else if (compendiums != NULL && compendiums->nitems > 0)
+ {
+ /* Ensure that the definitions and the compendiums are in the same
+ encoding. Prefer the encoding of the definitions file, if
+ possible; otherwise, if the definitions file is empty and the
+ compendiums are all in the same encoding, use that encoding;
+ otherwise, use UTF-8. */
+ bool conversion_done = false;
+ {
+ char *charset = NULL;
+
+ /* Get the encoding of the definitions file. */
+ for (k = 0; k < def->nitems; k++)
+ {
+ message_list_ty *mlp = def->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+ break;
+ }
+ }
+ }
+ if (charset != NULL)
+ break;
+ }
+ if (charset != NULL)
+ {
+ const char *canon_charset = po_charset_canonicalize (charset);
+
+ if (canon_charset != NULL)
+ {
+ bool all_compendiums_iconvable = true;
+
+ if (compendiums != NULL)
+ for (k = 0; k < compendiums->nitems; k++)
+ if (!is_message_list_iconvable (compendiums->item[k],
+ NULL, canon_charset))
+ {
+ all_compendiums_iconvable = false;
+ break;
+ }
+
+ if (all_compendiums_iconvable)
+ {
+ /* Convert the compendiums to def's encoding. */
+ if (compendiums != NULL)
+ for (k = 0; k < compendiums->nitems; k++)
+ iconv_message_list (compendiums->item[k],
+ NULL, canon_charset,
+ compendium_filenames->item[k]);
+ conversion_done = true;
+ }
+ }
+ freea (charset);
+ }
+ }
+ if (!conversion_done)
+ {
+ if (def->nitems == 0
+ || (def->nitems == 1 && def->item[0]->messages->nitems == 0))
+ {
+ /* The definitions file is empty.
+ Compare the encodings of the compendiums. */
+ const char *common_canon_charset = NULL;
+
+ for (k = 0; k < compendiums->nitems; k++)
+ {
+ message_list_ty *mlp = compendiums->item[k];
+ char *charset = NULL;
+ const char *canon_charset = NULL;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr =
+ c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ break;
+ }
+ }
+ }
+ if (charset != NULL)
+ {
+ canon_charset = po_charset_canonicalize (charset);
+ freea (charset);
+ }
+ /* If no charset declaration was found in this file,
+ or if it is not a valid encoding name, or if it
+ differs from the common charset found so far,
+ we have no common charset. */
+ if (canon_charset == NULL
+ || (common_canon_charset != NULL
+ && canon_charset != common_canon_charset))
+ {
+ common_canon_charset = NULL;
+ break;
+ }
+ common_canon_charset = canon_charset;
+ }
+
+ if (common_canon_charset != NULL)
+ /* No conversion needed in this case. */
+ conversion_done = true;
+ }
+ if (!conversion_done)
+ {
+ /* It's too hairy to find out what would be the optimal target
+ encoding. So, convert everything to UTF-8. */
+ def = iconv_msgdomain_list (def, "UTF-8", true, fn1);
+ if (compendiums != NULL)
+ for (k = 0; k < compendiums->nitems; k++)
+ iconv_message_list (compendiums->item[k],
+ NULL, po_charset_utf8,
+ compendium_filenames->item[k]);
+ }
+ }
+ }
+ }
+
+ /* Determine canonicalized encoding name of the definitions now, after
+ conversion. Only used for fuzzy matching. */
+ if (use_fuzzy_matching)
+ {
+ def_canon_charset = def->encoding;
+ if (def_canon_charset == NULL)
+ {
+ char *charset = NULL;
+
+ /* Get the encoding of the definitions file. */
+ for (k = 0; k < def->nitems; k++)
+ {
+ message_list_ty *mlp = def->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ const char *header = mlp->item[j]->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+ break;
+ }
+ }
+ }
+ if (charset != NULL)
+ break;
+ }
+ if (charset != NULL)
+ def_canon_charset = po_charset_canonicalize (charset);
+ if (def_canon_charset == NULL)
+ /* Unspecified encoding. Assume unibyte encoding. */
+ def_canon_charset = po_charset_ascii;
+ }
+ }
+ else
+ def_canon_charset = NULL;
+
+ /* Initialize and preprocess the total set of message definitions. */
+ definitions_init (&definitions, def_canon_charset);
+ empty_list = message_list_alloc (false);
+
+ result = msgdomain_list_alloc (false);
+ processed = 0;
+
+ /* Every reference must be matched with its definition. */
+ if (!multi_domain_mode)
+ for (k = 0; k < ref->nitems; k++)
+ {
+ const char *domain = ref->item[k]->domain;
+ message_list_ty *refmlp = ref->item[k]->messages;
+ message_list_ty *resultmlp =
+ msgdomain_list_sublist (result, domain, true);
+ message_list_ty *defmlp;
+
+ defmlp = msgdomain_list_sublist (def, domain, false);
+ if (defmlp == NULL)
+ defmlp = empty_list;
+ definitions_set_current_list (&definitions, defmlp);
+
+ match_domain (fn1, fn2, &definitions, refmlp, resultmlp,
+ &stats, &processed);
+ }
+ else
+ {
+ /* Apply the references messages in the default domain to each of
+ the definition domains. */
+ message_list_ty *refmlp = ref->item[0]->messages;
+
+ for (k = 0; k < def->nitems; k++)
+ {
+ const char *domain = def->item[k]->domain;
+ message_list_ty *defmlp = def->item[k]->messages;
+
+ /* Ignore the default message domain if it has no messages. */
+ if (k > 0 || defmlp->nitems > 0)
+ {
+ message_list_ty *resultmlp =
+ msgdomain_list_sublist (result, domain, true);
+
+ definitions_set_current_list (&definitions, defmlp);
+
+ match_domain (fn1, fn2, &definitions, refmlp, resultmlp,
+ &stats, &processed);
+ }
+ }
+ }
+
+ definitions_destroy (&definitions);
+
+ /* Look for messages in the definition file, which are not present
+ in the reference file, indicating messages which defined but not
+ used in the program. Don't scan the compendium(s). */
+ for (k = 0; k < def->nitems; ++k)
+ {
+ const char *domain = def->item[k]->domain;
+ message_list_ty *defmlp = def->item[k]->messages;
+
+ for (j = 0; j < defmlp->nitems; j++)
+ {
+ message_ty *defmsg = defmlp->item[j];
+
+ if (!defmsg->used)
+ {
+ /* Remember the old translation although it is not used anymore.
+ But we mark it as obsolete. */
+ message_ty *mp;
+
+ mp = message_copy (defmsg);
+ /* Clear the extracted comments. */
+ if (mp->comment_dot != NULL)
+ {
+ string_list_free (mp->comment_dot);
+ mp->comment_dot = NULL;
+ }
+ /* Clear the file position comments. */
+ if (mp->filepos != NULL)
+ {
+ size_t i;
+
+ for (i = 0; i < mp->filepos_count; i++)
+ free ((char *) mp->filepos[i].file_name);
+ mp->filepos_count = 0;
+ free (mp->filepos);
+ mp->filepos = NULL;
+ }
+ /* Mark as obsolete. */
+ mp->obsolete = true;
+
+ message_list_append (msgdomain_list_sublist (result, domain, true),
+ mp);
+ stats.obsolete++;
+ }
+ }
+ }
+
+ /* Determine the known a-priori encoding, if any. */
+ if (def->encoding == ref->encoding)
+ result->encoding = def->encoding;
+
+ /* Report some statistics. */
+ if (verbosity_level > 0)
+ fprintf (stderr, _("%s\
+Read %ld old + %ld reference, \
+merged %ld, fuzzied %ld, missing %ld, obsolete %ld.\n"),
+ !quiet && verbosity_level <= 1 ? "\n" : "",
+ (long) def->nitems, (long) ref->nitems,
+ (long) stats.merged, (long) stats.fuzzied, (long) stats.missing,
+ (long) stats.obsolete);
+ else if (!quiet)
+ fputs (_(" done.\n"), stderr);
+
+ /* Return results. */
+ *defp = def;
+ return result;
+}
diff --git a/gettext-tools/src/msgunfmt.c b/gettext-tools/src/msgunfmt.c
new file mode 100644
index 0000000..1e6dbd2
--- /dev/null
+++ b/gettext-tools/src/msgunfmt.c
@@ -0,0 +1,555 @@
+/* msgunfmt - converts binary .mo files to Uniforum style .po files
+ Copyright (C) 1995-1998, 2000-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "msgunfmt.h"
+#include "read-mo.h"
+#include "read-java.h"
+#include "read-csharp.h"
+#include "read-resources.h"
+#include "read-tcl.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Be more verbose. */
+bool verbose;
+
+/* Java mode input file specification. */
+static bool java_mode;
+static const char *java_resource_name;
+static const char *java_locale_name;
+
+/* C# mode input file specification. */
+static bool csharp_mode;
+static const char *csharp_resource_name;
+static const char *csharp_locale_name;
+static const char *csharp_base_directory;
+
+/* C# resources mode input file specification. */
+static bool csharp_resources_mode;
+
+/* Tcl mode input file specification. */
+static bool tcl_mode;
+static const char *tcl_locale_name;
+static const char *tcl_base_directory;
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "color", optional_argument, NULL, CHAR_MAX + 6 },
+ { "csharp", no_argument, NULL, CHAR_MAX + 4 },
+ { "csharp-resources", no_argument, NULL, CHAR_MAX + 5 },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "java", no_argument, NULL, 'j' },
+ { "locale", required_argument, NULL, 'l' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "resource", required_argument, NULL, 'r' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 3 },
+ { "style", required_argument, NULL, CHAR_MAX + 7 },
+ { "tcl", no_argument, NULL, CHAR_MAX + 1 },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void read_one_file (message_list_ty *mlp, const char *filename);
+
+
+int
+main (int argc, char **argv)
+{
+ int optchar;
+ bool do_help = false;
+ bool do_version = false;
+ const char *output_file = "-";
+ msgdomain_list_ty *result;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_msgid = false;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ while ((optchar = getopt_long (argc, argv, "d:eEhijl:o:pr:svVw:",
+ long_options, NULL))
+ != EOF)
+ switch (optchar)
+ {
+ case '\0':
+ /* long option */
+ break;
+
+ case 'd':
+ csharp_base_directory = optarg;
+ tcl_base_directory = optarg;
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'j':
+ java_mode = true;
+ break;
+
+ case 'l':
+ java_locale_name = optarg;
+ csharp_locale_name = optarg;
+ tcl_locale_name = optarg;
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'r':
+ java_resource_name = optarg;
+ csharp_resource_name = optarg;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1: /* --tcl */
+ tcl_mode = true;
+ break;
+
+ case CHAR_MAX + 2: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --csharp */
+ csharp_mode = true;
+ break;
+
+ case CHAR_MAX + 5: /* --csharp-resources */
+ csharp_resources_mode = true;
+ break;
+
+ case CHAR_MAX + 6: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 7: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2010");
+ printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Check for contradicting options. */
+ {
+ unsigned int modes =
+ (java_mode ? 1 : 0)
+ | (csharp_mode ? 2 : 0)
+ | (csharp_resources_mode ? 4 : 0)
+ | (tcl_mode ? 8 : 0);
+ static const char *mode_options[] =
+ { "--java", "--csharp", "--csharp-resources", "--tcl" };
+ /* More than one bit set? */
+ if (modes & (modes - 1))
+ {
+ const char *first_option;
+ const char *second_option;
+ unsigned int i;
+ for (i = 0; ; i++)
+ if (modes & (1 << i))
+ break;
+ first_option = mode_options[i];
+ for (i = i + 1; ; i++)
+ if (modes & (1 << i))
+ break;
+ second_option = mode_options[i];
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ first_option, second_option);
+ }
+ }
+ if (java_mode)
+ {
+ if (optind < argc)
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s and explicit file names are mutually exclusive"),
+ "--java");
+ }
+ }
+ else if (csharp_mode)
+ {
+ if (optind < argc)
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s and explicit file names are mutually exclusive"),
+ "--csharp");
+ }
+ if (csharp_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ if (csharp_base_directory == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-d directory\" specification"),
+ "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else if (tcl_mode)
+ {
+ if (optind < argc)
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s and explicit file names are mutually exclusive"),
+ "--tcl");
+ }
+ if (tcl_locale_name == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-l locale\" specification"),
+ "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ if (tcl_base_directory == NULL)
+ {
+ error (EXIT_SUCCESS, 0,
+ _("%s requires a \"-d directory\" specification"),
+ "--tcl");
+ usage (EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ if (java_resource_name != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
+ "--resource", "--java", "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ if (java_locale_name != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
+ "--locale", "--java", "--csharp");
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ /* Read the given .mo file. */
+ if (java_mode)
+ {
+ result = msgdomain_read_java (java_resource_name, java_locale_name);
+ }
+ else if (csharp_mode)
+ {
+ result = msgdomain_read_csharp (csharp_resource_name, csharp_locale_name,
+ csharp_base_directory);
+ }
+ else if (tcl_mode)
+ {
+ result = msgdomain_read_tcl (tcl_locale_name, tcl_base_directory);
+ }
+ else
+ {
+ message_list_ty *mlp;
+
+ mlp = message_list_alloc (false);
+ if (optind < argc)
+ {
+ do
+ read_one_file (mlp, argv[optind]);
+ while (++optind < argc);
+ }
+ else
+ read_one_file (mlp, "-");
+
+ result = msgdomain_list_alloc (false);
+ result->item[0]->messages = mlp;
+ }
+
+ /* Sorting the list of messages. */
+ if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the resulting message list to the given .po file. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ /* No problems. */
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [FILE]...\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Convert binary message catalog to Uniforum style .po file.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Operation mode:\n"));
+ printf (_("\
+ -j, --java Java mode: input is a Java ResourceBundle class\n"));
+ printf (_("\
+ --csharp C# mode: input is a .NET .dll file\n"));
+ printf (_("\
+ --csharp-resources C# resources mode: input is a .NET .resources file\n"));
+ printf (_("\
+ --tcl Tcl mode: input is a tcl/msgcat .msg file\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ FILE ... input .mo files\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location in Java mode:\n"));
+ printf (_("\
+ -r, --resource=RESOURCE resource name\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+The class name is determined by appending the locale name to the resource name,\n\
+separated with an underscore. The class is located using the CLASSPATH.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Input file location in C# mode:\n"));
+ printf (_("\
+ -r, --resource=RESOURCE resource name\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -d DIRECTORY base directory for locale dependent .dll files\n"));
+ printf (_("\
+The -l and -d options are mandatory. The .dll file is located in a\n\
+subdirectory of the specified directory whose name depends on the locale.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location in Tcl mode:\n"));
+ printf (_("\
+ -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
+ printf (_("\
+ -d DIRECTORY base directory of .msg message catalogs\n"));
+ printf (_("\
+The -l and -d options are mandatory. The .msg file is located in the\n\
+specified directory.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write indented output style\n"));
+ printf (_("\
+ --strict write strict uniforum style\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf (_("\
+ -v, --verbose increase verbosity level\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+static void
+read_one_file (message_list_ty *mlp, const char *filename)
+{
+ if (csharp_resources_mode)
+ read_resources_file (mlp, filename);
+ else
+ read_mo_file (mlp, filename);
+}
diff --git a/gettext-tools/src/msgunfmt.cs b/gettext-tools/src/msgunfmt.cs
new file mode 100644
index 0000000..28a66b8
--- /dev/null
+++ b/gettext-tools/src/msgunfmt.cs
@@ -0,0 +1,241 @@
+/* GNU gettext for C#
+ * Copyright (C) 2003-2004, 2007 Free Software Foundation, Inc.
+ * Written by Bruno Haible <bruno@clisp.org>, 2003.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This program dumps a GettextResourceSet subclass (in a satellite assembly)
+ * or a .resources file as a PO file.
+ */
+
+using System; /* Object, String, Type, Console, Exception */
+using System.Reflection; /* Assembly, MethodInfo, ConstructorInfo */
+using System.Collections; /* Hashtable, DictionaryEntry */
+using System.IO; /* BufferedStream, StreamWriter, TextWriter, FileNotFoundException, Path */
+using System.Text; /* StringBuilder, UTF8Encoding */
+using System.Resources; /* ResourceReader */
+using GNU.Gettext; /* GettextResourceSet */
+
+namespace GNU.Gettext {
+ public class DumpResource {
+ private TextWriter Out;
+ private void DumpString (String str) {
+ int n = str.Length;
+ Out.Write('"');
+ for (int i = 0; i < n; i++) {
+ char c = str[i];
+ if (c == 0x0008) {
+ Out.Write('\\'); Out.Write('b');
+ } else if (c == 0x000c) {
+ Out.Write('\\'); Out.Write('f');
+ } else if (c == 0x000a) {
+ Out.Write('\\'); Out.Write('n');
+ } else if (c == 0x000d) {
+ Out.Write('\\'); Out.Write('r');
+ } else if (c == 0x0009) {
+ Out.Write('\\'); Out.Write('t');
+ } else if (c == '\\' || c == '"') {
+ Out.Write('\\'); Out.Write(c);
+ } else
+ Out.Write(c);
+ }
+ Out.Write('"');
+ }
+ private void DumpMessage (String msgid, String msgid_plural, Object msgstr) {
+ int separatorPos = msgid.IndexOf('\u0004');
+ if (separatorPos >= 0) {
+ String msgctxt = msgid.Substring(0,separatorPos);
+ msgid = msgid.Substring(separatorPos+1);
+ Out.Write("msgctxt "); DumpString(msgctxt);
+ }
+ Out.Write("msgid "); DumpString(msgid); Out.Write('\n');
+ if (msgid_plural != null) {
+ Out.Write("msgid_plural "); DumpString(msgid_plural); Out.Write('\n');
+ for (int i = 0; i < (msgstr as String[]).Length; i++) {
+ Out.Write("msgstr[" + i + "] ");
+ DumpString((msgstr as String[])[i]);
+ Out.Write('\n');
+ }
+ } else {
+ Out.Write("msgstr "); DumpString(msgstr as String); Out.Write('\n');
+ }
+ Out.Write('\n');
+ }
+
+ // ---------------- Dumping a GettextResourceSet ----------------
+
+ private void Dump (GettextResourceSet catalog) {
+ MethodInfo pluralMethod =
+ catalog.GetType().GetMethod("GetMsgidPluralTable", Type.EmptyTypes);
+ // Search for the header entry.
+ {
+ Object header_entry = catalog.GetObject("");
+ // If there is no header entry, fake one.
+ // FIXME: This is not needed; right after po_lex_charset_init set
+ // the PO charset to UTF-8.
+ if (header_entry == null)
+ header_entry = "Content-Type: text/plain; charset=UTF-8\n";
+ DumpMessage("", null, header_entry);
+ }
+ // Now the other messages.
+ {
+ Hashtable plural = null;
+ if (pluralMethod != null)
+ plural = pluralMethod.Invoke(catalog, new Object[0]) as Hashtable;
+ foreach (String key in catalog.Keys)
+ if (!"".Equals(key)) {
+ Object value = catalog.GetObject(key);
+ String key_plural =
+ (plural != null && value is String[] ? plural[key] as String : null);
+ DumpMessage(key, key_plural, value);
+ }
+ }
+ }
+ // Essentially taken from class GettextResourceManager.
+ private static Assembly GetSatelliteAssembly (String baseDirectory, String resourceName, String cultureName) {
+ String satelliteExpectedLocation =
+ baseDirectory
+ + Path.DirectorySeparatorChar + cultureName
+ + Path.DirectorySeparatorChar + resourceName + ".resources.dll";
+ return Assembly.LoadFrom(satelliteExpectedLocation);
+ }
+ // Taken from class GettextResourceManager.
+ private static String ConstructClassName (String resourceName) {
+ // We could just return an arbitrary fixed class name, like "Messages",
+ // assuming that every assembly will only ever contain one
+ // GettextResourceSet subclass, but this assumption would break the day
+ // we want to support multi-domain PO files in the same format...
+ bool valid = (resourceName.Length > 0);
+ for (int i = 0; valid && i < resourceName.Length; i++) {
+ char c = resourceName[i];
+ if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
+ || (i > 0 && c >= '0' && c <= '9')))
+ valid = false;
+ }
+ if (valid)
+ return resourceName;
+ else {
+ // Use hexadecimal escapes, using the underscore as escape character.
+ String hexdigit = "0123456789abcdef";
+ StringBuilder b = new StringBuilder();
+ b.Append("__UESCAPED__");
+ for (int i = 0; i < resourceName.Length; i++) {
+ char c = resourceName[i];
+ if (c >= 0xd800 && c < 0xdc00
+ && i+1 < resourceName.Length
+ && resourceName[i+1] >= 0xdc00 && resourceName[i+1] < 0xe000) {
+ // Combine two UTF-16 words to a character.
+ char c2 = resourceName[i+1];
+ int uc = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ b.Append('_');
+ b.Append('U');
+ b.Append(hexdigit[(uc >> 28) & 0x0f]);
+ b.Append(hexdigit[(uc >> 24) & 0x0f]);
+ b.Append(hexdigit[(uc >> 20) & 0x0f]);
+ b.Append(hexdigit[(uc >> 16) & 0x0f]);
+ b.Append(hexdigit[(uc >> 12) & 0x0f]);
+ b.Append(hexdigit[(uc >> 8) & 0x0f]);
+ b.Append(hexdigit[(uc >> 4) & 0x0f]);
+ b.Append(hexdigit[uc & 0x0f]);
+ i++;
+ } else if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9'))) {
+ int uc = c;
+ b.Append('_');
+ b.Append('u');
+ b.Append(hexdigit[(uc >> 12) & 0x0f]);
+ b.Append(hexdigit[(uc >> 8) & 0x0f]);
+ b.Append(hexdigit[(uc >> 4) & 0x0f]);
+ b.Append(hexdigit[uc & 0x0f]);
+ } else
+ b.Append(c);
+ }
+ return b.ToString();
+ }
+ }
+ // Essentially taken from class GettextResourceManager.
+ private static GettextResourceSet InstantiateResourceSet (Assembly satelliteAssembly, String resourceName, String cultureName) {
+ Type clazz = satelliteAssembly.GetType(ConstructClassName(resourceName)+"_"+cultureName.Replace('-','_'));
+ ConstructorInfo constructor = clazz.GetConstructor(Type.EmptyTypes);
+ return constructor.Invoke(null) as GettextResourceSet;
+ }
+ public DumpResource (String baseDirectory, String resourceName, String cultureName) {
+ // We are only interested in the messages belonging to the locale
+ // itself, not in the inherited messages. Therefore we instantiate just
+ // the GettextResourceSet, not a GettextResourceManager.
+ Assembly satelliteAssembly =
+ GetSatelliteAssembly(baseDirectory, resourceName, cultureName);
+ GettextResourceSet catalog =
+ InstantiateResourceSet(satelliteAssembly, resourceName, cultureName);
+ BufferedStream stream = new BufferedStream(Console.OpenStandardOutput());
+ Out = new StreamWriter(stream, new UTF8Encoding());
+ Dump(catalog);
+ Out.Close();
+ stream.Close();
+ }
+
+ // ----------------- Dumping a .resources file ------------------
+
+ public DumpResource (String filename) {
+ BufferedStream stream = new BufferedStream(Console.OpenStandardOutput());
+ Out = new StreamWriter(stream, new UTF8Encoding());
+ ResourceReader rr;
+ if (filename.Equals("-")) {
+ BufferedStream input = new BufferedStream(Console.OpenStandardInput());
+ // A temporary output stream is needed because ResourceReader expects
+ // to be able to seek in the Stream.
+ byte[] contents;
+ {
+ MemoryStream tmpstream = new MemoryStream();
+ byte[] buf = new byte[1024];
+ for (;;) {
+ int n = input.Read(buf, 0, 1024);
+ if (n == 0)
+ break;
+ tmpstream.Write(buf, 0, n);
+ }
+ contents = tmpstream.ToArray();
+ tmpstream.Close();
+ }
+ MemoryStream tmpinput = new MemoryStream(contents);
+ rr = new ResourceReader(tmpinput);
+ } else {
+ rr = new ResourceReader(filename);
+ }
+ foreach (DictionaryEntry entry in rr) // uses rr.GetEnumerator()
+ DumpMessage(entry.Key as String, null, entry.Value as String);
+ rr.Close();
+ Out.Close();
+ stream.Close();
+ }
+
+ // --------------------------------------------------------------
+
+ public static int Main (String[] args) {
+ try {
+ if (args.Length > 1)
+ new DumpResource(args[0], args[1], args[2]);
+ else
+ new DumpResource(args[0]);
+ } catch (Exception e) {
+ Console.Error.WriteLine(e);
+ Console.Error.WriteLine(e.StackTrace);
+ return 1;
+ }
+ return 0;
+ }
+ }
+}
diff --git a/gettext-tools/src/msgunfmt.h b/gettext-tools/src/msgunfmt.h
new file mode 100644
index 0000000..d1d79ec
--- /dev/null
+++ b/gettext-tools/src/msgunfmt.h
@@ -0,0 +1,24 @@
+/* msgunfmt specifics
+ Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _MSGUNFMT_H
+#define _MSGUNFMT_H
+
+/* Be more verbose. */
+extern bool verbose;
+
+#endif /* _MSGUNFMT_H */
diff --git a/gettext-tools/src/msgunfmt.tcl b/gettext-tools/src/msgunfmt.tcl
new file mode 100644
index 0000000..11c1d6f
--- /dev/null
+++ b/gettext-tools/src/msgunfmt.tcl
@@ -0,0 +1,82 @@
+# Reading tcl/msgcat .msg files.
+# Copyright (C) 2002 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 <http://www.gnu.org/licenses/>.
+
+namespace eval msgcat {
+ namespace export mcset mcdump
+ variable header ""
+}
+
+proc msgcat::puts_po_string {str} {
+ # Replace " with \"
+ regsub -all "\"" $str "\\\"" str
+ # Replace \ with \\
+ regsub -all "\\\\" $str "\\\\\\" str
+ # Replace newline with \n
+ regsub -all [subst "\n"] $str "\\n" str
+ regsub -all [subst "\a"] $str "\\a" str
+ regsub -all [subst "\b"] $str "\\b" str
+ regsub -all [subst "\f"] $str "\\f" str
+ regsub -all [subst "\r"] $str "\\r" str
+ regsub -all [subst "\t"] $str "\\t" str
+ regsub -all [subst "\v"] $str "\\v" str
+ # Output it.
+ puts -nonewline "\"$str\""
+}
+
+proc msgcat::write_po_message {msgid msgstr} {
+ puts -nonewline "msgid "
+ puts_po_string $msgid
+ puts ""
+ puts -nonewline "msgstr "
+ puts_po_string $msgstr
+ puts ""
+ puts ""
+}
+
+# This gets called once for each message in the .msg catalog.
+proc msgcat::mcset {locale src {dest ""}} {
+ msgcat::write_po_message $src $dest
+}
+
+# Main function.
+proc msgcat::mcdump {langfile} {
+ if {[file exists $langfile]} {
+ # msgunfmt expects the output in UTF-8 encoding.
+ fconfigure stdout -encoding utf-8
+
+ set msgcat::header ""
+
+ set fd [open $langfile r]
+ # In newer tcl versions, the .msg files are in UTF-8 encoding.
+ fconfigure $fd -encoding utf-8
+ eval [read $fd]
+ close $fd
+
+ if {$msgcat::header == ""} {
+ # Provide a minimal header.
+ set msgcat::header [subst "MIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n"]
+ }
+ msgcat::write_po_message "" $msgcat::header
+ } else {
+ # Tell msgunfmt to emit an internationalized error message.
+ exit 2
+ }
+}
+
+# Main code: call the main function on the first and only argument.
+msgcat::mcdump [lindex $argv 0]
+
+exit 0
diff --git a/gettext-tools/src/msguniq.c b/gettext-tools/src/msguniq.c
new file mode 100644
index 0000000..24f4c31
--- /dev/null
+++ b/gettext-tools/src/msguniq.c
@@ -0,0 +1,434 @@
+/* Remove, select or merge duplicate translations.
+ Copyright (C) 2001-2007, 2009-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "closeout.h"
+#include "dir-list.h"
+#include "str-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "message.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "msgl-cat.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Target encoding. */
+static const char *to_code;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-location", optional_argument, NULL, 'n' },
+ { "color", optional_argument, NULL, CHAR_MAX + 5 },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "force-po", no_argument, &force_po, 1 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 7 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
+ { "output-file", required_argument, NULL, 'o' },
+ { "properties-input", no_argument, NULL, 'P' },
+ { "properties-output", no_argument, NULL, 'p' },
+ { "repeated", no_argument, NULL, 'd' },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
+ { "style", required_argument, NULL, CHAR_MAX + 6 },
+ { "to-code", required_argument, NULL, 't' },
+ { "unique", no_argument, NULL, 'u' },
+ { "use-first", no_argument, NULL, CHAR_MAX + 1 },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+
+
+int
+main (int argc, char **argv)
+{
+ int optchar;
+ bool do_help;
+ bool do_version;
+ char *output_file;
+ const char *input_file;
+ string_list_ty *file_list;
+ msgdomain_list_ty *result;
+ catalog_input_format_ty input_syntax = &input_format_po;
+ catalog_output_format_ty output_syntax = &output_format_po;
+ bool sort_by_msgid = false;
+ bool sort_by_filepos = false;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+ output_file = NULL;
+ input_file = NULL;
+ more_than = 0;
+ less_than = INT_MAX;
+ use_first = false;
+
+ while ((optchar = getopt_long (argc, argv, "dD:eEFhino:pPst:uVw:",
+ long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'd':
+ more_than = 1;
+ less_than = INT_MAX;
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ output_syntax = &output_format_properties;
+ break;
+
+ case 'P':
+ input_syntax = &input_format_properties;
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 't':
+ to_code = optarg;
+ break;
+
+ case 'u':
+ more_than = 0;
+ less_than = 2;
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case CHAR_MAX + 1:
+ use_first = true;
+ break;
+
+ case CHAR_MAX + 2: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 3: /* --stringtable-input */
+ input_syntax = &input_format_stringtable;
+ break;
+
+ case CHAR_MAX + 4: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 5: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 6: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 7: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2010");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test whether we have an .po file name as argument. */
+ if (optind == argc)
+ input_file = "-";
+ else if (optind + 1 == argc)
+ input_file = argv[optind];
+ else
+ {
+ error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* Determine list of files we have to process: a single file. */
+ file_list = string_list_alloc ();
+ string_list_append (file_list, input_file);
+
+ /* Read input files, then filter, convert and merge messages. */
+ allow_duplicates = true;
+ result = catenate_msgdomain_list (file_list, input_syntax, to_code);
+
+ string_list_free (file_list);
+
+ /* Sorting the list of messages. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (result);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (result);
+
+ /* Write the PO file. */
+ msgdomain_list_print (result, output_file, output_syntax, force_po, false);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Unifies duplicate translations in a translation catalog.\n\
+Finds duplicate translations of the same message ID. Such duplicates are\n\
+invalid input for other programs like msgfmt, msgmerge or msgcat. By\n\
+default, duplicates are merged together. When using the --repeated option,\n\
+only duplicates are output, and all other messages are discarded. Comments\n\
+and extracted comments will be cumulated, except that if --use-first is\n\
+specified, they will be taken from the first translation. File positions\n\
+will be cumulated. When using the --unique option, duplicates are discarded.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE input PO file\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If no input file is given or if it is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -o, --output-file=FILE write output to specified file\n"));
+ printf (_("\
+The results are written to standard output if no output file is specified\n\
+or if it is -.\n"));
+ printf ("\n");
+ printf (_("\
+Message selection:\n"));
+ printf (_("\
+ -d, --repeated print only duplicates\n"));
+ printf (_("\
+ -u, --unique print only unique messages, discard duplicates\n"));
+ printf ("\n");
+ printf (_("\
+Input file syntax:\n"));
+ printf (_("\
+ -P, --properties-input input file is in Java .properties syntax\n"));
+ printf (_("\
+ --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ -t, --to-code=NAME encoding for output\n"));
+ printf (_("\
+ --use-first use first available translation for each\n\
+ message, don't merge several translations\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write the .po file using indented style\n"));
+ printf (_("\
+ --no-location do not write '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location generate '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict write out strict Uniforum conforming .po file\n"));
+ printf (_("\
+ -p, --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
diff --git a/gettext-tools/src/open-catalog.c b/gettext-tools/src/open-catalog.c
new file mode 100644
index 0000000..27eed90
--- /dev/null
+++ b/gettext-tools/src/open-catalog.c
@@ -0,0 +1,128 @@
+/* open-po - search for .po file along search path list and open for reading
+ Copyright (C) 1995-1996, 2000-2003, 2005-2009 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "open-catalog.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dir-list.h"
+#include "filename.h"
+#include "concat-filename.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* This macro is used to determine the number of elements in an array. */
+#define SIZEOF(a) (sizeof(a)/sizeof(a[0]))
+
+static FILE *
+try_open_catalog_file (const char *input_name, char **real_file_name_p)
+{
+ static const char *extension[] = { "", ".po", ".pot", };
+ char *file_name;
+ FILE *ret_val;
+ int j;
+ size_t k;
+ const char *dir;
+
+ if (strcmp (input_name, "-") == 0 || strcmp (input_name, "/dev/stdin") == 0)
+ {
+ *real_file_name_p = xstrdup (_("<stdin>"));
+ return stdin;
+ }
+
+ /* We have a real name for the input file. If the name is absolute,
+ try the various extensions, but ignore the directory search list. */
+ if (IS_ABSOLUTE_PATH (input_name))
+ {
+ for (k = 0; k < SIZEOF (extension); ++k)
+ {
+ file_name = xconcatenated_filename ("", input_name, extension[k]);
+
+ ret_val = fopen (file_name, "r");
+ if (ret_val != NULL || errno != ENOENT)
+ {
+ /* We found the file. */
+ *real_file_name_p = file_name;
+ return ret_val;
+ }
+
+ free (file_name);
+ }
+ }
+ else
+ {
+ /* For relative file names, look through the directory search list,
+ trying the various extensions. If no directory search list is
+ specified, the current directory is used. */
+ for (j = 0; (dir = dir_list_nth (j)) != NULL; ++j)
+ for (k = 0; k < SIZEOF (extension); ++k)
+ {
+ file_name = xconcatenated_filename (dir, input_name, extension[k]);
+
+ ret_val = fopen (file_name, "r");
+ if (ret_val != NULL || errno != ENOENT)
+ {
+ *real_file_name_p = file_name;
+ return ret_val;
+ }
+
+ free (file_name);
+ }
+ }
+
+ /* File does not exist. */
+ *real_file_name_p = xstrdup (input_name);
+ errno = ENOENT;
+ return NULL;
+}
+
+/* Open the input file with the name INPUT_NAME. The ending .po is added
+ if necessary. If INPUT_NAME is not an absolute file name and the file is
+ not found, the list of directories in "dir-list.h" is searched. The
+ file's pathname is returned in *REAL_FILE_NAME_P, for error message
+ purposes. */
+FILE *
+open_catalog_file (const char *input_name, char **real_file_name_p,
+ bool exit_on_error)
+{
+ FILE *fp = try_open_catalog_file (input_name, real_file_name_p);
+
+ if (fp == NULL && exit_on_error)
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while opening \"%s\" for reading"),
+ *real_file_name_p),
+ errno_description));
+ }
+
+ return fp;
+}
diff --git a/gettext-tools/src/open-catalog.h b/gettext-tools/src/open-catalog.h
new file mode 100644
index 0000000..503371d
--- /dev/null
+++ b/gettext-tools/src/open-catalog.h
@@ -0,0 +1,43 @@
+/* Opening PO files.
+ Copyright (C) 1995-1997, 2000-2003, 2006 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _OPEN_CATALOG_H
+#define _OPEN_CATALOG_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Open the input file with the name INPUT_NAME. The ending .po is added
+ if necessary. If INPUT_NAME is not an absolute file name and the file is
+ not found, the list of directories in "dir-list.h" is searched. The
+ file's pathname is returned in *REAL_FILE_NAME_P, for error message
+ purposes. */
+extern FILE *open_catalog_file (const char *input_name,
+ char **real_file_name_p, bool exit_on_error);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _OPEN_CATALOG_H */
diff --git a/gettext-tools/src/plural-count.c b/gettext-tools/src/plural-count.c
new file mode 100644
index 0000000..3fd8838
--- /dev/null
+++ b/gettext-tools/src/plural-count.c
@@ -0,0 +1,38 @@
+/* Plural form count.
+ Copyright (C) 2003, 2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "plural-count.h"
+
+#include "plural-exp.h"
+
+/* Extract the number of plural forms from a header entry. */
+
+unsigned long int
+get_plural_count (const char *header)
+{
+ const struct expression *plural;
+ unsigned long int nplurals;
+
+ extract_plural_expression (header, &plural, &nplurals);
+
+ return nplurals;
+}
diff --git a/gettext-tools/src/plural-count.h b/gettext-tools/src/plural-count.h
new file mode 100644
index 0000000..14eccf9
--- /dev/null
+++ b/gettext-tools/src/plural-count.h
@@ -0,0 +1,29 @@
+/* Plural form count.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Extract the number of plural forms from a header entry. */
+extern unsigned long int get_plural_count (const char *header);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/plural-distrib.h b/gettext-tools/src/plural-distrib.h
new file mode 100644
index 0000000..4376f9f
--- /dev/null
+++ b/gettext-tools/src/plural-distrib.h
@@ -0,0 +1,57 @@
+/* Value distribution of plural form expressions.
+ Copyright (C) 2001-2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2001-2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PLURAL_DISTRIB_H
+#define _PLURAL_DISTRIB_H
+
+
+/* Definition of 'struct expression'. */
+#include "plural-exp.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* The value distribution of a plural formula. */
+struct plural_distribution
+{
+ /* The plural formula as a parsed expression. */
+ const struct expression *expr;
+
+ /* OFTEN is either NULL or an array of nplurals elements,
+ OFTEN[j] being true if the value j appears to be assumed infinitely often
+ by the plural formula. */
+ const unsigned char *often;
+
+ /* The length of the OFTEN array. */
+ unsigned long often_length;
+
+ /* A function which evaluates the plural formula for min <= n <= max
+ and returns the estimated number of times the value j was assumed. */
+ unsigned int (*histogram) (const struct plural_distribution *self,
+ int min, int max, unsigned long j);
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _PLURAL_DISTRIB_H */
diff --git a/gettext-tools/src/plural-eval.c b/gettext-tools/src/plural-eval.c
new file mode 100644
index 0000000..6c63d4e
--- /dev/null
+++ b/gettext-tools/src/plural-eval.c
@@ -0,0 +1,93 @@
+/* Expression evaluation for plural form selection.
+ Copyright (C) 2000-2003, 2005 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "plural-eval.h"
+
+#include <stddef.h>
+#include <signal.h>
+
+#include "plural-exp.h"
+
+
+#define STATIC /*extern*/
+
+/* Include the expression evaluation code from libintl, this time with
+ 'extern' linkage. */
+#include "eval-plural.h"
+
+
+/* Exit point. Must be set before calling install_sigfpe_handler(). */
+sigjmp_buf sigfpe_exit;
+
+#if USE_SIGINFO
+
+/* Additional information that is set before sigfpe_exit is invoked. */
+int sigfpe_code;
+
+/* Signal handler called in case of arithmetic exception (e.g. division
+ by zero) during plural_eval. */
+static void
+sigfpe_handler (int sig, siginfo_t *sip, void *scp)
+{
+ sigfpe_code = sip->si_code;
+ siglongjmp (sigfpe_exit, 1);
+}
+
+#else
+
+/* Signal handler called in case of arithmetic exception (e.g. division
+ by zero) during plural_eval. */
+static void
+sigfpe_handler (int sig)
+{
+ siglongjmp (sigfpe_exit, 1);
+}
+
+#endif
+
+void
+install_sigfpe_handler (void)
+{
+#if USE_SIGINFO
+ struct sigaction action;
+ action.sa_sigaction = sigfpe_handler;
+ action.sa_flags = SA_SIGINFO;
+ sigemptyset (&action.sa_mask);
+ sigaction (SIGFPE, &action, (struct sigaction *) NULL);
+#else
+ signal (SIGFPE, sigfpe_handler);
+#endif
+}
+
+void
+uninstall_sigfpe_handler (void)
+{
+#if USE_SIGINFO
+ struct sigaction action;
+ action.sa_handler = SIG_DFL;
+ action.sa_flags = 0;
+ sigemptyset (&action.sa_mask);
+ sigaction (SIGFPE, &action, (struct sigaction *) NULL);
+#else
+ signal (SIGFPE, SIG_DFL);
+#endif
+}
diff --git a/gettext-tools/src/plural-eval.h b/gettext-tools/src/plural-eval.h
new file mode 100644
index 0000000..b64c079
--- /dev/null
+++ b/gettext-tools/src/plural-eval.h
@@ -0,0 +1,67 @@
+/* Expression evaluation for plural form selection.
+ Copyright (C) 2005-2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PLURAL_EVAL_H
+#define _PLURAL_EVAL_H
+
+
+/* Definition of 'struct expression', and
+ declaration of extract_plural_expression() and plural_eval(). */
+#include "plural-exp.h"
+
+
+/* Protection against signals during plural evaluation. */
+
+#include <setjmp.h>
+
+/* Some platforms don't have the sigjmp_buf type in <setjmp.h>. */
+#if defined _MSC_VER || defined __MINGW32__
+/* Native Woe32 API. */
+# define sigjmp_buf jmp_buf
+# define sigsetjmp(env,savesigs) setjmp (env)
+# define siglongjmp longjmp
+#endif
+
+/* We use siginfo to get precise information about the signal.
+ But siginfo doesn't work on Irix 6.5 and on Cygwin 2005. */
+#if HAVE_SIGINFO && !defined (__sgi) && !defined (__CYGWIN__)
+# define USE_SIGINFO 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Exit point. Must be set before calling install_sigfpe_handler(). */
+extern sigjmp_buf sigfpe_exit;
+
+#if USE_SIGINFO
+/* Additional information that is set before sigfpe_exit is invoked. */
+extern int sigfpe_code;
+#endif
+
+/* Protect against signals during plural evaluation. Must be called around
+ calls to plural_eval(). Must be called in pairs. */
+extern void install_sigfpe_handler (void);
+extern void uninstall_sigfpe_handler (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _PLURAL_EVAL_H */
diff --git a/gettext-tools/src/plural-exp.c b/gettext-tools/src/plural-exp.c
new file mode 100644
index 0000000..d3f4b5f
--- /dev/null
+++ b/gettext-tools/src/plural-exp.c
@@ -0,0 +1,21 @@
+/* Expression parsing for plural form selection.
+ Copyright (C) 2000-2001, 2003 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+/* Include the expression parsing code from libintl, with different function
+ names. */
+#include "../intl/pluralx.c"
+#include "../../gettext-runtime/intl/plural-exp.c"
diff --git a/gettext-tools/src/plural-table.c b/gettext-tools/src/plural-table.c
new file mode 100644
index 0000000..556cbcc
--- /dev/null
+++ b/gettext-tools/src/plural-table.c
@@ -0,0 +1,67 @@
+/* Table of known plural form expressions.
+ Copyright (C) 2001-2006, 2009-2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "plural-table.h"
+
+/* Formulas taken from the documentation, node "Plural forms". */
+struct plural_table_entry plural_table[] =
+ {
+ { "ja", "Japanese", "nplurals=1; plural=0;" },
+ { "vi", "Vietnamese", "nplurals=1; plural=0;" },
+ { "ko", "Korean", "nplurals=1; plural=0;" },
+ { "en", "English", "nplurals=2; plural=(n != 1);" },
+ { "de", "German", "nplurals=2; plural=(n != 1);" },
+ { "nl", "Dutch", "nplurals=2; plural=(n != 1);" },
+ { "sv", "Swedish", "nplurals=2; plural=(n != 1);" },
+ { "da", "Danish", "nplurals=2; plural=(n != 1);" },
+ { "no", "Norwegian", "nplurals=2; plural=(n != 1);" },
+ { "nb", "Norwegian Bokmal", "nplurals=2; plural=(n != 1);" },
+ { "nn", "Norwegian Nynorsk", "nplurals=2; plural=(n != 1);" },
+ { "fo", "Faroese", "nplurals=2; plural=(n != 1);" },
+ { "es", "Spanish", "nplurals=2; plural=(n != 1);" },
+ { "pt", "Portuguese", "nplurals=2; plural=(n != 1);" },
+ { "it", "Italian", "nplurals=2; plural=(n != 1);" },
+ { "bg", "Bulgarian", "nplurals=2; plural=(n != 1);" },
+ { "el", "Greek", "nplurals=2; plural=(n != 1);" },
+ { "fi", "Finnish", "nplurals=2; plural=(n != 1);" },
+ { "et", "Estonian", "nplurals=2; plural=(n != 1);" },
+ { "he", "Hebrew", "nplurals=2; plural=(n != 1);" },
+ { "eo", "Esperanto", "nplurals=2; plural=(n != 1);" },
+ { "hu", "Hungarian", "nplurals=2; plural=(n != 1);" },
+ { "tr", "Turkish", "nplurals=2; plural=(n != 1);" },
+ { "pt_BR", "Brazilian", "nplurals=2; plural=(n > 1);" },
+ { "fr", "French", "nplurals=2; plural=(n > 1);" },
+ { "lv", "Latvian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
+ { "ga", "Irish", "nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;" },
+ { "ro", "Romanian", "nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;" },
+ { "lt", "Lithuanian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "ru", "Russian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "uk", "Ukrainian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "be", "Belarusian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "sr", "Serbian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "hr", "Croatian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "cs", "Czech", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" },
+ { "sk", "Slovak", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" },
+ { "pl", "Polish", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { "sl", "Slovenian", "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" }
+ };
+const size_t plural_table_size = sizeof (plural_table) / sizeof (plural_table[0]);
diff --git a/gettext-tools/src/plural-table.h b/gettext-tools/src/plural-table.h
new file mode 100644
index 0000000..6af9bf6
--- /dev/null
+++ b/gettext-tools/src/plural-table.h
@@ -0,0 +1,33 @@
+/* Table of known plural form expressions.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PLURAL_TABLE_H
+#define _PLURAL_TABLE_H
+
+#include <stddef.h>
+
+struct plural_table_entry
+{
+ const char *lang;
+ const char *language;
+ const char *value;
+};
+
+extern DLL_VARIABLE struct plural_table_entry plural_table[];
+extern DLL_VARIABLE const size_t plural_table_size;
+
+#endif /* _PLURAL_TABLE_H */
diff --git a/gettext-tools/src/po-charset.c b/gettext-tools/src/po-charset.c
new file mode 100644
index 0000000..4c0dcdb
--- /dev/null
+++ b/gettext-tools/src/po-charset.c
@@ -0,0 +1,662 @@
+/* Charset handling while reading PO files.
+ Copyright (C) 2001-2007, 2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "po-charset.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmalloca.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "basename.h"
+#include "progname.h"
+#include "c-strstr.h"
+#include "c-strcase.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+static const char ascii[] = "ASCII";
+
+/* The canonicalized encoding name for ASCII. */
+const char *po_charset_ascii = ascii;
+
+static const char utf8[] = "UTF-8";
+
+/* The canonicalized encoding name for UTF-8. */
+const char *po_charset_utf8 = utf8;
+
+/* Canonicalize an encoding name. */
+const char *
+po_charset_canonicalize (const char *charset)
+{
+ /* The list of charsets supported by glibc's iconv() and by the portable
+ iconv() across platforms. Taken from intl/config.charset. */
+ static const char *standard_charsets[] =
+ {
+ ascii, "ANSI_X3.4-1968", "US-ASCII", /* i = 0..2 */
+ "ISO-8859-1", "ISO_8859-1", /* i = 3, 4 */
+ "ISO-8859-2", "ISO_8859-2",
+ "ISO-8859-3", "ISO_8859-3",
+ "ISO-8859-4", "ISO_8859-4",
+ "ISO-8859-5", "ISO_8859-5",
+ "ISO-8859-6", "ISO_8859-6",
+ "ISO-8859-7", "ISO_8859-7",
+ "ISO-8859-8", "ISO_8859-8",
+ "ISO-8859-9", "ISO_8859-9",
+ "ISO-8859-13", "ISO_8859-13",
+ "ISO-8859-14", "ISO_8859-14",
+ "ISO-8859-15", "ISO_8859-15", /* i = 25, 26 */
+ "KOI8-R",
+ "KOI8-U",
+ "KOI8-T",
+ "CP850",
+ "CP866",
+ "CP874",
+ "CP932",
+ "CP949",
+ "CP950",
+ "CP1250",
+ "CP1251",
+ "CP1252",
+ "CP1253",
+ "CP1254",
+ "CP1255",
+ "CP1256",
+ "CP1257",
+ "GB2312",
+ "EUC-JP",
+ "EUC-KR",
+ "EUC-TW",
+ "BIG5",
+ "BIG5-HKSCS",
+ "GBK",
+ "GB18030",
+ "SHIFT_JIS",
+ "JOHAB",
+ "TIS-620",
+ "VISCII",
+ "GEORGIAN-PS",
+ utf8
+ };
+ size_t i;
+
+ for (i = 0; i < SIZEOF (standard_charsets); i++)
+ if (c_strcasecmp (charset, standard_charsets[i]) == 0)
+ return standard_charsets[i < 3 ? 0 : i < 27 ? ((i - 3) & ~1) + 3 : i];
+ return NULL;
+}
+
+/* Test for ASCII compatibility. */
+bool
+po_charset_ascii_compatible (const char *canon_charset)
+{
+ /* There are only a few exceptions to ASCII compatibility. */
+ if (strcmp (canon_charset, "SHIFT_JIS") == 0
+ || strcmp (canon_charset, "JOHAB") == 0
+ || strcmp (canon_charset, "VISCII") == 0)
+ return false;
+ else
+ return true;
+}
+
+/* Test for a weird encoding, i.e. an encoding which has double-byte
+ characters ending in 0x5C. */
+bool po_is_charset_weird (const char *canon_charset)
+{
+ static const char *weird_charsets[] =
+ {
+ "BIG5",
+ "BIG5-HKSCS",
+ "GBK",
+ "GB18030",
+ "SHIFT_JIS",
+ "JOHAB"
+ };
+ size_t i;
+
+ for (i = 0; i < SIZEOF (weird_charsets); i++)
+ if (strcmp (canon_charset, weird_charsets[i]) == 0)
+ return true;
+ return false;
+}
+
+/* Test for a weird CJK encoding, i.e. a weird encoding with CJK structure.
+ An encoding has CJK structure if every valid character stream is composed
+ of single bytes in the range 0x{00..7F} and of byte pairs in the range
+ 0x{80..FF}{30..FF}. */
+bool po_is_charset_weird_cjk (const char *canon_charset)
+{
+ static const char *weird_cjk_charsets[] =
+ { /* single bytes double bytes */
+ "BIG5", /* 0x{00..7F}, 0x{A1..F9}{40..FE} */
+ "BIG5-HKSCS", /* 0x{00..7F}, 0x{88..FE}{40..FE} */
+ "GBK", /* 0x{00..7F}, 0x{81..FE}{40..FE} */
+ "GB18030", /* 0x{00..7F}, 0x{81..FE}{30..FE} */
+ "SHIFT_JIS", /* 0x{00..7F}, 0x{81..F9}{40..FC} */
+ "JOHAB" /* 0x{00..7F}, 0x{84..F9}{31..FE} */
+ };
+ size_t i;
+
+ for (i = 0; i < SIZEOF (weird_cjk_charsets); i++)
+ if (strcmp (canon_charset, weird_cjk_charsets[i]) == 0)
+ return true;
+ return false;
+}
+
+/* Hardcoded iterator functions for all kinds of encodings.
+ We could also implement a general iterator function with iconv(),
+ but we need a fast one. */
+
+/* Character iterator for 8-bit encodings. */
+static size_t
+char_iterator (const char *s)
+{
+ return 1;
+}
+
+/* Character iterator for GB2312. See libiconv/lib/euc_cn.h. */
+/* Character iterator for EUC-KR. See libiconv/lib/euc_kr.h. */
+static size_t
+euc_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0xa1 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 < 0xff)
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for EUC-JP. See libiconv/lib/euc_jp.h. */
+static size_t
+euc_jp_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0xa1 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 < 0xff)
+ return 2;
+ }
+ else if (c == 0x8e)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 < 0xe0)
+ return 2;
+ }
+ else if (c == 0x8f)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 < 0xff)
+ {
+ unsigned char c3 = s[2];
+ if (c3 >= 0xa1 && c3 < 0xff)
+ return 3;
+ }
+ }
+ return 1;
+}
+
+/* Character iterator for EUC-TW. See libiconv/lib/euc_tw.h. */
+static size_t
+euc_tw_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0xa1 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 < 0xff)
+ return 2;
+ }
+ else if (c == 0x8e)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0xa1 && c2 <= 0xb0)
+ {
+ unsigned char c3 = s[2];
+ if (c3 >= 0xa1 && c3 < 0xff)
+ {
+ unsigned char c4 = s[3];
+ if (c4 >= 0xa1 && c4 < 0xff)
+ return 4;
+ }
+ }
+ }
+ return 1;
+}
+
+/* Character iterator for BIG5. See libiconv/lib/ces_big5.h. */
+static size_t
+big5_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0xa1 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0xa1 && c2 < 0xff))
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for BIG5-HKSCS. See libiconv/lib/big5hkscs.h. */
+static size_t
+big5hkscs_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0x88 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0xa1 && c2 < 0xff))
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for GBK. See libiconv/lib/ces_gbk.h and
+ libiconv/lib/gbk.h. */
+static size_t
+gbk_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0x81 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xff))
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for GB18030. See libiconv/lib/gb18030.h. */
+static size_t
+gb18030_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0x81 && c < 0xff)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xff))
+ return 2;
+ }
+ if (c >= 0x81 && c <= 0x84)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0x30 && c2 <= 0x39)
+ {
+ unsigned char c3 = s[2];
+ if (c3 >= 0x81 && c3 < 0xff)
+ {
+ unsigned char c4 = s[3];
+ if (c4 >= 0x30 && c4 <= 0x39)
+ return 4;
+ }
+ }
+ }
+ return 1;
+}
+
+/* Character iterator for SHIFT_JIS. See libiconv/lib/sjis.h. */
+static size_t
+shift_jis_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xf9))
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x40 && c2 <= 0x7e) || (c2 >= 0x80 && c2 <= 0xfc))
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for JOHAB. See libiconv/lib/johab.h and
+ libiconv/lib/johab_hangul.h. */
+static size_t
+johab_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0x84 && c <= 0xd3)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x41 && c2 < 0x7f) || (c2 >= 0x81 && c2 < 0xff))
+ return 2;
+ }
+ else if (c >= 0xd9 && c <= 0xf9)
+ {
+ unsigned char c2 = s[1];
+ if ((c2 >= 0x31 && c2 <= 0x7e) || (c2 >= 0x91 && c2 <= 0xfe))
+ return 2;
+ }
+ return 1;
+}
+
+/* Character iterator for UTF-8. See libiconv/lib/utf8.h. */
+static size_t
+utf8_character_iterator (const char *s)
+{
+ unsigned char c = *s;
+ if (c >= 0xc2)
+ {
+ if (c < 0xe0)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0x80 && c2 < 0xc0)
+ return 2;
+ }
+ else if (c < 0xf0)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0x80 && c2 < 0xc0)
+ {
+ unsigned char c3 = s[2];
+ if (c3 >= 0x80 && c3 < 0xc0)
+ return 3;
+ }
+ }
+ else if (c < 0xf8)
+ {
+ unsigned char c2 = s[1];
+ if (c2 >= 0x80 && c2 < 0xc0)
+ {
+ unsigned char c3 = s[2];
+ if (c3 >= 0x80 && c3 < 0xc0)
+ {
+ unsigned char c4 = s[3];
+ if (c4 >= 0x80 && c4 < 0xc0)
+ return 4;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/* Returns a character iterator for a given encoding.
+ Given a pointer into a string, it returns the number occupied by the next
+ single character. If the piece of string is not valid or if the *s == '\0',
+ it returns 1. */
+character_iterator_t
+po_charset_character_iterator (const char *canon_charset)
+{
+ if (canon_charset == utf8)
+ return utf8_character_iterator;
+ if (strcmp (canon_charset, "GB2312") == 0
+ || strcmp (canon_charset, "EUC-KR") == 0)
+ return euc_character_iterator;
+ if (strcmp (canon_charset, "EUC-JP") == 0)
+ return euc_jp_character_iterator;
+ if (strcmp (canon_charset, "EUC-TW") == 0)
+ return euc_tw_character_iterator;
+ if (strcmp (canon_charset, "BIG5") == 0)
+ return big5_character_iterator;
+ if (strcmp (canon_charset, "BIG5-HKSCS") == 0)
+ return big5hkscs_character_iterator;
+ if (strcmp (canon_charset, "GBK") == 0)
+ return gbk_character_iterator;
+ if (strcmp (canon_charset, "GB18030") == 0)
+ return gb18030_character_iterator;
+ if (strcmp (canon_charset, "SHIFT_JIS") == 0)
+ return shift_jis_character_iterator;
+ if (strcmp (canon_charset, "JOHAB") == 0)
+ return johab_character_iterator;
+ return char_iterator;
+}
+
+
+/* The PO file's encoding, as specified in the header entry. */
+const char *po_lex_charset;
+
+#if HAVE_ICONV
+/* Converter from the PO file's encoding to UTF-8. */
+iconv_t po_lex_iconv;
+#endif
+/* If no converter is available, some information about the structure of the
+ PO file's encoding. */
+bool po_lex_weird_cjk;
+
+void
+po_lex_charset_init ()
+{
+ po_lex_charset = NULL;
+#if HAVE_ICONV
+ po_lex_iconv = (iconv_t)(-1);
+#endif
+ po_lex_weird_cjk = false;
+}
+
+void
+po_lex_charset_set (const char *header_entry, const char *filename)
+{
+ /* Verify the validity of CHARSET. It is necessary
+ 1. for the correct treatment of multibyte characters containing
+ 0x5C bytes in the PO lexer,
+ 2. so that at run time, gettext() can call iconv() to convert
+ msgstr. */
+ const char *charsetstr = c_strstr (header_entry, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+ const char *canon_charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = (char *) xmalloca (len + 1);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ canon_charset = po_charset_canonicalize (charset);
+ if (canon_charset == NULL)
+ {
+ /* Don't warn for POT files, because POT files usually contain
+ only ASCII msgids. */
+ size_t filenamelen = strlen (filename);
+
+ if (!(filenamelen >= 4
+ && memcmp (filename + filenamelen - 4, ".pot", 4) == 0
+ && strcmp (charset, "CHARSET") == 0))
+ {
+ char *warning_message =
+ xasprintf (_("\
+Charset \"%s\" is not a portable encoding name.\n\
+Message conversion to user's charset might not work.\n"),
+ charset);
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ filename, (size_t)(-1), (size_t)(-1), true,
+ warning_message);
+ free (warning_message);
+ }
+ }
+ else
+ {
+ const char *envval;
+
+ po_lex_charset = canon_charset;
+#if HAVE_ICONV
+ if (po_lex_iconv != (iconv_t)(-1))
+ iconv_close (po_lex_iconv);
+#endif
+
+ /* The old Solaris/openwin msgfmt and GNU msgfmt <= 0.10.35
+ don't know about multibyte encodings, and require a spurious
+ backslash after every multibyte character whose last byte is
+ 0x5C. Some programs, like vim, distribute PO files in this
+ broken format. GNU msgfmt must continue to support this old
+ PO file format when the Makefile requests it. */
+ envval = getenv ("OLD_PO_FILE_INPUT");
+ if (envval != NULL && *envval != '\0')
+ {
+ /* Assume the PO file is in old format, with extraneous
+ backslashes. */
+#if HAVE_ICONV
+ po_lex_iconv = (iconv_t)(-1);
+#endif
+ po_lex_weird_cjk = false;
+ }
+ else
+ {
+ /* Use iconv() to parse multibyte characters. */
+#if HAVE_ICONV
+ /* Avoid glibc-2.1 bug with EUC-KR. */
+# if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
+ && !defined _LIBICONV_VERSION
+ if (strcmp (po_lex_charset, "EUC-KR") == 0)
+ po_lex_iconv = (iconv_t)(-1);
+ else
+# endif
+ /* Avoid Solaris 2.9 bug with GB2312, EUC-TW, BIG5, BIG5-HKSCS,
+ GBK, GB18030. */
+# if defined __sun && !defined _LIBICONV_VERSION
+ if ( strcmp (po_lex_charset, "GB2312") == 0
+ || strcmp (po_lex_charset, "EUC-TW") == 0
+ || strcmp (po_lex_charset, "BIG5") == 0
+ || strcmp (po_lex_charset, "BIG5-HKSCS") == 0
+ || strcmp (po_lex_charset, "GBK") == 0
+ || strcmp (po_lex_charset, "GB18030") == 0)
+ po_lex_iconv = (iconv_t)(-1);
+ else
+# endif
+ po_lex_iconv = iconv_open ("UTF-8", po_lex_charset);
+ if (po_lex_iconv == (iconv_t)(-1))
+ {
+ char *warning_message;
+ const char *recommendation;
+ const char *note;
+ char *whole_message;
+
+ warning_message =
+ xasprintf (_("\
+Charset \"%s\" is not supported. %s relies on iconv(),\n\
+and iconv() does not support \"%s\".\n"),
+ po_lex_charset, basename (program_name),
+ po_lex_charset);
+
+# if !defined _LIBICONV_VERSION
+ recommendation = _("\
+Installing GNU libiconv and then reinstalling GNU gettext\n\
+would fix this problem.\n");
+# else
+ recommendation = "";
+# endif
+
+ /* Test for a charset which has double-byte characters
+ ending in 0x5C. For these encodings, the string parser
+ is likely to be confused if it can't see the character
+ boundaries. */
+ po_lex_weird_cjk = po_is_charset_weird_cjk (po_lex_charset);
+ if (po_is_charset_weird (po_lex_charset)
+ && !po_lex_weird_cjk)
+ note = _("Continuing anyway, expect parse errors.");
+ else
+ note = _("Continuing anyway.");
+
+ whole_message =
+ xasprintf ("%s%s%s\n",
+ warning_message, recommendation, note);
+
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ filename, (size_t)(-1), (size_t)(-1), true,
+ whole_message);
+
+ free (whole_message);
+ free (warning_message);
+ }
+#else
+ /* Test for a charset which has double-byte characters
+ ending in 0x5C. For these encodings, the string parser
+ is likely to be confused if it can't see the character
+ boundaries. */
+ po_lex_weird_cjk = po_is_charset_weird_cjk (po_lex_charset);
+ if (po_is_charset_weird (po_lex_charset) && !po_lex_weird_cjk)
+ {
+ char *warning_message;
+ const char *recommendation;
+ const char *note;
+ char *whole_message;
+
+ warning_message =
+ xasprintf (_("\
+Charset \"%s\" is not supported. %s relies on iconv().\n\
+This version was built without iconv().\n"),
+ po_lex_charset, basename (program_name));
+
+ recommendation = _("\
+Installing GNU libiconv and then reinstalling GNU gettext\n\
+would fix this problem.\n");
+
+ note = _("Continuing anyway, expect parse errors.");
+
+ whole_message =
+ xasprintf ("%s%s%s\n",
+ warning_message, recommendation, note);
+
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ filename, (size_t)(-1), (size_t)(-1), true,
+ whole_message);
+
+ free (whole_message);
+ free (warning_message);
+ }
+#endif
+ }
+ }
+ freea (charset);
+ }
+ else
+ {
+ /* Don't warn for POT files, because POT files usually contain
+ only ASCII msgids. */
+ size_t filenamelen = strlen (filename);
+
+ if (!(filenamelen >= 4
+ && memcmp (filename + filenamelen - 4, ".pot", 4) == 0))
+ po_xerror (PO_SEVERITY_WARNING,
+ NULL, filename, (size_t)(-1), (size_t)(-1), true,
+ _("\
+Charset missing in header.\n\
+Message conversion to user's charset will not work.\n"));
+ }
+}
+
+void
+po_lex_charset_close ()
+{
+ po_lex_charset = NULL;
+#if HAVE_ICONV
+ if (po_lex_iconv != (iconv_t)(-1))
+ {
+ iconv_close (po_lex_iconv);
+ po_lex_iconv = (iconv_t)(-1);
+ }
+#endif
+ po_lex_weird_cjk = false;
+}
diff --git a/gettext-tools/src/po-charset.h b/gettext-tools/src/po-charset.h
new file mode 100644
index 0000000..65798e7
--- /dev/null
+++ b/gettext-tools/src/po-charset.h
@@ -0,0 +1,93 @@
+/* Charset handling while reading PO files.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_CHARSET_H
+#define _PO_CHARSET_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#if HAVE_ICONV
+#include <iconv.h>
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Canonicalize an encoding name.
+ The results of this function are statically allocated and can be
+ compared using ==. */
+extern const char *po_charset_canonicalize (const char *charset);
+
+/* The canonicalized encoding name for ASCII. */
+extern DLL_VARIABLE const char *po_charset_ascii;
+
+/* The canonicalized encoding name for UTF-8. */
+extern DLL_VARIABLE const char *po_charset_utf8;
+
+/* Test for ASCII compatibility. */
+extern bool po_charset_ascii_compatible (const char *canon_charset);
+
+/* Test for a weird encoding, i.e. an encoding which has double-byte
+ characters ending in 0x5C. */
+extern bool po_is_charset_weird (const char *canon_charset);
+
+/* Test for a weird CJK encoding, i.e. a weird encoding with CJK structure.
+ An encoding has CJK structure if every valid character stream is composed
+ of single bytes in the range 0x{00..7F} and of byte pairs in the range
+ 0x{80..FF}{30..FF}. */
+extern bool po_is_charset_weird_cjk (const char *canon_charset);
+
+/* Returns a character iterator for a given encoding.
+ Given a pointer into a string, it returns the number occupied by the next
+ single character. If the piece of string is not valid or if the *s == '\0',
+ it returns 1. */
+typedef size_t (*character_iterator_t) (const char *s);
+extern character_iterator_t po_charset_character_iterator (const char *canon_charset);
+
+
+/* The PO file's encoding, as specified in the header entry. */
+extern DLL_VARIABLE const char *po_lex_charset;
+
+#if HAVE_ICONV
+/* Converter from the PO file's encoding to UTF-8. */
+extern DLL_VARIABLE iconv_t po_lex_iconv;
+#endif
+/* If no converter is available, some information about the structure of the
+ PO file's encoding. */
+extern DLL_VARIABLE bool po_lex_weird_cjk;
+
+/* Initialize the PO file's encoding. */
+extern void po_lex_charset_init (void);
+
+/* Set the PO file's encoding from the header entry. */
+extern void po_lex_charset_set (const char *header_entry,
+ const char *filename);
+
+/* Finish up with the PO file's encoding. */
+extern void po_lex_charset_close (void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _PO_CHARSET_H */
diff --git a/gettext-tools/src/po-error.c b/gettext-tools/src/po-error.c
new file mode 100644
index 0000000..067264a
--- /dev/null
+++ b/gettext-tools/src/po-error.c
@@ -0,0 +1,42 @@
+/* Error handling during reading and writing of PO files.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2004.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "po-error.h"
+
+#include "error.h"
+#include "xerror.h"
+
+
+void (*po_error) (int status, int errnum,
+ const char *format, ...)
+ = error;
+
+void (*po_error_at_line) (int status, int errnum,
+ const char *filename, unsigned int lineno,
+ const char *format, ...)
+ = error_at_line;
+
+void (*po_multiline_warning) (char *prefix, char *message)
+ = multiline_warning;
+void (*po_multiline_error) (char *prefix, char *message)
+ = multiline_error;
diff --git a/gettext-tools/src/po-error.h b/gettext-tools/src/po-error.h
new file mode 100644
index 0000000..036a69b
--- /dev/null
+++ b/gettext-tools/src/po-error.h
@@ -0,0 +1,76 @@
+/* Error handling during reading and writing of PO files.
+ Copyright (C) 2004, 2006, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2004.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_ERROR_H
+#define _PO_ERROR_H
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# endif
+/* The __-protected variants of 'format' and 'printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __format__ format
+# define __printf__ printf
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Both functions must work like the GNU error(), error_at_line() functions.
+ In particular,
+ - The functions must not return if the status code is nonzero.
+ - The functions must increment the error_message_count variable declared
+ in error.h. */
+
+extern DLL_VARIABLE
+ void (*po_error) (int status, int errnum,
+ const char *format, ...)
+#if (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) || __GNUC__ > 3
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+#endif
+ ;
+extern DLL_VARIABLE
+ void (*po_error_at_line) (int status, int errnum,
+ const char *filename, unsigned int lineno,
+ const char *format, ...)
+#if (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) || __GNUC__ > 3
+ __attribute__ ((__format__ (__printf__, 5, 6)))
+#endif
+ ;
+
+/* Both functions must work like the xerror.h multiline_warning(),
+ multiline_error() functions. In particular,
+ - multiline_error must increment the error_message_count variable declared
+ in error.h if prefix != NULL. */
+
+extern DLL_VARIABLE
+ void (*po_multiline_warning) (char *prefix, char *message);
+extern DLL_VARIABLE
+ void (*po_multiline_error) (char *prefix, char *message);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PO_ERROR_H */
diff --git a/gettext-tools/src/po-gram-gen.c b/gettext-tools/src/po-gram-gen.c
new file mode 100644
index 0000000..655684e
--- /dev/null
+++ b/gettext-tools/src/po-gram-gen.c
@@ -0,0 +1,1922 @@
+/* A Bison parser, made by GNU Bison 3.0.2. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 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 <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 19 "po-gram-gen.y" /* yacc.c:339 */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "po-gram.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "str-list.h"
+#include "po-lex.h"
+#include "po-charset.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+#include "read-catalog-abstract.h"
+
+#define _(str) gettext (str)
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in the same program. Note that these are only
+ the variables produced by yacc. If other parser generators (bison,
+ byacc, etc) produce additional global names that conflict at link time,
+ then those parser generators need to be fixed instead of adding those
+ names to this list. */
+
+#define yymaxdepth po_gram_maxdepth
+#define yyparse po_gram_parse
+#define yylex po_gram_lex
+#define yyerror po_gram_error
+#define yylval po_gram_lval
+#define yychar po_gram_char
+#define yydebug po_gram_debug
+#define yypact po_gram_pact
+#define yyr1 po_gram_r1
+#define yyr2 po_gram_r2
+#define yydef po_gram_def
+#define yychk po_gram_chk
+#define yypgo po_gram_pgo
+#define yyact po_gram_act
+#define yyexca po_gram_exca
+#define yyerrflag po_gram_errflag
+#define yynerrs po_gram_nerrs
+#define yyps po_gram_ps
+#define yypv po_gram_pv
+#define yys po_gram_s
+#define yy_yys po_gram_yys
+#define yystate po_gram_state
+#define yytmp po_gram_tmp
+#define yyv po_gram_v
+#define yy_yyv po_gram_yyv
+#define yyval po_gram_val
+#define yylloc po_gram_lloc
+#define yyreds po_gram_reds /* With YYDEBUG defined */
+#define yytoks po_gram_toks /* With YYDEBUG defined */
+#define yylhs po_gram_yylhs
+#define yylen po_gram_yylen
+#define yydefred po_gram_yydefred
+#define yydgoto po_gram_yydgoto
+#define yysindex po_gram_yysindex
+#define yyrindex po_gram_yyrindex
+#define yygindex po_gram_yygindex
+#define yytable po_gram_yytable
+#define yycheck po_gram_yycheck
+
+static long plural_counter;
+
+#define check_obsolete(value1,value2) \
+ if ((value1).obsolete != (value2).obsolete) \
+ po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~"));
+
+static inline void
+do_callback_message (char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid, char *prev_msgid_plural,
+ bool obsolete)
+{
+ /* Test for header entry. Ignore fuzziness of the header entry. */
+ if (msgctxt == NULL && msgid[0] == '\0' && !obsolete)
+ po_lex_charset_set (msgstr, gram_pos.file_name);
+
+ po_callback_message (msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ false, obsolete);
+}
+
+#define free_message_intro(value) \
+ if ((value).prev_ctxt != NULL) \
+ free ((value).prev_ctxt); \
+ if ((value).prev_id != NULL) \
+ free ((value).prev_id); \
+ if ((value).prev_id_plural != NULL) \
+ free ((value).prev_id_plural); \
+ if ((value).ctxt != NULL) \
+ free ((value).ctxt);
+
+
+#line 173 "po-gram-gen.c" /* yacc.c:339 */
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef YY_YY_PO_GRAM_GEN_H_INCLUDED
+# define YY_YY_PO_GRAM_GEN_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ COMMENT = 258,
+ DOMAIN = 259,
+ JUNK = 260,
+ PREV_MSGCTXT = 261,
+ PREV_MSGID = 262,
+ PREV_MSGID_PLURAL = 263,
+ PREV_STRING = 264,
+ MSGCTXT = 265,
+ MSGID = 266,
+ MSGID_PLURAL = 267,
+ MSGSTR = 268,
+ NAME = 269,
+ NUMBER = 270,
+ STRING = 271
+ };
+#endif
+/* Tokens. */
+#define COMMENT 258
+#define DOMAIN 259
+#define JUNK 260
+#define PREV_MSGCTXT 261
+#define PREV_MSGID 262
+#define PREV_MSGID_PLURAL 263
+#define PREV_STRING 264
+#define MSGCTXT 265
+#define MSGID 266
+#define MSGID_PLURAL 267
+#define MSGSTR 268
+#define NAME 269
+#define NUMBER 270
+#define STRING 271
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 143 "po-gram-gen.y" /* yacc.c:355 */
+
+ struct { char *string; lex_pos_ty pos; bool obsolete; } string;
+ struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
+ struct { long number; lex_pos_ty pos; bool obsolete; } number;
+ struct { lex_pos_ty pos; bool obsolete; } pos;
+ struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
+ struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
+ struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
+
+#line 255 "po-gram-gen.c" /* yacc.c:355 */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (void);
+
+#endif /* !YY_YY_PO_GRAM_GEN_H_INCLUDED */
+
+/* Copy the second part of user declarations. */
+
+#line 270 "po-gram-gen.c" /* yacc.c:358 */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__ \
+ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \
+ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+# define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+# define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 40
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 19
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 15
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 30
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 46
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+ by yylex, with out-of-bounds checking. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 271
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, without out-of-bounds checking. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 15, 2, 16, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 17, 18
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 168, 168, 170, 171, 172, 173, 178, 186, 194,
+ 215, 239, 248, 257, 268, 277, 291, 300, 314, 320,
+ 331, 337, 349, 360, 371, 375, 390, 413, 421, 433,
+ 441
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "COMMENT", "DOMAIN", "JUNK",
+ "PREV_MSGCTXT", "PREV_MSGID", "PREV_MSGID_PLURAL", "PREV_STRING",
+ "MSGCTXT", "MSGID", "MSGID_PLURAL", "MSGSTR", "NAME", "'['", "']'",
+ "NUMBER", "STRING", "$accept", "po_file", "comment", "domain", "message",
+ "message_intro", "prev", "msg_intro", "prev_msg_intro",
+ "msgid_pluralform", "prev_msgid_pluralform", "pluralform_list",
+ "pluralform", "string_list", "prev_string_list", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 91, 93, 270, 271
+};
+# endif
+
+#define YYPACT_NINF -26
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-26)))
+
+#define YYTABLE_NINF -1
+
+#define yytable_value_is_error(Yytable_value) \
+ 0
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int8 yypact[] =
+{
+ -26, 2, -26, -26, -26, -8, 5, -26, 0, -26,
+ -26, -26, -26, 0, 13, -26, 5, -26, -26, 20,
+ -26, -7, 8, -26, 24, -26, -26, -26, -26, 0,
+ 7, 15, 15, -26, 5, -26, 12, 17, 12, 21,
+ 15, -26, 26, 22, 0, 12
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 1, 6, 7, 0, 0, 20, 0, 18,
+ 3, 4, 5, 0, 0, 14, 0, 8, 29, 0,
+ 27, 0, 13, 15, 16, 21, 30, 19, 28, 0,
+ 0, 11, 12, 24, 0, 17, 22, 0, 9, 0,
+ 10, 25, 23, 0, 0, 26
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -26, -26, -26, -26, -26, -26, -26, 23, -26, -26,
+ -26, 9, -25, -13, -15
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 10, 11, 12, 13, 14, 15, 16, 31,
+ 35, 32, 33, 21, 19
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_uint8 yytable[] =
+{
+ 22, 24, 2, 3, 27, 4, 5, 41, 6, 7,
+ 17, 28, 8, 9, 18, 41, 36, 38, 20, 42,
+ 29, 30, 37, 8, 9, 20, 28, 25, 39, 26,
+ 28, 45, 34, 26, 43, 26, 37, 23, 44, 0,
+ 40
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 13, 16, 0, 1, 11, 3, 4, 32, 6, 7,
+ 18, 18, 10, 11, 9, 40, 29, 30, 18, 34,
+ 12, 13, 15, 10, 11, 18, 18, 7, 13, 9,
+ 18, 44, 8, 9, 17, 9, 15, 14, 16, -1,
+ 31
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 20, 0, 1, 3, 4, 6, 7, 10, 11,
+ 21, 22, 23, 24, 25, 26, 27, 18, 9, 33,
+ 18, 32, 32, 26, 33, 7, 9, 11, 18, 12,
+ 13, 28, 30, 31, 8, 29, 32, 15, 32, 13,
+ 30, 31, 33, 17, 16, 32
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 19, 20, 20, 20, 20, 20, 21, 22, 23,
+ 23, 23, 23, 23, 24, 24, 25, 25, 26, 26,
+ 27, 27, 28, 29, 30, 30, 31, 32, 32, 33,
+ 33
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 2, 2, 1, 2, 4,
+ 4, 3, 3, 2, 1, 2, 2, 3, 1, 3,
+ 1, 3, 2, 2, 1, 2, 5, 1, 2, 1,
+ 2
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT. |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+ YYFPRINTF (yyoutput, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+{
+ unsigned long int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[yyssp[yyi + 1 - yynrhs]],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+{
+ YYUSE (yyvaluep);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (void)
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex ();
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 7:
+#line 179 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ po_callback_comment_dispatcher ((yyvsp[0].string).string);
+ }
+#line 1376 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 8:
+#line 187 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ po_callback_domain ((yyvsp[0].string).string);
+ }
+#line 1384 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 9:
+#line 195 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ char *string2 = string_list_concat_destroy (&(yyvsp[-2].stringlist).stringlist);
+ char *string4 = string_list_concat_destroy (&(yyvsp[0].stringlist).stringlist);
+
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[-2].stringlist));
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[-1].pos));
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[0].stringlist));
+ if (!(yyvsp[-3].message_intro).obsolete || pass_obsolete_entries)
+ do_callback_message ((yyvsp[-3].message_intro).ctxt, string2, &(yyvsp[-3].message_intro).pos, NULL,
+ string4, strlen (string4) + 1, &(yyvsp[-1].pos).pos,
+ (yyvsp[-3].message_intro).prev_ctxt,
+ (yyvsp[-3].message_intro).prev_id, (yyvsp[-3].message_intro).prev_id_plural,
+ (yyvsp[-3].message_intro).obsolete);
+ else
+ {
+ free_message_intro ((yyvsp[-3].message_intro));
+ free (string2);
+ free (string4);
+ }
+ }
+#line 1409 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 10:
+#line 216 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ char *string2 = string_list_concat_destroy (&(yyvsp[-2].stringlist).stringlist);
+
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[-2].stringlist));
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[-1].string));
+ check_obsolete ((yyvsp[-3].message_intro), (yyvsp[0].rhs));
+ if (!(yyvsp[-3].message_intro).obsolete || pass_obsolete_entries)
+ {
+ do_callback_message ((yyvsp[-3].message_intro).ctxt, string2, &(yyvsp[-3].message_intro).pos, (yyvsp[-1].string).string,
+ (yyvsp[0].rhs).rhs.msgstr, (yyvsp[0].rhs).rhs.msgstr_len, &(yyvsp[0].rhs).pos,
+ (yyvsp[-3].message_intro).prev_ctxt,
+ (yyvsp[-3].message_intro).prev_id, (yyvsp[-3].message_intro).prev_id_plural,
+ (yyvsp[-3].message_intro).obsolete);
+ free ((yyvsp[-1].string).string);
+ }
+ else
+ {
+ free_message_intro ((yyvsp[-3].message_intro));
+ free (string2);
+ free ((yyvsp[-1].string).string);
+ free ((yyvsp[0].rhs).rhs.msgstr);
+ }
+ }
+#line 1437 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 11:
+#line 240 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-2].message_intro), (yyvsp[-1].stringlist));
+ check_obsolete ((yyvsp[-2].message_intro), (yyvsp[0].string));
+ po_gram_error_at_line (&(yyvsp[-2].message_intro).pos, _("missing 'msgstr[]' section"));
+ free_message_intro ((yyvsp[-2].message_intro));
+ string_list_destroy (&(yyvsp[-1].stringlist).stringlist);
+ free ((yyvsp[0].string).string);
+ }
+#line 1450 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 12:
+#line 249 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-2].message_intro), (yyvsp[-1].stringlist));
+ check_obsolete ((yyvsp[-2].message_intro), (yyvsp[0].rhs));
+ po_gram_error_at_line (&(yyvsp[-2].message_intro).pos, _("missing 'msgid_plural' section"));
+ free_message_intro ((yyvsp[-2].message_intro));
+ string_list_destroy (&(yyvsp[-1].stringlist).stringlist);
+ free ((yyvsp[0].rhs).rhs.msgstr);
+ }
+#line 1463 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 13:
+#line 258 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].message_intro), (yyvsp[0].stringlist));
+ po_gram_error_at_line (&(yyvsp[-1].message_intro).pos, _("missing 'msgstr' section"));
+ free_message_intro ((yyvsp[-1].message_intro));
+ string_list_destroy (&(yyvsp[0].stringlist).stringlist);
+ }
+#line 1474 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 14:
+#line 269 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ (yyval.message_intro).prev_ctxt = NULL;
+ (yyval.message_intro).prev_id = NULL;
+ (yyval.message_intro).prev_id_plural = NULL;
+ (yyval.message_intro).ctxt = (yyvsp[0].string).string;
+ (yyval.message_intro).pos = (yyvsp[0].string).pos;
+ (yyval.message_intro).obsolete = (yyvsp[0].string).obsolete;
+ }
+#line 1487 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 15:
+#line 278 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].prev), (yyvsp[0].string));
+ (yyval.message_intro).prev_ctxt = (yyvsp[-1].prev).ctxt;
+ (yyval.message_intro).prev_id = (yyvsp[-1].prev).id;
+ (yyval.message_intro).prev_id_plural = (yyvsp[-1].prev).id_plural;
+ (yyval.message_intro).ctxt = (yyvsp[0].string).string;
+ (yyval.message_intro).pos = (yyvsp[0].string).pos;
+ (yyval.message_intro).obsolete = (yyvsp[0].string).obsolete;
+ }
+#line 1501 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 16:
+#line 292 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].string), (yyvsp[0].stringlist));
+ (yyval.prev).ctxt = (yyvsp[-1].string).string;
+ (yyval.prev).id = string_list_concat_destroy (&(yyvsp[0].stringlist).stringlist);
+ (yyval.prev).id_plural = NULL;
+ (yyval.prev).pos = (yyvsp[-1].string).pos;
+ (yyval.prev).obsolete = (yyvsp[-1].string).obsolete;
+ }
+#line 1514 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 17:
+#line 301 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-2].string), (yyvsp[-1].stringlist));
+ check_obsolete ((yyvsp[-2].string), (yyvsp[0].string));
+ (yyval.prev).ctxt = (yyvsp[-2].string).string;
+ (yyval.prev).id = string_list_concat_destroy (&(yyvsp[-1].stringlist).stringlist);
+ (yyval.prev).id_plural = (yyvsp[0].string).string;
+ (yyval.prev).pos = (yyvsp[-2].string).pos;
+ (yyval.prev).obsolete = (yyvsp[-2].string).obsolete;
+ }
+#line 1528 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 18:
+#line 315 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ (yyval.string).string = NULL;
+ (yyval.string).pos = (yyvsp[0].pos).pos;
+ (yyval.string).obsolete = (yyvsp[0].pos).obsolete;
+ }
+#line 1538 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 19:
+#line 321 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-2].pos), (yyvsp[-1].stringlist));
+ check_obsolete ((yyvsp[-2].pos), (yyvsp[0].pos));
+ (yyval.string).string = string_list_concat_destroy (&(yyvsp[-1].stringlist).stringlist);
+ (yyval.string).pos = (yyvsp[0].pos).pos;
+ (yyval.string).obsolete = (yyvsp[0].pos).obsolete;
+ }
+#line 1550 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 20:
+#line 332 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ (yyval.string).string = NULL;
+ (yyval.string).pos = (yyvsp[0].pos).pos;
+ (yyval.string).obsolete = (yyvsp[0].pos).obsolete;
+ }
+#line 1560 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 21:
+#line 338 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-2].pos), (yyvsp[-1].stringlist));
+ check_obsolete ((yyvsp[-2].pos), (yyvsp[0].pos));
+ (yyval.string).string = string_list_concat_destroy (&(yyvsp[-1].stringlist).stringlist);
+ (yyval.string).pos = (yyvsp[0].pos).pos;
+ (yyval.string).obsolete = (yyvsp[0].pos).obsolete;
+ }
+#line 1572 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 22:
+#line 350 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].pos), (yyvsp[0].stringlist));
+ plural_counter = 0;
+ (yyval.string).string = string_list_concat_destroy (&(yyvsp[0].stringlist).stringlist);
+ (yyval.string).pos = (yyvsp[-1].pos).pos;
+ (yyval.string).obsolete = (yyvsp[-1].pos).obsolete;
+ }
+#line 1584 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 23:
+#line 361 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].pos), (yyvsp[0].stringlist));
+ (yyval.string).string = string_list_concat_destroy (&(yyvsp[0].stringlist).stringlist);
+ (yyval.string).pos = (yyvsp[-1].pos).pos;
+ (yyval.string).obsolete = (yyvsp[-1].pos).obsolete;
+ }
+#line 1595 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 24:
+#line 372 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ (yyval.rhs) = (yyvsp[0].rhs);
+ }
+#line 1603 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 25:
+#line 376 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].rhs), (yyvsp[0].rhs));
+ (yyval.rhs).rhs.msgstr = XNMALLOC ((yyvsp[-1].rhs).rhs.msgstr_len + (yyvsp[0].rhs).rhs.msgstr_len, char);
+ memcpy ((yyval.rhs).rhs.msgstr, (yyvsp[-1].rhs).rhs.msgstr, (yyvsp[-1].rhs).rhs.msgstr_len);
+ memcpy ((yyval.rhs).rhs.msgstr + (yyvsp[-1].rhs).rhs.msgstr_len, (yyvsp[0].rhs).rhs.msgstr, (yyvsp[0].rhs).rhs.msgstr_len);
+ (yyval.rhs).rhs.msgstr_len = (yyvsp[-1].rhs).rhs.msgstr_len + (yyvsp[0].rhs).rhs.msgstr_len;
+ free ((yyvsp[-1].rhs).rhs.msgstr);
+ free ((yyvsp[0].rhs).rhs.msgstr);
+ (yyval.rhs).pos = (yyvsp[-1].rhs).pos;
+ (yyval.rhs).obsolete = (yyvsp[-1].rhs).obsolete;
+ }
+#line 1619 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 26:
+#line 391 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-4].pos), (yyvsp[-3].pos));
+ check_obsolete ((yyvsp[-4].pos), (yyvsp[-2].number));
+ check_obsolete ((yyvsp[-4].pos), (yyvsp[-1].pos));
+ check_obsolete ((yyvsp[-4].pos), (yyvsp[0].stringlist));
+ if ((yyvsp[-2].number).number != plural_counter)
+ {
+ if (plural_counter == 0)
+ po_gram_error_at_line (&(yyvsp[-4].pos).pos, _("first plural form has nonzero index"));
+ else
+ po_gram_error_at_line (&(yyvsp[-4].pos).pos, _("plural form has wrong index"));
+ }
+ plural_counter++;
+ (yyval.rhs).rhs.msgstr = string_list_concat_destroy (&(yyvsp[0].stringlist).stringlist);
+ (yyval.rhs).rhs.msgstr_len = strlen ((yyval.rhs).rhs.msgstr) + 1;
+ (yyval.rhs).pos = (yyvsp[-4].pos).pos;
+ (yyval.rhs).obsolete = (yyvsp[-4].pos).obsolete;
+ }
+#line 1642 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 27:
+#line 414 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ string_list_init (&(yyval.stringlist).stringlist);
+ string_list_append (&(yyval.stringlist).stringlist, (yyvsp[0].string).string);
+ free ((yyvsp[0].string).string);
+ (yyval.stringlist).pos = (yyvsp[0].string).pos;
+ (yyval.stringlist).obsolete = (yyvsp[0].string).obsolete;
+ }
+#line 1654 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 28:
+#line 422 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].stringlist), (yyvsp[0].string));
+ (yyval.stringlist).stringlist = (yyvsp[-1].stringlist).stringlist;
+ string_list_append (&(yyval.stringlist).stringlist, (yyvsp[0].string).string);
+ free ((yyvsp[0].string).string);
+ (yyval.stringlist).pos = (yyvsp[-1].stringlist).pos;
+ (yyval.stringlist).obsolete = (yyvsp[-1].stringlist).obsolete;
+ }
+#line 1667 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 29:
+#line 434 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ string_list_init (&(yyval.stringlist).stringlist);
+ string_list_append (&(yyval.stringlist).stringlist, (yyvsp[0].string).string);
+ free ((yyvsp[0].string).string);
+ (yyval.stringlist).pos = (yyvsp[0].string).pos;
+ (yyval.stringlist).obsolete = (yyvsp[0].string).obsolete;
+ }
+#line 1679 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+ case 30:
+#line 442 "po-gram-gen.y" /* yacc.c:1646 */
+ {
+ check_obsolete ((yyvsp[-1].stringlist), (yyvsp[0].string));
+ (yyval.stringlist).stringlist = (yyvsp[-1].stringlist).stringlist;
+ string_list_append (&(yyval.stringlist).stringlist, (yyvsp[0].string).string);
+ free ((yyvsp[0].string).string);
+ (yyval.stringlist).pos = (yyvsp[-1].stringlist).pos;
+ (yyval.stringlist).obsolete = (yyvsp[-1].stringlist).obsolete;
+ }
+#line 1692 "po-gram-gen.c" /* yacc.c:1646 */
+ break;
+
+
+#line 1696 "po-gram-gen.c" /* yacc.c:1646 */
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
diff --git a/gettext-tools/src/po-gram-gen.h b/gettext-tools/src/po-gram-gen.h
new file mode 100644
index 0000000..3f20e75
--- /dev/null
+++ b/gettext-tools/src/po-gram-gen.h
@@ -0,0 +1,106 @@
+/* A Bison parser, made by GNU Bison 3.0.2. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 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 <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_PO_GRAM_GEN_H_INCLUDED
+# define YY_YY_PO_GRAM_GEN_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ COMMENT = 258,
+ DOMAIN = 259,
+ JUNK = 260,
+ PREV_MSGCTXT = 261,
+ PREV_MSGID = 262,
+ PREV_MSGID_PLURAL = 263,
+ PREV_STRING = 264,
+ MSGCTXT = 265,
+ MSGID = 266,
+ MSGID_PLURAL = 267,
+ MSGSTR = 268,
+ NAME = 269,
+ NUMBER = 270,
+ STRING = 271
+ };
+#endif
+/* Tokens. */
+#define COMMENT 258
+#define DOMAIN 259
+#define JUNK 260
+#define PREV_MSGCTXT 261
+#define PREV_MSGID 262
+#define PREV_MSGID_PLURAL 263
+#define PREV_STRING 264
+#define MSGCTXT 265
+#define MSGID 266
+#define MSGID_PLURAL 267
+#define MSGSTR 268
+#define NAME 269
+#define NUMBER 270
+#define STRING 271
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 143 "po-gram-gen.y" /* yacc.c:1909 */
+
+ struct { char *string; lex_pos_ty pos; bool obsolete; } string;
+ struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
+ struct { long number; lex_pos_ty pos; bool obsolete; } number;
+ struct { lex_pos_ty pos; bool obsolete; } pos;
+ struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
+ struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
+ struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
+
+#line 96 "po-gram-gen.h" /* yacc.c:1909 */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (void);
+
+#endif /* !YY_YY_PO_GRAM_GEN_H_INCLUDED */
diff --git a/gettext-tools/src/po-gram-gen.y b/gettext-tools/src/po-gram-gen.y
new file mode 100644
index 0000000..8f888bd
--- /dev/null
+++ b/gettext-tools/src/po-gram-gen.y
@@ -0,0 +1,450 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1996, 1998, 2000-2001, 2003, 2005-2006, 2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <pmiller@agso.gov.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+%{
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "po-gram.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "str-list.h"
+#include "po-lex.h"
+#include "po-charset.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+#include "read-catalog-abstract.h"
+
+#define _(str) gettext (str)
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in the same program. Note that these are only
+ the variables produced by yacc. If other parser generators (bison,
+ byacc, etc) produce additional global names that conflict at link time,
+ then those parser generators need to be fixed instead of adding those
+ names to this list. */
+
+#define yymaxdepth po_gram_maxdepth
+#define yyparse po_gram_parse
+#define yylex po_gram_lex
+#define yyerror po_gram_error
+#define yylval po_gram_lval
+#define yychar po_gram_char
+#define yydebug po_gram_debug
+#define yypact po_gram_pact
+#define yyr1 po_gram_r1
+#define yyr2 po_gram_r2
+#define yydef po_gram_def
+#define yychk po_gram_chk
+#define yypgo po_gram_pgo
+#define yyact po_gram_act
+#define yyexca po_gram_exca
+#define yyerrflag po_gram_errflag
+#define yynerrs po_gram_nerrs
+#define yyps po_gram_ps
+#define yypv po_gram_pv
+#define yys po_gram_s
+#define yy_yys po_gram_yys
+#define yystate po_gram_state
+#define yytmp po_gram_tmp
+#define yyv po_gram_v
+#define yy_yyv po_gram_yyv
+#define yyval po_gram_val
+#define yylloc po_gram_lloc
+#define yyreds po_gram_reds /* With YYDEBUG defined */
+#define yytoks po_gram_toks /* With YYDEBUG defined */
+#define yylhs po_gram_yylhs
+#define yylen po_gram_yylen
+#define yydefred po_gram_yydefred
+#define yydgoto po_gram_yydgoto
+#define yysindex po_gram_yysindex
+#define yyrindex po_gram_yyrindex
+#define yygindex po_gram_yygindex
+#define yytable po_gram_yytable
+#define yycheck po_gram_yycheck
+
+static long plural_counter;
+
+#define check_obsolete(value1,value2) \
+ if ((value1).obsolete != (value2).obsolete) \
+ po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~"));
+
+static inline void
+do_callback_message (char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid, char *prev_msgid_plural,
+ bool obsolete)
+{
+ /* Test for header entry. Ignore fuzziness of the header entry. */
+ if (msgctxt == NULL && msgid[0] == '\0' && !obsolete)
+ po_lex_charset_set (msgstr, gram_pos.file_name);
+
+ po_callback_message (msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ false, obsolete);
+}
+
+#define free_message_intro(value) \
+ if ((value).prev_ctxt != NULL) \
+ free ((value).prev_ctxt); \
+ if ((value).prev_id != NULL) \
+ free ((value).prev_id); \
+ if ((value).prev_id_plural != NULL) \
+ free ((value).prev_id_plural); \
+ if ((value).ctxt != NULL) \
+ free ((value).ctxt);
+
+%}
+
+%token COMMENT
+%token DOMAIN
+%token JUNK
+%token PREV_MSGCTXT
+%token PREV_MSGID
+%token PREV_MSGID_PLURAL
+%token PREV_STRING
+%token MSGCTXT
+%token MSGID
+%token MSGID_PLURAL
+%token MSGSTR
+%token NAME
+%token '[' ']'
+%token NUMBER
+%token STRING
+
+%union
+{
+ struct { char *string; lex_pos_ty pos; bool obsolete; } string;
+ struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
+ struct { long number; lex_pos_ty pos; bool obsolete; } number;
+ struct { lex_pos_ty pos; bool obsolete; } pos;
+ struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
+ struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
+ struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
+}
+
+%type <string> STRING PREV_STRING COMMENT NAME
+ msg_intro prev_msg_intro msgid_pluralform prev_msgid_pluralform
+%type <stringlist> string_list prev_string_list
+%type <number> NUMBER
+%type <pos> DOMAIN
+ PREV_MSGCTXT PREV_MSGID PREV_MSGID_PLURAL
+ MSGCTXT MSGID MSGID_PLURAL MSGSTR '[' ']'
+%type <prev> prev
+%type <message_intro> message_intro
+%type <rhs> pluralform pluralform_list
+
+%right MSGSTR
+
+%%
+
+po_file
+ : /* empty */
+ | po_file comment
+ | po_file domain
+ | po_file message
+ | po_file error
+ ;
+
+
+comment
+ : COMMENT
+ {
+ po_callback_comment_dispatcher ($1.string);
+ }
+ ;
+
+
+domain
+ : DOMAIN STRING
+ {
+ po_callback_domain ($2.string);
+ }
+ ;
+
+
+message
+ : message_intro string_list MSGSTR string_list
+ {
+ char *string2 = string_list_concat_destroy (&$2.stringlist);
+ char *string4 = string_list_concat_destroy (&$4.stringlist);
+
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ check_obsolete ($1, $4);
+ if (!$1.obsolete || pass_obsolete_entries)
+ do_callback_message ($1.ctxt, string2, &$1.pos, NULL,
+ string4, strlen (string4) + 1, &$3.pos,
+ $1.prev_ctxt,
+ $1.prev_id, $1.prev_id_plural,
+ $1.obsolete);
+ else
+ {
+ free_message_intro ($1);
+ free (string2);
+ free (string4);
+ }
+ }
+ | message_intro string_list msgid_pluralform pluralform_list
+ {
+ char *string2 = string_list_concat_destroy (&$2.stringlist);
+
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ check_obsolete ($1, $4);
+ if (!$1.obsolete || pass_obsolete_entries)
+ {
+ do_callback_message ($1.ctxt, string2, &$1.pos, $3.string,
+ $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos,
+ $1.prev_ctxt,
+ $1.prev_id, $1.prev_id_plural,
+ $1.obsolete);
+ free ($3.string);
+ }
+ else
+ {
+ free_message_intro ($1);
+ free (string2);
+ free ($3.string);
+ free ($4.rhs.msgstr);
+ }
+ }
+ | message_intro string_list msgid_pluralform
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ po_gram_error_at_line (&$1.pos, _("missing 'msgstr[]' section"));
+ free_message_intro ($1);
+ string_list_destroy (&$2.stringlist);
+ free ($3.string);
+ }
+ | message_intro string_list pluralform_list
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ po_gram_error_at_line (&$1.pos, _("missing 'msgid_plural' section"));
+ free_message_intro ($1);
+ string_list_destroy (&$2.stringlist);
+ free ($3.rhs.msgstr);
+ }
+ | message_intro string_list
+ {
+ check_obsolete ($1, $2);
+ po_gram_error_at_line (&$1.pos, _("missing 'msgstr' section"));
+ free_message_intro ($1);
+ string_list_destroy (&$2.stringlist);
+ }
+ ;
+
+
+message_intro
+ : msg_intro
+ {
+ $$.prev_ctxt = NULL;
+ $$.prev_id = NULL;
+ $$.prev_id_plural = NULL;
+ $$.ctxt = $1.string;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | prev msg_intro
+ {
+ check_obsolete ($1, $2);
+ $$.prev_ctxt = $1.ctxt;
+ $$.prev_id = $1.id;
+ $$.prev_id_plural = $1.id_plural;
+ $$.ctxt = $2.string;
+ $$.pos = $2.pos;
+ $$.obsolete = $2.obsolete;
+ }
+ ;
+
+
+prev
+ : prev_msg_intro prev_string_list
+ {
+ check_obsolete ($1, $2);
+ $$.ctxt = $1.string;
+ $$.id = string_list_concat_destroy (&$2.stringlist);
+ $$.id_plural = NULL;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | prev_msg_intro prev_string_list prev_msgid_pluralform
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ $$.ctxt = $1.string;
+ $$.id = string_list_concat_destroy (&$2.stringlist);
+ $$.id_plural = $3.string;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+
+msg_intro
+ : MSGID
+ {
+ $$.string = NULL;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | MSGCTXT string_list MSGID
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ $$.string = string_list_concat_destroy (&$2.stringlist);
+ $$.pos = $3.pos;
+ $$.obsolete = $3.obsolete;
+ }
+ ;
+
+prev_msg_intro
+ : PREV_MSGID
+ {
+ $$.string = NULL;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | PREV_MSGCTXT prev_string_list PREV_MSGID
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ $$.string = string_list_concat_destroy (&$2.stringlist);
+ $$.pos = $3.pos;
+ $$.obsolete = $3.obsolete;
+ }
+ ;
+
+
+msgid_pluralform
+ : MSGID_PLURAL string_list
+ {
+ check_obsolete ($1, $2);
+ plural_counter = 0;
+ $$.string = string_list_concat_destroy (&$2.stringlist);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+prev_msgid_pluralform
+ : PREV_MSGID_PLURAL prev_string_list
+ {
+ check_obsolete ($1, $2);
+ $$.string = string_list_concat_destroy (&$2.stringlist);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+
+pluralform_list
+ : pluralform
+ {
+ $$ = $1;
+ }
+ | pluralform_list pluralform
+ {
+ check_obsolete ($1, $2);
+ $$.rhs.msgstr = XNMALLOC ($1.rhs.msgstr_len + $2.rhs.msgstr_len, char);
+ memcpy ($$.rhs.msgstr, $1.rhs.msgstr, $1.rhs.msgstr_len);
+ memcpy ($$.rhs.msgstr + $1.rhs.msgstr_len, $2.rhs.msgstr, $2.rhs.msgstr_len);
+ $$.rhs.msgstr_len = $1.rhs.msgstr_len + $2.rhs.msgstr_len;
+ free ($1.rhs.msgstr);
+ free ($2.rhs.msgstr);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+pluralform
+ : MSGSTR '[' NUMBER ']' string_list
+ {
+ check_obsolete ($1, $2);
+ check_obsolete ($1, $3);
+ check_obsolete ($1, $4);
+ check_obsolete ($1, $5);
+ if ($3.number != plural_counter)
+ {
+ if (plural_counter == 0)
+ po_gram_error_at_line (&$1.pos, _("first plural form has nonzero index"));
+ else
+ po_gram_error_at_line (&$1.pos, _("plural form has wrong index"));
+ }
+ plural_counter++;
+ $$.rhs.msgstr = string_list_concat_destroy (&$5.stringlist);
+ $$.rhs.msgstr_len = strlen ($$.rhs.msgstr) + 1;
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+
+string_list
+ : STRING
+ {
+ string_list_init (&$$.stringlist);
+ string_list_append (&$$.stringlist, $1.string);
+ free ($1.string);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | string_list STRING
+ {
+ check_obsolete ($1, $2);
+ $$.stringlist = $1.stringlist;
+ string_list_append (&$$.stringlist, $2.string);
+ free ($2.string);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
+
+prev_string_list
+ : PREV_STRING
+ {
+ string_list_init (&$$.stringlist);
+ string_list_append (&$$.stringlist, $1.string);
+ free ($1.string);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ | prev_string_list PREV_STRING
+ {
+ check_obsolete ($1, $2);
+ $$.stringlist = $1.stringlist;
+ string_list_append (&$$.stringlist, $2.string);
+ free ($2.string);
+ $$.pos = $1.pos;
+ $$.obsolete = $1.obsolete;
+ }
+ ;
diff --git a/gettext-tools/src/po-gram-gen2.h b/gettext-tools/src/po-gram-gen2.h
new file mode 100644
index 0000000..36e9f4b
--- /dev/null
+++ b/gettext-tools/src/po-gram-gen2.h
@@ -0,0 +1,106 @@
+/* A Bison parser, made by GNU Bison 3.0.2. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 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 <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_PO_GRAM_GEN_H_INCLUDED
+# define YY_YY_PO_GRAM_GEN_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern DLL_VARIABLE int po_gram_debug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum po_gram_tokentype
+ {
+ COMMENT = 258,
+ DOMAIN = 259,
+ JUNK = 260,
+ PREV_MSGCTXT = 261,
+ PREV_MSGID = 262,
+ PREV_MSGID_PLURAL = 263,
+ PREV_STRING = 264,
+ MSGCTXT = 265,
+ MSGID = 266,
+ MSGID_PLURAL = 267,
+ MSGSTR = 268,
+ NAME = 269,
+ NUMBER = 270,
+ STRING = 271
+ };
+#endif
+/* Tokens. */
+#define COMMENT 258
+#define DOMAIN 259
+#define JUNK 260
+#define PREV_MSGCTXT 261
+#define PREV_MSGID 262
+#define PREV_MSGID_PLURAL 263
+#define PREV_STRING 264
+#define MSGCTXT 265
+#define MSGID 266
+#define MSGID_PLURAL 267
+#define MSGSTR 268
+#define NAME 269
+#define NUMBER 270
+#define STRING 271
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 143 "po-gram-gen.y" /* yacc.c:1909 */
+
+ struct { char *string; lex_pos_ty pos; bool obsolete; } string;
+ struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
+ struct { long number; lex_pos_ty pos; bool obsolete; } number;
+ struct { lex_pos_ty pos; bool obsolete; } pos;
+ struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
+ struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
+ struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
+
+#line 96 "po-gram-gen.h" /* yacc.c:1909 */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern DLL_VARIABLE YYSTYPE po_gram_lval;
+
+int po_gram_parse (void);
+
+#endif /* !YY_YY_PO_GRAM_GEN_H_INCLUDED */
diff --git a/gettext-tools/src/po-gram.h b/gettext-tools/src/po-gram.h
new file mode 100644
index 0000000..0e2d079
--- /dev/null
+++ b/gettext-tools/src/po-gram.h
@@ -0,0 +1,32 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995, 2002-2003, 2006 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_GRAM_H
+#define _PO_GRAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int po_gram_parse (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gettext-tools/src/po-lex.c b/gettext-tools/src/po-lex.c
new file mode 100644
index 0000000..3bf5449
--- /dev/null
+++ b/gettext-tools/src/po-lex.c
@@ -0,0 +1,1151 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1999, 2000-2009, 2011 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>.
+ Multibyte character handling by Bruno Haible <haible@clisp.cons.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "po-lex.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#if HAVE_ICONV
+# include <iconv.h>
+#endif
+
+#include "c-ctype.h"
+#include "uniwidth.h"
+#include "gettext.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xvasprintf.h"
+#include "po-error.h"
+#include "po-xerror.h"
+#include "pos.h"
+#include "message.h"
+#include "str-list.h"
+#include "po-gram-gen2.h"
+
+#define _(str) gettext(str)
+
+#if HAVE_ICONV
+# include "unistr.h"
+#endif
+
+#if HAVE_DECL_GETC_UNLOCKED
+# undef getc
+# define getc getc_unlocked
+#endif
+
+
+/* Current position within the PO file. */
+lex_pos_ty gram_pos;
+int gram_pos_column;
+
+
+/* Error handling during the parsing of a PO file.
+ These functions can access gram_pos and gram_pos_column. */
+
+/* VARARGS1 */
+void
+po_gram_error (const char *fmt, ...)
+{
+ va_list ap;
+ char *buffer;
+
+ va_start (ap, fmt);
+ if (vasprintf (&buffer, fmt, ap) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ va_end (ap);
+ po_xerror (PO_SEVERITY_ERROR, NULL, gram_pos.file_name, gram_pos.line_number,
+ gram_pos_column + 1, false, buffer);
+ free (buffer);
+
+ if (error_message_count >= gram_max_allowed_errors)
+ po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
+}
+
+/* VARARGS2 */
+void
+po_gram_error_at_line (const lex_pos_ty *pp, const char *fmt, ...)
+{
+ va_list ap;
+ char *buffer;
+
+ va_start (ap, fmt);
+ if (vasprintf (&buffer, fmt, ap) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ va_end (ap);
+ po_xerror (PO_SEVERITY_ERROR, NULL, pp->file_name, pp->line_number,
+ (size_t)(-1), false, buffer);
+ free (buffer);
+
+ if (error_message_count >= gram_max_allowed_errors)
+ po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
+}
+
+
+/* The lowest level of PO file parsing converts bytes to multibyte characters.
+ This is needed
+ 1. for C compatibility: ISO C 99 section 5.1.1.2 says that the first
+ translation phase maps bytes to characters.
+ 2. to keep track of the current column, for the sake of precise error
+ location. Emacs compile.el interprets the column in error messages
+ by default as a screen column number, not as character number.
+ 3. to avoid skipping backslash-newline in the midst of a multibyte
+ character. If XY is a multibyte character, X \ newline Y is invalid.
+ */
+
+/* Multibyte character data type. */
+/* Note this depends on po_lex_charset and po_lex_iconv, which get set
+ while the file is being parsed. */
+
+#define MBCHAR_BUF_SIZE 24
+
+struct mbchar
+{
+ size_t bytes; /* number of bytes of current character, > 0 */
+#if HAVE_ICONV
+ bool uc_valid; /* true if uc is a valid Unicode character */
+ ucs4_t uc; /* if uc_valid: the current character */
+#endif
+ char buf[MBCHAR_BUF_SIZE]; /* room for the bytes */
+};
+
+/* We want to pass multibyte characters by reference automatically,
+ therefore we use an array type. */
+typedef struct mbchar mbchar_t[1];
+
+/* A version of memcpy optimized for the case n <= 1. */
+static inline void
+memcpy_small (void *dst, const void *src, size_t n)
+{
+ if (n > 0)
+ {
+ char *q = (char *) dst;
+ const char *p = (const char *) src;
+
+ *q = *p;
+ if (--n > 0)
+ do *++q = *++p; while (--n > 0);
+ }
+}
+
+/* EOF (not a real character) is represented with bytes = 0 and
+ uc_valid = false. */
+static inline bool
+mb_iseof (const mbchar_t mbc)
+{
+ return (mbc->bytes == 0);
+}
+
+/* Access the current character. */
+static inline const char *
+mb_ptr (const mbchar_t mbc)
+{
+ return mbc->buf;
+}
+static inline size_t
+mb_len (const mbchar_t mbc)
+{
+ return mbc->bytes;
+}
+
+/* Comparison of characters. */
+
+static inline bool
+mb_iseq (const mbchar_t mbc, char sc)
+{
+ /* Note: It is wrong to compare only mbc->uc, because when the encoding is
+ SHIFT_JIS, mbc->buf[0] == '\\' corresponds to mbc->uc == 0x00A5, but we
+ want to treat it as an escape character, although it looks like a Yen
+ sign. */
+#if HAVE_ICONV && 0
+ if (mbc->uc_valid)
+ return (mbc->uc == sc); /* wrong! */
+ else
+#endif
+ return (mbc->bytes == 1 && mbc->buf[0] == sc);
+}
+
+static inline bool
+mb_isnul (const mbchar_t mbc)
+{
+#if HAVE_ICONV
+ if (mbc->uc_valid)
+ return (mbc->uc == 0);
+ else
+#endif
+ return (mbc->bytes == 1 && mbc->buf[0] == 0);
+}
+
+static inline int
+mb_cmp (const mbchar_t mbc1, const mbchar_t mbc2)
+{
+#if HAVE_ICONV
+ if (mbc1->uc_valid && mbc2->uc_valid)
+ return (int) mbc1->uc - (int) mbc2->uc;
+ else
+#endif
+ return (mbc1->bytes == mbc2->bytes
+ ? memcmp (mbc1->buf, mbc2->buf, mbc1->bytes)
+ : mbc1->bytes < mbc2->bytes
+ ? (memcmp (mbc1->buf, mbc2->buf, mbc1->bytes) > 0 ? 1 : -1)
+ : (memcmp (mbc1->buf, mbc2->buf, mbc2->bytes) >= 0 ? 1 : -1));
+}
+
+static inline bool
+mb_equal (const mbchar_t mbc1, const mbchar_t mbc2)
+{
+#if HAVE_ICONV
+ if (mbc1->uc_valid && mbc2->uc_valid)
+ return mbc1->uc == mbc2->uc;
+ else
+#endif
+ return (mbc1->bytes == mbc2->bytes
+ && memcmp (mbc1->buf, mbc2->buf, mbc1->bytes) == 0);
+}
+
+/* <ctype.h>, <wctype.h> classification. */
+
+static inline bool
+mb_isascii (const mbchar_t mbc)
+{
+#if HAVE_ICONV
+ if (mbc->uc_valid)
+ return (mbc->uc >= 0x0000 && mbc->uc <= 0x007F);
+ else
+#endif
+ return (mbc->bytes == 1
+#if CHAR_MIN < 0x00 /* to avoid gcc warning */
+ && mbc->buf[0] >= 0x00
+#endif
+#if CHAR_MAX > 0x7F /* to avoid gcc warning */
+ && mbc->buf[0] <= 0x7F
+#endif
+ );
+}
+
+/* Extra <wchar.h> function. */
+
+/* Unprintable characters appear as a small box of width 1. */
+#define MB_UNPRINTABLE_WIDTH 1
+
+static int
+mb_width (const mbchar_t mbc)
+{
+#if HAVE_ICONV
+ if (mbc->uc_valid)
+ {
+ ucs4_t uc = mbc->uc;
+ const char *encoding =
+ (po_lex_iconv != (iconv_t)(-1) ? po_lex_charset : "");
+ int w = uc_width (uc, encoding);
+ /* For unprintable characters, arbitrarily return 0 for control
+ characters (except tab) and MB_UNPRINTABLE_WIDTH otherwise. */
+ if (w >= 0)
+ return w;
+ if (uc >= 0x0000 && uc <= 0x001F)
+ {
+ if (uc == 0x0009)
+ return 8 - (gram_pos_column & 7);
+ return 0;
+ }
+ if ((uc >= 0x007F && uc <= 0x009F) || (uc >= 0x2028 && uc <= 0x2029))
+ return 0;
+ return MB_UNPRINTABLE_WIDTH;
+ }
+ else
+#endif
+ {
+ if (mbc->bytes == 1)
+ {
+ if (
+#if CHAR_MIN < 0x00 /* to avoid gcc warning */
+ mbc->buf[0] >= 0x00 &&
+#endif
+ mbc->buf[0] <= 0x1F)
+ {
+ if (mbc->buf[0] == 0x09)
+ return 8 - (gram_pos_column & 7);
+ return 0;
+ }
+ if (mbc->buf[0] == 0x7F)
+ return 0;
+ }
+ return MB_UNPRINTABLE_WIDTH;
+ }
+}
+
+/* Output. */
+static inline void
+mb_putc (const mbchar_t mbc, FILE *stream)
+{
+ fwrite (mbc->buf, 1, mbc->bytes, stream);
+}
+
+/* Assignment. */
+static inline void
+mb_setascii (mbchar_t mbc, char sc)
+{
+ mbc->bytes = 1;
+#if HAVE_ICONV
+ mbc->uc_valid = 1;
+ mbc->uc = sc;
+#endif
+ mbc->buf[0] = sc;
+}
+
+/* Copying a character. */
+static inline void
+mb_copy (mbchar_t new_mbc, const mbchar_t old_mbc)
+{
+ memcpy_small (&new_mbc->buf[0], &old_mbc->buf[0], old_mbc->bytes);
+ new_mbc->bytes = old_mbc->bytes;
+#if HAVE_ICONV
+ if ((new_mbc->uc_valid = old_mbc->uc_valid))
+ new_mbc->uc = old_mbc->uc;
+#endif
+}
+
+
+/* Multibyte character input. */
+
+/* Number of characters that can be pushed back.
+ We need 1 for lex_getc, plus 1 for lex_ungetc. */
+#define NPUSHBACK 2
+
+/* Data type of a multibyte character input stream. */
+struct mbfile
+{
+ FILE *fp;
+ bool eof_seen;
+ int have_pushback;
+ unsigned int bufcount;
+ char buf[MBCHAR_BUF_SIZE];
+ struct mbchar pushback[NPUSHBACK];
+};
+
+/* We want to pass multibyte streams by reference automatically,
+ therefore we use an array type. */
+typedef struct mbfile mbfile_t[1];
+
+/* Whether invalid multibyte sequences in the input shall be signalled
+ or silently tolerated. */
+static bool signal_eilseq;
+
+static inline void
+mbfile_init (mbfile_t mbf, FILE *stream)
+{
+ mbf->fp = stream;
+ mbf->eof_seen = false;
+ mbf->have_pushback = 0;
+ mbf->bufcount = 0;
+}
+
+/* Read the next multibyte character from mbf and put it into mbc.
+ If a read error occurs, errno is set and ferror (mbf->fp) becomes true. */
+static void
+mbfile_getc (mbchar_t mbc, mbfile_t mbf)
+{
+ size_t bytes;
+
+ /* If EOF has already been seen, don't use getc. This matters if
+ mbf->fp is connected to an interactive tty. */
+ if (mbf->eof_seen)
+ goto eof;
+
+ /* Return character pushed back, if there is one. */
+ if (mbf->have_pushback > 0)
+ {
+ mbf->have_pushback--;
+ mb_copy (mbc, &mbf->pushback[mbf->have_pushback]);
+ return;
+ }
+
+ /* Before using iconv, we need at least one byte. */
+ if (mbf->bufcount == 0)
+ {
+ int c = getc (mbf->fp);
+ if (c == EOF)
+ {
+ mbf->eof_seen = true;
+ goto eof;
+ }
+ mbf->buf[0] = (unsigned char) c;
+ mbf->bufcount++;
+ }
+
+#if HAVE_ICONV
+ if (po_lex_iconv != (iconv_t)(-1))
+ {
+ /* Use iconv on an increasing number of bytes. Read only as many
+ bytes from mbf->fp as needed. This is needed to give reasonable
+ interactive behaviour when mbf->fp is connected to an interactive
+ tty. */
+ for (;;)
+ {
+ unsigned char scratchbuf[64];
+ const char *inptr = &mbf->buf[0];
+ size_t insize = mbf->bufcount;
+ char *outptr = (char *) &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+
+ size_t res = iconv (po_lex_iconv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ /* We expect that a character has been produced if and only if
+ some input bytes have been consumed. */
+ if ((insize < mbf->bufcount) != (outsize < sizeof (scratchbuf)))
+ abort ();
+ if (outsize == sizeof (scratchbuf))
+ {
+ /* No character has been produced. Must be an error. */
+ if (res != (size_t)(-1))
+ abort ();
+
+ if (errno == EILSEQ)
+ {
+ /* An invalid multibyte sequence was encountered. */
+ /* Return a single byte. */
+ if (signal_eilseq)
+ po_gram_error (_("invalid multibyte sequence"));
+ bytes = 1;
+ mbc->uc_valid = false;
+ break;
+ }
+ else if (errno == EINVAL)
+ {
+ /* An incomplete multibyte character. */
+ int c;
+
+ if (mbf->bufcount == MBCHAR_BUF_SIZE)
+ {
+ /* An overlong incomplete multibyte sequence was
+ encountered. */
+ /* Return a single byte. */
+ bytes = 1;
+ mbc->uc_valid = false;
+ break;
+ }
+
+ /* Read one more byte and retry iconv. */
+ c = getc (mbf->fp);
+ if (c == EOF)
+ {
+ mbf->eof_seen = true;
+ if (ferror (mbf->fp))
+ goto eof;
+ if (signal_eilseq)
+ po_gram_error (_("\
+incomplete multibyte sequence at end of file"));
+ bytes = mbf->bufcount;
+ mbc->uc_valid = false;
+ break;
+ }
+ mbf->buf[mbf->bufcount++] = (unsigned char) c;
+ if (c == '\n')
+ {
+ if (signal_eilseq)
+ po_gram_error (_("\
+incomplete multibyte sequence at end of line"));
+ bytes = mbf->bufcount - 1;
+ mbc->uc_valid = false;
+ break;
+ }
+ }
+ else
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ _("iconv failure"),
+ errno_description));
+ }
+ }
+ else
+ {
+ size_t outbytes = sizeof (scratchbuf) - outsize;
+ bytes = mbf->bufcount - insize;
+
+ /* We expect that one character has been produced. */
+ if (bytes == 0)
+ abort ();
+ if (outbytes == 0)
+ abort ();
+ /* Convert it from UTF-8 to UCS-4. */
+ if (u8_mbtoucr (&mbc->uc, scratchbuf, outbytes) < (int) outbytes)
+ {
+ /* scratchbuf contains an out-of-range Unicode character
+ (> 0x10ffff). */
+ if (signal_eilseq)
+ po_gram_error (_("invalid multibyte sequence"));
+ mbc->uc_valid = false;
+ break;
+ }
+ mbc->uc_valid = true;
+ break;
+ }
+ }
+ }
+ else
+#endif
+ {
+ if (po_lex_weird_cjk
+ /* Special handling of encodings with CJK structure. */
+ && (unsigned char) mbf->buf[0] >= 0x80)
+ {
+ if (mbf->bufcount == 1)
+ {
+ /* Read one more byte. */
+ int c = getc (mbf->fp);
+ if (c == EOF)
+ {
+ if (ferror (mbf->fp))
+ {
+ mbf->eof_seen = true;
+ goto eof;
+ }
+ }
+ else
+ {
+ mbf->buf[1] = (unsigned char) c;
+ mbf->bufcount++;
+ }
+ }
+ if (mbf->bufcount >= 2 && (unsigned char) mbf->buf[1] >= 0x30)
+ /* Return a double byte. */
+ bytes = 2;
+ else
+ /* Return a single byte. */
+ bytes = 1;
+ }
+ else
+ {
+ /* Return a single byte. */
+ bytes = 1;
+ }
+#if HAVE_ICONV
+ mbc->uc_valid = false;
+#endif
+ }
+
+ /* Return the multibyte sequence mbf->buf[0..bytes-1]. */
+ memcpy_small (&mbc->buf[0], &mbf->buf[0], bytes);
+ mbc->bytes = bytes;
+
+ mbf->bufcount -= bytes;
+ if (mbf->bufcount > 0)
+ {
+ /* It's not worth calling memmove() for so few bytes. */
+ unsigned int count = mbf->bufcount;
+ char *p = &mbf->buf[0];
+
+ do
+ {
+ *p = *(p + bytes);
+ p++;
+ }
+ while (--count > 0);
+ }
+ return;
+
+eof:
+ /* An mbchar_t with bytes == 0 is used to indicate EOF. */
+ mbc->bytes = 0;
+#if HAVE_ICONV
+ mbc->uc_valid = false;
+#endif
+ return;
+}
+
+static void
+mbfile_ungetc (const mbchar_t mbc, mbfile_t mbf)
+{
+ if (mbf->have_pushback >= NPUSHBACK)
+ abort ();
+ mb_copy (&mbf->pushback[mbf->have_pushback], mbc);
+ mbf->have_pushback++;
+}
+
+
+/* Lexer variables. */
+
+static mbfile_t mbf;
+unsigned int gram_max_allowed_errors = 20;
+static bool po_lex_obsolete;
+static bool po_lex_previous;
+static bool pass_comments = false;
+bool pass_obsolete_entries = false;
+
+
+/* Prepare lexical analysis. */
+void
+lex_start (FILE *fp, const char *real_filename, const char *logical_filename)
+{
+ /* Ignore the logical_filename, because PO file entries already have
+ their file names attached. But use real_filename for error messages. */
+ gram_pos.file_name = xstrdup (real_filename);
+
+ mbfile_init (mbf, fp);
+
+ gram_pos.line_number = 1;
+ gram_pos_column = 0;
+ signal_eilseq = true;
+ po_lex_obsolete = false;
+ po_lex_previous = false;
+ po_lex_charset_init ();
+}
+
+/* Terminate lexical analysis. */
+void
+lex_end ()
+{
+ mbf->fp = NULL;
+ gram_pos.file_name = NULL;
+ gram_pos.line_number = 0;
+ gram_pos_column = 0;
+ signal_eilseq = false;
+ po_lex_obsolete = false;
+ po_lex_previous = false;
+ po_lex_charset_close ();
+}
+
+
+/* Read a single character, dealing with backslash-newline.
+ Also keep track of the current line number and column number. */
+static void
+lex_getc (mbchar_t mbc)
+{
+ for (;;)
+ {
+ mbfile_getc (mbc, mbf);
+
+ if (mb_iseof (mbc))
+ {
+ if (ferror (mbf->fp))
+ bomb:
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while reading \"%s\""),
+ gram_pos.file_name),
+ errno_description));
+ }
+ break;
+ }
+
+ if (mb_iseq (mbc, '\n'))
+ {
+ gram_pos.line_number++;
+ gram_pos_column = 0;
+ break;
+ }
+
+ gram_pos_column += mb_width (mbc);
+
+ if (mb_iseq (mbc, '\\'))
+ {
+ mbchar_t mbc2;
+
+ mbfile_getc (mbc2, mbf);
+
+ if (mb_iseof (mbc2))
+ {
+ if (ferror (mbf->fp))
+ goto bomb;
+ break;
+ }
+
+ if (!mb_iseq (mbc2, '\n'))
+ {
+ mbfile_ungetc (mbc2, mbf);
+ break;
+ }
+
+ gram_pos.line_number++;
+ gram_pos_column = 0;
+ }
+ else
+ break;
+ }
+}
+
+
+static void
+lex_ungetc (const mbchar_t mbc)
+{
+ if (!mb_iseof (mbc))
+ {
+ if (mb_iseq (mbc, '\n'))
+ /* Decrement the line number, but don't care about the column. */
+ gram_pos.line_number--;
+ else
+ /* Decrement the column number. Also works well enough for tabs. */
+ gram_pos_column -= mb_width (mbc);
+
+ mbfile_ungetc (mbc, mbf);
+ }
+}
+
+
+static int
+keyword_p (const char *s)
+{
+ if (!po_lex_previous)
+ {
+ if (!strcmp (s, "domain"))
+ return DOMAIN;
+ if (!strcmp (s, "msgid"))
+ return MSGID;
+ if (!strcmp (s, "msgid_plural"))
+ return MSGID_PLURAL;
+ if (!strcmp (s, "msgstr"))
+ return MSGSTR;
+ if (!strcmp (s, "msgctxt"))
+ return MSGCTXT;
+ }
+ else
+ {
+ /* Inside a "#|" context, the keywords have a different meaning. */
+ if (!strcmp (s, "msgid"))
+ return PREV_MSGID;
+ if (!strcmp (s, "msgid_plural"))
+ return PREV_MSGID_PLURAL;
+ if (!strcmp (s, "msgctxt"))
+ return PREV_MSGCTXT;
+ }
+ po_gram_error_at_line (&gram_pos, _("keyword \"%s\" unknown"), s);
+ return NAME;
+}
+
+
+static int
+control_sequence ()
+{
+ mbchar_t mbc;
+ int val;
+ int max;
+
+ lex_getc (mbc);
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ case 'n':
+ return '\n';
+
+ case 't':
+ return '\t';
+
+ case 'b':
+ return '\b';
+
+ case 'r':
+ return '\r';
+
+ case 'f':
+ return '\f';
+
+ case 'v':
+ return '\v';
+
+ case 'a':
+ return '\a';
+
+ case '\\':
+ case '"':
+ return mb_ptr (mbc) [0];
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ val = 0;
+ max = 0;
+ for (;;)
+ {
+ char c = mb_ptr (mbc) [0];
+ /* Warning: not portable, can't depend on '0'..'7' ordering. */
+ val = val * 8 + (c - '0');
+ if (++max == 3)
+ break;
+ lex_getc (mbc);
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ continue;
+
+ default:
+ break;
+ }
+ lex_ungetc (mbc);
+ break;
+ }
+ return val;
+
+ case 'x':
+ lex_getc (mbc);
+ if (mb_iseof (mbc) || mb_len (mbc) != 1
+ || !c_isxdigit (mb_ptr (mbc) [0]))
+ break;
+
+ val = 0;
+ for (;;)
+ {
+ char c = mb_ptr (mbc) [0];
+ val *= 16;
+ if (c_isdigit (c))
+ /* Warning: not portable, can't depend on '0'..'9' ordering */
+ val += c - '0';
+ else if (c_isupper (c))
+ /* Warning: not portable, can't depend on 'A'..'F' ordering */
+ val += c - 'A' + 10;
+ else
+ /* Warning: not portable, can't depend on 'a'..'f' ordering */
+ val += c - 'a' + 10;
+
+ lex_getc (mbc);
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ continue;
+
+ default:
+ break;
+ }
+ lex_ungetc (mbc);
+ break;
+ }
+ return val;
+
+ /* FIXME: \u and \U are not handled. */
+ }
+ lex_ungetc (mbc);
+ po_gram_error (_("invalid control sequence"));
+ return ' ';
+}
+
+
+/* Return the next token in the PO file. The return codes are defined
+ in "po-gram-gen2.h". Associated data is put in 'po_gram_lval'. */
+int
+po_gram_lex ()
+{
+ static char *buf;
+ static size_t bufmax;
+ mbchar_t mbc;
+ size_t bufpos;
+
+ for (;;)
+ {
+ lex_getc (mbc);
+
+ if (mb_iseof (mbc))
+ /* Yacc want this for end of file. */
+ return 0;
+
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ case '\n':
+ po_lex_obsolete = false;
+ po_lex_previous = false;
+ /* Ignore whitespace, not relevant for the grammar. */
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\f':
+ case '\v':
+ /* Ignore whitespace, not relevant for the grammar. */
+ break;
+
+ case '#':
+ lex_getc (mbc);
+ if (mb_iseq (mbc, '~'))
+ /* A pseudo-comment beginning with #~ is found. This is
+ not a comment. It is the format for obsolete entries.
+ We simply discard the "#~" prefix. The following
+ characters are expected to be well formed. */
+ {
+ po_lex_obsolete = true;
+ /* A pseudo-comment beginning with #~| denotes a previous
+ untranslated string in an obsolete entry. This does not
+ make much sense semantically, and is implemented here
+ for completeness only. */
+ lex_getc (mbc);
+ if (mb_iseq (mbc, '|'))
+ po_lex_previous = true;
+ else
+ lex_ungetc (mbc);
+ break;
+ }
+ if (mb_iseq (mbc, '|'))
+ /* A pseudo-comment beginning with #| is found. This is
+ the previous untranslated string. We discard the "#|"
+ prefix, but change the keywords and string returns
+ accordingly. */
+ {
+ po_lex_previous = true;
+ break;
+ }
+
+ /* Accumulate comments into a buffer. If we have been asked
+ to pass comments, generate a COMMENT token, otherwise
+ discard it. */
+ signal_eilseq = false;
+ if (pass_comments)
+ {
+ bufpos = 0;
+ for (;;)
+ {
+ while (bufpos + mb_len (mbc) >= bufmax)
+ {
+ bufmax += 100;
+ buf = xrealloc (buf, bufmax);
+ }
+ if (mb_iseof (mbc) || mb_iseq (mbc, '\n'))
+ break;
+
+ memcpy_small (&buf[bufpos], mb_ptr (mbc), mb_len (mbc));
+ bufpos += mb_len (mbc);
+
+ lex_getc (mbc);
+ }
+ buf[bufpos] = '\0';
+
+ po_gram_lval.string.string = buf;
+ po_gram_lval.string.pos = gram_pos;
+ po_gram_lval.string.obsolete = po_lex_obsolete;
+ po_lex_obsolete = false;
+ signal_eilseq = true;
+ return COMMENT;
+ }
+ else
+ {
+ /* We do this in separate loop because collecting large
+ comments while they get not passed to the upper layers
+ is not very efficient. */
+ while (!mb_iseof (mbc) && !mb_iseq (mbc, '\n'))
+ lex_getc (mbc);
+ po_lex_obsolete = false;
+ signal_eilseq = true;
+ }
+ break;
+
+ case '"':
+ /* Accumulate a string. */
+ bufpos = 0;
+ for (;;)
+ {
+ lex_getc (mbc);
+ while (bufpos + mb_len (mbc) >= bufmax)
+ {
+ bufmax += 100;
+ buf = xrealloc (buf, bufmax);
+ }
+ if (mb_iseof (mbc))
+ {
+ po_gram_error_at_line (&gram_pos,
+ _("end-of-file within string"));
+ break;
+ }
+ if (mb_iseq (mbc, '\n'))
+ {
+ po_gram_error_at_line (&gram_pos,
+ _("end-of-line within string"));
+ break;
+ }
+ if (mb_iseq (mbc, '"'))
+ break;
+ if (mb_iseq (mbc, '\\'))
+ {
+ buf[bufpos++] = control_sequence ();
+ continue;
+ }
+
+ /* Add mbc to the accumulator. */
+ memcpy_small (&buf[bufpos], mb_ptr (mbc), mb_len (mbc));
+ bufpos += mb_len (mbc);
+ }
+ buf[bufpos] = '\0';
+
+ /* Strings cannot contain the msgctxt separator, because it cannot
+ be faithfully represented in the msgid of a .mo file. */
+ if (strchr (buf, MSGCTXT_SEPARATOR) != NULL)
+ po_gram_error_at_line (&gram_pos,
+ _("context separator <EOT> within string"));
+
+ /* FIXME: Treatment of embedded \000 chars is incorrect. */
+ po_gram_lval.string.string = xstrdup (buf);
+ po_gram_lval.string.pos = gram_pos;
+ po_gram_lval.string.obsolete = po_lex_obsolete;
+ return (po_lex_previous ? PREV_STRING : STRING);
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_': case '$':
+ bufpos = 0;
+ for (;;)
+ {
+ char c = mb_ptr (mbc) [0];
+ if (bufpos + 1 >= bufmax)
+ {
+ bufmax += 100;
+ buf = xrealloc (buf, bufmax);
+ }
+ buf[bufpos++] = c;
+ lex_getc (mbc);
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ default:
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '_': case '$':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ }
+ break;
+ }
+ lex_ungetc (mbc);
+
+ buf[bufpos] = '\0';
+
+ {
+ int k = keyword_p (buf);
+ if (k == NAME)
+ {
+ po_gram_lval.string.string = xstrdup (buf);
+ po_gram_lval.string.pos = gram_pos;
+ po_gram_lval.string.obsolete = po_lex_obsolete;
+ }
+ else
+ {
+ po_gram_lval.pos.pos = gram_pos;
+ po_gram_lval.pos.obsolete = po_lex_obsolete;
+ }
+ return k;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ bufpos = 0;
+ for (;;)
+ {
+ char c = mb_ptr (mbc) [0];
+ if (bufpos + 1 >= bufmax)
+ {
+ bufmax += 100;
+ buf = xrealloc (buf, bufmax + 1);
+ }
+ buf[bufpos++] = c;
+ lex_getc (mbc);
+ if (mb_len (mbc) == 1)
+ switch (mb_ptr (mbc) [0])
+ {
+ default:
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ }
+ break;
+ }
+ lex_ungetc (mbc);
+
+ buf[bufpos] = '\0';
+
+ po_gram_lval.number.number = atol (buf);
+ po_gram_lval.number.pos = gram_pos;
+ po_gram_lval.number.obsolete = po_lex_obsolete;
+ return NUMBER;
+
+ case '[':
+ po_gram_lval.pos.pos = gram_pos;
+ po_gram_lval.pos.obsolete = po_lex_obsolete;
+ return '[';
+
+ case ']':
+ po_gram_lval.pos.pos = gram_pos;
+ po_gram_lval.pos.obsolete = po_lex_obsolete;
+ return ']';
+
+ default:
+ /* This will cause a syntax error. */
+ return JUNK;
+ }
+ else
+ /* This will cause a syntax error. */
+ return JUNK;
+ }
+}
+
+
+/* po_gram_lex() can return comments as COMMENT. Switch this on or off. */
+void
+po_lex_pass_comments (bool flag)
+{
+ pass_comments = flag;
+}
+
+
+/* po_gram_lex() can return obsolete entries as if they were normal entries.
+ Switch this on or off. */
+void
+po_lex_pass_obsolete_entries (bool flag)
+{
+ pass_obsolete_entries = flag;
+}
diff --git a/gettext-tools/src/po-lex.h b/gettext-tools/src/po-lex.h
new file mode 100644
index 0000000..da05f38
--- /dev/null
+++ b/gettext-tools/src/po-lex.h
@@ -0,0 +1,103 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2006, 2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_LEX_H
+#define _PO_LEX_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "error.h"
+#include "error-progname.h"
+#include "xerror.h"
+#include "pos.h"
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# endif
+/* The __-protected variants of 'format' and 'printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __format__ format
+# define __printf__ printf
+# endif
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Lexical analyzer for reading PO files. */
+
+
+/* Global variables from po-lex.c. */
+
+/* Current position within the PO file. */
+extern DLL_VARIABLE lex_pos_ty gram_pos;
+extern DLL_VARIABLE int gram_pos_column;
+
+/* Number of parse errors within a PO file that cause the program to
+ terminate. Cf. error_message_count, declared in <error.h>. */
+extern DLL_VARIABLE unsigned int gram_max_allowed_errors;
+
+/* True if obsolete entries shall be considered as valid. */
+extern DLL_VARIABLE bool pass_obsolete_entries;
+
+
+/* Prepare lexical analysis. */
+extern void lex_start (FILE *fp, const char *real_filename,
+ const char *logical_filename);
+
+/* Terminate lexical analysis. */
+extern void lex_end (void);
+
+/* Return the next token in the PO file. The return codes are defined
+ in "po-gram-gen2.h". Associated data is put in 'po_gram_lval. */
+extern int po_gram_lex (void);
+
+/* po_gram_lex() can return comments as COMMENT. Switch this on or off. */
+extern void po_lex_pass_comments (bool flag);
+
+/* po_gram_lex() can return obsolete entries as if they were normal entries.
+ Switch this on or off. */
+extern void po_lex_pass_obsolete_entries (bool flag);
+
+extern void po_gram_error (const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+extern void po_gram_error_at_line (const lex_pos_ty *pos, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+
+
+/* Contains information about the definition of one translation. */
+struct msgstr_def
+{
+ char *msgstr;
+ size_t msgstr_len;
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/gettext-tools/src/po-time.c b/gettext-tools/src/po-time.c
new file mode 100644
index 0000000..dd357f1
--- /dev/null
+++ b/gettext-tools/src/po-time.c
@@ -0,0 +1,75 @@
+/* PO/POT file timestamps.
+ Copyright (C) 1995-1998, 2000-2003, 2006 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "po-time.h"
+
+#include "xvasprintf.h"
+
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static long
+difftm (const struct tm *a, const struct tm *b)
+{
+ int ay = a->tm_year + TM_YEAR_ORIGIN - 1;
+ int by = b->tm_year + TM_YEAR_ORIGIN - 1;
+ /* Some compilers cannot handle this as a single return statement. */
+ long days = (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay / 100 - by / 100)
+ + ((ay / 100 >> 2) - (by / 100 >> 2))
+ /* + difference in years * 365 */
+ + (long) (ay - by) * 365l);
+
+ return 60l * (60l * (24l * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec);
+}
+
+
+char *
+po_strftime (const time_t *tp)
+{
+ struct tm local_time;
+ char tz_sign;
+ long tz_min;
+
+ local_time = *localtime (tp);
+ tz_sign = '+';
+ tz_min = difftm (&local_time, gmtime (tp)) / 60;
+ if (tz_min < 0)
+ {
+ tz_min = -tz_min;
+ tz_sign = '-';
+ }
+ return xasprintf ("%d-%02d-%02d %02d:%02d%c%02ld%02ld",
+ local_time.tm_year + TM_YEAR_ORIGIN,
+ local_time.tm_mon + 1,
+ local_time.tm_mday,
+ local_time.tm_hour,
+ local_time.tm_min,
+ tz_sign, tz_min / 60, tz_min % 60);
+}
diff --git a/gettext-tools/src/po-time.h b/gettext-tools/src/po-time.h
new file mode 100644
index 0000000..846dea0
--- /dev/null
+++ b/gettext-tools/src/po-time.h
@@ -0,0 +1,38 @@
+/* PO/POT file timestamps.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_TIME_H
+#define _PO_TIME_H
+
+#include <time.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Return a freshly allocated string containing the given time in the
+ format YYYY-MM-DD HH:MM+TZOFF. */
+extern char *po_strftime (const time_t *tp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PO_TIME_H */
diff --git a/gettext-tools/src/po-xerror.c b/gettext-tools/src/po-xerror.c
new file mode 100644
index 0000000..8a889d8
--- /dev/null
+++ b/gettext-tools/src/po-xerror.c
@@ -0,0 +1,199 @@
+/* Error handling during reading and writing of PO files.
+ Copyright (C) 2005-2007 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "po-xerror.h"
+
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "progname.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xerror.h"
+#include "error.h"
+#include "xvasprintf.h"
+#include "po-error.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+static void
+xerror (int severity, const char *prefix_tail,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+{
+ if (multiline_p)
+ {
+ bool old_error_with_progname = error_with_progname;
+ char *prefix;
+
+ if (filename != NULL)
+ {
+ if (lineno != (size_t)(-1))
+ {
+ if (column != (size_t)(-1))
+ prefix =
+ xasprintf ("%s:%ld:%ld: %s", filename,
+ (long) lineno, (long) column, prefix_tail);
+ else
+ prefix =
+ xasprintf ("%s:%ld: %s", filename,
+ (long) lineno, prefix_tail);
+ }
+ else
+ prefix = xasprintf ("%s: %s", filename, prefix_tail);
+ error_with_progname = false;
+ }
+ else
+ prefix = xasprintf ("%s: %s", program_name, prefix_tail);
+
+ if (severity >= PO_SEVERITY_ERROR)
+ po_multiline_error (prefix, xstrdup (message_text));
+ else
+ po_multiline_warning (prefix, xstrdup (message_text));
+ error_with_progname = old_error_with_progname;
+
+ if (severity == PO_SEVERITY_FATAL_ERROR)
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ int exit_status =
+ (severity == PO_SEVERITY_FATAL_ERROR ? EXIT_FAILURE : 0);
+
+ if (filename != NULL)
+ {
+ error_with_progname = false;
+ if (lineno != (size_t)(-1))
+ {
+ if (column != (size_t)(-1))
+ po_error (exit_status, 0, "%s:%ld:%ld: %s%s",
+ filename, (long) lineno, (long) column,
+ prefix_tail, message_text);
+ else
+ po_error_at_line (exit_status, 0, filename, lineno, "%s%s",
+ prefix_tail, message_text);
+ }
+ else
+ po_error (exit_status, 0, "%s: %s%s",
+ filename, prefix_tail, message_text);
+ error_with_progname = true;
+ }
+ else
+ po_error (exit_status, 0, "%s%s", prefix_tail, message_text);
+ if (severity < PO_SEVERITY_ERROR)
+ --error_message_count;
+ }
+}
+
+/* The default error handler is based on the lower-level error handler
+ in po-error.h, so that gettext-po.h can offer to override one or the
+ other. */
+void
+textmode_xerror (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+{
+ const char *prefix_tail =
+ (severity == PO_SEVERITY_WARNING ? _("warning: ") : "");
+
+ if (message != NULL && (filename == NULL || lineno == (size_t)(-1)))
+ {
+ filename = message->pos.file_name;
+ lineno = message->pos.line_number;
+ column = (size_t)(-1);
+ }
+
+ xerror (severity, prefix_tail, filename, lineno, column,
+ multiline_p, message_text);
+}
+
+void
+textmode_xerror2 (int severity,
+ const struct message_ty *message1,
+ const char *filename1, size_t lineno1, size_t column1,
+ int multiline_p1, const char *message_text1,
+ const struct message_ty *message2,
+ const char *filename2, size_t lineno2, size_t column2,
+ int multiline_p2, const char *message_text2)
+{
+ int severity1 = /* Don't exit before both texts have been output. */
+ (severity == PO_SEVERITY_FATAL_ERROR ? PO_SEVERITY_ERROR : severity);
+ const char *prefix_tail =
+ (severity == PO_SEVERITY_WARNING ? _("warning: ") : "");
+
+ if (message1 != NULL && (filename1 == NULL || lineno1 == (size_t)(-1)))
+ {
+ filename1 = message1->pos.file_name;
+ lineno1 = message1->pos.line_number;
+ column1 = (size_t)(-1);
+ }
+
+ if (message2 != NULL && (filename2 == NULL || lineno2 == (size_t)(-1)))
+ {
+ filename2 = message2->pos.file_name;
+ lineno2 = message2->pos.line_number;
+ column2 = (size_t)(-1);
+ }
+
+ if (multiline_p1)
+ xerror (severity1, prefix_tail, filename1, lineno1, column1, multiline_p1,
+ message_text1);
+ else
+ {
+ char *message_text1_extended = xasprintf ("%s...", message_text1);
+ xerror (severity1, prefix_tail, filename1, lineno1, column1,
+ multiline_p1, message_text1_extended);
+ free (message_text1_extended);
+ }
+
+ {
+ char *message_text2_extended = xasprintf ("...%s", message_text2);
+ xerror (severity, prefix_tail, filename2, lineno2, column2,
+ multiline_p2, message_text2_extended);
+ free (message_text2_extended);
+ }
+
+ if (severity >= PO_SEVERITY_ERROR)
+ /* error_message_count needs to be incremented only by 1, not by 2. */
+ --error_message_count;
+}
+
+void (*po_xerror) (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text)
+ = textmode_xerror;
+
+void (*po_xerror2) (int severity,
+ const struct message_ty *message1,
+ const char *filename1, size_t lineno1, size_t column1,
+ int multiline_p1, const char *message_text1,
+ const struct message_ty *message2,
+ const char *filename2, size_t lineno2, size_t column2,
+ int multiline_p2, const char *message_text2)
+ = textmode_xerror2;
diff --git a/gettext-tools/src/po-xerror.h b/gettext-tools/src/po-xerror.h
new file mode 100644
index 0000000..521ec3f
--- /dev/null
+++ b/gettext-tools/src/po-xerror.h
@@ -0,0 +1,82 @@
+/* Error handling during reading and writing of PO files.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _PO_XERROR_H
+#define _PO_XERROR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+struct message_ty;
+
+
+/* A higher-level error handler than the one in po-error.h. */
+
+/* These values must be the same as those in gettext-po.h. */
+#define PO_SEVERITY_WARNING 0 /* just a warning, tell the user */
+#define PO_SEVERITY_ERROR 1 /* an error, the operation cannot complete */
+#define PO_SEVERITY_FATAL_ERROR 2 /* an error, the operation must be aborted */
+
+/* Signal a problem of the given severity.
+ MESSAGE and/or FILENAME + LINENO indicate where the problem occurred.
+ If FILENAME is NULL, FILENAME and LINENO and COLUMN should be ignored.
+ If LINENO is (size_t)(-1), LINENO and COLUMN should be ignored.
+ If COLUMN is (size_t)(-1), it should be ignored.
+ MESSAGE_TEXT is the problem description (if MULTILINE_P is true,
+ multiple lines of text, each terminated with a newline, otherwise
+ usually a single line).
+ Must not return if SEVERITY is PO_SEVERITY_FATAL_ERROR. */
+extern DLL_VARIABLE
+ void (*po_xerror) (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text);
+
+/* Signal a problem that refers to two messages.
+ Similar to two calls to po_xerror.
+ If possible, a "..." can be appended to MESSAGE_TEXT1 and prepended to
+ MESSAGE_TEXT2. */
+extern DLL_VARIABLE
+ void (*po_xerror2) (int severity,
+ const struct message_ty *message1,
+ const char *filename1, size_t lineno1, size_t column1,
+ int multiline_p1, const char *message_text1,
+ const struct message_ty *message2,
+ const char *filename2, size_t lineno2, size_t column2,
+ int multiline_p2, const char *message_text2);
+
+/* The default error handler. */
+extern void textmode_xerror (int severity,
+ const struct message_ty *message,
+ const char *filename, size_t lineno, size_t column,
+ int multiline_p, const char *message_text);
+extern void textmode_xerror2 (int severity,
+ const struct message_ty *message1,
+ const char *filename1, size_t lineno1, size_t column1,
+ int multiline_p1, const char *message_text1,
+ const struct message_ty *message2,
+ const char *filename2, size_t lineno2, size_t column2,
+ int multiline_p2, const char *message_text2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PO_XERROR_H */
diff --git a/gettext-tools/src/pos.h b/gettext-tools/src/pos.h
new file mode 100644
index 0000000..684986e
--- /dev/null
+++ b/gettext-tools/src/pos.h
@@ -0,0 +1,32 @@
+/* Source file positions.
+ Copyright (C) 1995-1998, 2000-2001 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _POS_H
+#define _POS_H
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Position of a message within a source file.
+ Used for error reporting purposes. */
+typedef struct lex_pos_ty lex_pos_ty;
+struct lex_pos_ty
+{
+ char *file_name;
+ size_t line_number;
+};
+
+#endif /* _POS_H */
diff --git a/gettext-tools/src/project-id b/gettext-tools/src/project-id
new file mode 100755
index 0000000..e60c8e3
--- /dev/null
+++ b/gettext-tools/src/project-id
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Prints a package's identification PACKAGE VERSION or PACKAGE.
+#
+# Copyright (C) 2001-2003, 2005 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 <http://www.gnu.org/licenses/>.
+
+want_version="$1"
+
+# NLS nuisances: Letter ranges are different in the Estonian locale.
+LC_ALL=C
+
+while true; do
+ if test -f configure; then
+ package=`(grep '^PACKAGE_NAME=' configure; grep '^ *PACKAGE=' configure) | grep -v '=[ ]*$' | sed -e '1q' | sed -e 's/^[^=]*=//' | sed -e "s/^'//" -e "s/'$//"`
+ case "$package" in
+ *[\"\$\`\{\}]*)
+ # Some packages (gcal) retrieve the package name dynamically.
+ package=
+ ;;
+ esac
+ if test -n "$package"; then
+ is_gnu=`LC_ALL=C grep "GNU $package" * 2>/dev/null | grep -v '^libtool:'`
+ if test -n "$is_gnu"; then
+ package="GNU $package"
+ fi
+ if test -n "$want_version"; then
+ version=`(grep '^PACKAGE_VERSION=' configure; grep '^ *VERSION=' configure) | grep -v '=[ ]*$' | sed -e '1q' | sed -e 's/^[^=]*=//' | sed -e "s/^'//" -e "s/'$//"`
+ case "$version" in
+ *[\"\$\`\{\}]*)
+ # Some packages (gcal, gcc, clisp) retrieve the version dynamically.
+ version=
+ ;;
+ esac
+ if test -n "$version"; then
+ echo "$package $version"
+ else
+ echo "$package"
+ fi
+ else
+ echo "$package"
+ fi
+ exit 0
+ fi
+ fi
+ dir=`basename "\`pwd\`"`
+ case "$dir" in
+ i18n)
+ # This directory name, used in GNU make, is not the top level directory.
+ ;;
+ *[A-Za-z]*[0-9]*)
+ package=`echo "$dir" | sed -e 's/^\([^0-9]*\)[0-9].*$/\1/' -e 's/[-_]$//'`
+ if test -n "$want_version"; then
+ version=`echo "$dir" | sed -e 's/^[^0-9]*\([0-9].*\)$/\1/'`
+ echo "$package $version"
+ else
+ echo "$package"
+ fi
+ exit 0
+ ;;
+ esac
+ # Go to parent directory
+ last=`/bin/pwd`
+ cd ..
+ curr=`/bin/pwd`
+ if test "$last" = "$curr"; then
+ # Oops, didn't find the package name.
+ if test -n "$want_version"; then
+ echo "PACKAGE VERSION"
+ else
+ echo "PACKAGE"
+ fi
+ exit 0
+ fi
+done
diff --git a/gettext-tools/src/read-catalog-abstract.c b/gettext-tools/src/read-catalog-abstract.c
new file mode 100644
index 0000000..d4e98ee
--- /dev/null
+++ b/gettext-tools/src/read-catalog-abstract.c
@@ -0,0 +1,748 @@
+/* Reading PO files, abstract class.
+ Copyright (C) 1995-1996, 1998, 2000-2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "read-catalog-abstract.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "error.h"
+#include "gettext.h"
+
+/* Local variables. */
+static abstract_catalog_reader_ty *callback_arg;
+
+
+/* ========================================================================= */
+/* Allocating and freeing instances of abstract_catalog_reader_ty. */
+
+
+abstract_catalog_reader_ty *
+catalog_reader_alloc (abstract_catalog_reader_class_ty *method_table)
+{
+ abstract_catalog_reader_ty *pop;
+
+ pop = (abstract_catalog_reader_ty *) xmalloc (method_table->size);
+ pop->methods = method_table;
+ if (method_table->constructor)
+ method_table->constructor (pop);
+ return pop;
+}
+
+
+void
+catalog_reader_free (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->destructor)
+ pop->methods->destructor (pop);
+ free (pop);
+}
+
+
+/* ========================================================================= */
+/* Inline functions to invoke the methods. */
+
+
+static inline void
+call_parse_brief (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->parse_brief)
+ pop->methods->parse_brief (pop);
+}
+
+static inline void
+call_parse_debrief (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->parse_debrief)
+ pop->methods->parse_debrief (pop);
+}
+
+static inline void
+call_directive_domain (abstract_catalog_reader_ty *pop, char *name)
+{
+ if (pop->methods->directive_domain)
+ pop->methods->directive_domain (pop, name);
+}
+
+static inline void
+call_directive_message (abstract_catalog_reader_ty *pop,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ if (pop->methods->directive_message)
+ pop->methods->directive_message (pop, msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt,
+ prev_msgid,
+ prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+static inline void
+call_comment (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment != NULL)
+ pop->methods->comment (pop, s);
+}
+
+static inline void
+call_comment_dot (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment_dot != NULL)
+ pop->methods->comment_dot (pop, s);
+}
+
+static inline void
+call_comment_filepos (abstract_catalog_reader_ty *pop, const char *name,
+ size_t line)
+{
+ if (pop->methods->comment_filepos)
+ pop->methods->comment_filepos (pop, name, line);
+}
+
+static inline void
+call_comment_special (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment_special != NULL)
+ pop->methods->comment_special (pop, s);
+}
+
+
+/* ========================================================================= */
+/* Exported functions. */
+
+
+static inline void
+parse_start (abstract_catalog_reader_ty *pop)
+{
+ /* The parse will call the po_callback_... functions (see below)
+ when the various directive are recognised. The callback_arg
+ variable is used to tell these functions which instance is to
+ have the relevant method invoked. */
+ callback_arg = pop;
+
+ call_parse_brief (pop);
+}
+
+static inline void
+parse_end (abstract_catalog_reader_ty *pop)
+{
+ call_parse_debrief (pop);
+ callback_arg = NULL;
+}
+
+
+void
+catalog_reader_parse (abstract_catalog_reader_ty *pop, FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ catalog_input_format_ty input_syntax)
+{
+ error_message_count = 0;
+
+ /* Parse the stream's content. */
+ parse_start (pop);
+ input_syntax->parse (pop, fp, real_filename, logical_filename);
+ parse_end (pop);
+
+ if (error_message_count > 0)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
+ /*real_filename*/ NULL, (size_t)(-1), (size_t)(-1), false,
+ xasprintf (ngettext ("found %d fatal error",
+ "found %d fatal errors",
+ error_message_count),
+ error_message_count));
+}
+
+
+/* ========================================================================= */
+/* Callbacks used by po-gram.y or po-lex.c, indirectly from
+ catalog_reader_parse. */
+
+
+/* This function is called by po_gram_lex() whenever a domain directive
+ has been seen. */
+void
+po_callback_domain (char *name)
+{
+ /* assert(callback_arg); */
+ call_directive_domain (callback_arg, name);
+}
+
+
+/* This function is called by po_gram_lex() whenever a message has been
+ seen. */
+void
+po_callback_message (char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ /* assert(callback_arg); */
+ call_directive_message (callback_arg, msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+
+void
+po_callback_comment (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment (callback_arg, s);
+}
+
+
+void
+po_callback_comment_dot (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_dot (callback_arg, s);
+}
+
+
+/* This function is called by po_parse_comment_filepos(), once for each
+ filename. */
+void
+po_callback_comment_filepos (const char *name, size_t line)
+{
+ /* assert(callback_arg); */
+ call_comment_filepos (callback_arg, name, line);
+}
+
+
+void
+po_callback_comment_special (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_special (callback_arg, s);
+}
+
+
+/* Parse a special comment and put the result in *fuzzyp, formatp, *rangep,
+ *wrapp. */
+void
+po_parse_comment_special (const char *s,
+ bool *fuzzyp, enum is_format formatp[NFORMATS],
+ struct argument_range *rangep, enum is_wrap *wrapp)
+{
+ size_t i;
+
+ *fuzzyp = false;
+ for (i = 0; i < NFORMATS; i++)
+ formatp[i] = undecided;
+ rangep->min = -1;
+ rangep->max = -1;
+ *wrapp = undecided;
+
+ while (*s != '\0')
+ {
+ const char *t;
+
+ /* Skip whitespace. */
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
+ s++;
+
+ /* Collect a token. */
+ t = s;
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
+ s++;
+ if (s != t)
+ {
+ size_t len = s - t;
+
+ /* Accept fuzzy flag. */
+ if (len == 5 && memcmp (t, "fuzzy", 5) == 0)
+ {
+ *fuzzyp = true;
+ continue;
+ }
+
+ /* Accept format description. */
+ if (len >= 7 && memcmp (t + len - 7, "-format", 7) == 0)
+ {
+ const char *p;
+ size_t n;
+ enum is_format value;
+
+ p = t;
+ n = len - 7;
+
+ if (n >= 3 && memcmp (p, "no-", 3) == 0)
+ {
+ p += 3;
+ n -= 3;
+ value = no;
+ }
+ else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
+ {
+ p += 9;
+ n -= 9;
+ value = possible;
+ }
+ else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
+ {
+ p += 11;
+ n -= 11;
+ value = impossible;
+ }
+ else
+ value = yes;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (strlen (format_language[i]) == n
+ && memcmp (format_language[i], p, n) == 0)
+ {
+ formatp[i] = value;
+ break;
+ }
+ if (i < NFORMATS)
+ continue;
+ }
+
+ /* Accept range description "range: <min>..<max>". */
+ if (len == 6 && memcmp (t, "range:", 6) == 0)
+ {
+ /* Skip whitespace. */
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
+ s++;
+
+ /* Collect a token. */
+ t = s;
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
+ s++;
+ /* Parse it. */
+ if (*t >= '0' && *t <= '9')
+ {
+ unsigned int min = 0;
+
+ for (; *t >= '0' && *t <= '9'; t++)
+ {
+ if (min <= INT_MAX / 10)
+ {
+ min = 10 * min + (*t - '0');
+ if (min > INT_MAX)
+ min = INT_MAX;
+ }
+ else
+ /* Avoid integer overflow. */
+ min = INT_MAX;
+ }
+ if (*t++ == '.')
+ if (*t++ == '.')
+ if (*t >= '0' && *t <= '9')
+ {
+ unsigned int max = 0;
+ for (; *t >= '0' && *t <= '9'; t++)
+ {
+ if (max <= INT_MAX / 10)
+ {
+ max = 10 * max + (*t - '0');
+ if (max > INT_MAX)
+ max = INT_MAX;
+ }
+ else
+ /* Avoid integer overflow. */
+ max = INT_MAX;
+ }
+ if (min <= max)
+ {
+ rangep->min = min;
+ rangep->max = max;
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Accept wrap description. */
+ if (len == 4 && memcmp (t, "wrap", 4) == 0)
+ {
+ *wrapp = yes;
+ continue;
+ }
+ if (len == 7 && memcmp (t, "no-wrap", 7) == 0)
+ {
+ *wrapp = no;
+ continue;
+ }
+
+ /* Unknown special comment marker. It may have been generated
+ from a future xgettext version. Ignore it. */
+ }
+ }
+}
+
+
+/* Parse a GNU style file comment.
+ Syntax: an arbitrary number of
+ STRING COLON NUMBER
+ or
+ STRING
+ The latter style, without line number, occurs in PO files converted e.g.
+ from Pascal .rst files or from OpenOffice resource files.
+ Call po_callback_comment_filepos for each of them. */
+static void
+po_parse_comment_filepos (const char *s)
+{
+ while (*s != '\0')
+ {
+ while (*s == ' ' || *s == '\t' || *s == '\n')
+ s++;
+ if (*s != '\0')
+ {
+ const char *string_start = s;
+
+ do
+ s++;
+ while (!(*s == '\0' || *s == ' ' || *s == '\t' || *s == '\n'));
+
+ /* See if there is a COLON and NUMBER after the STRING, separated
+ through optional spaces. */
+ {
+ const char *p = s;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p == ':')
+ {
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
+ {
+ /* Parsed a GNU style file comment with spaces. */
+ const char *string_end = s;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ s = p;
+ continue;
+ }
+ }
+ }
+ }
+
+ /* See if there is a COLON at the end of STRING and a NUMBER after
+ it, separated through optional spaces. */
+ if (s[-1] == ':')
+ {
+ const char *p = s;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
+ {
+ /* Parsed a GNU style file comment with spaces. */
+ const char *string_end = s - 1;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ s = p;
+ continue;
+ }
+ }
+ }
+
+ /* See if there is a COLON and NUMBER at the end of the STRING,
+ without separating spaces. */
+ {
+ const char *p = s;
+
+ while (p > string_start)
+ {
+ p--;
+ if (!(*p >= '0' && *p <= '9'))
+ {
+ p++;
+ break;
+ }
+ }
+
+ /* p now points to the beginning of the trailing digits segment
+ at the end of STRING. */
+
+ if (p < s
+ && p > string_start + 1
+ && p[-1] == ':')
+ {
+ /* Parsed a GNU style file comment without spaces. */
+ const char *string_end = p - 1;
+
+ /* Accumulate a number. */
+ {
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (p < s);
+
+ {
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Parsed a file comment without line number. */
+ {
+ const char *string_end = s;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, (size_t)(-1));
+
+ free (string);
+ }
+ }
+ }
+}
+
+
+/* Parse a SunOS or Solaris style file comment.
+ Syntax of SunOS style:
+ FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD COLON NUMBER
+ Syntax of Solaris style:
+ FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD NUMBER_KEYWORD COLON NUMBER
+ where
+ FILE_KEYWORD ::= "file" | "File"
+ COLON ::= ":"
+ COMMA ::= ","
+ LINE_KEYWORD ::= "line"
+ NUMBER_KEYWORD ::= "number"
+ NUMBER ::= [0-9]+
+ Return true if parsed, false if not a comment of this form. */
+static bool
+po_parse_comment_solaris_filepos (const char *s)
+{
+ if (s[0] == ' '
+ && (s[1] == 'F' || s[1] == 'f')
+ && s[2] == 'i' && s[3] == 'l' && s[4] == 'e'
+ && s[5] == ':')
+ {
+ const char *string_start;
+ const char *string_end;
+
+ {
+ const char *p = s + 6;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ string_start = p;
+ }
+
+ for (string_end = string_start; *string_end != '\0'; string_end++)
+ {
+ const char *p = string_end;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == ',')
+ {
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (p[0] == 'l' && p[1] == 'i' && p[2] == 'n' && p[3] == 'e')
+ {
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (p[0] == 'n' && p[1] == 'u' && p[2] == 'm'
+ && p[3] == 'b' && p[4] == 'e' && p[5] == 'r')
+ {
+ p += 6;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+
+ if (*p == ':')
+ {
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p == '\0')
+ {
+ /* Parsed a Sun style file comment. */
+ size_t string_length = string_end - string_start;
+ char *string =
+ XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/* This function is called by po_gram_lex() whenever a comment is
+ seen. It analyzes the comment to see what sort it is, and then
+ dispatches it to the appropriate method: call_comment, call_comment_dot,
+ call_comment_filepos (via po_parse_comment_filepos), or
+ call_comment_special. */
+void
+po_callback_comment_dispatcher (const char *s)
+{
+ if (*s == '.')
+ {
+ s++;
+ /* There is usually a space before the comment. People don't
+ consider it part of the comment, therefore remove it here. */
+ if (*s == ' ')
+ s++;
+ po_callback_comment_dot (s);
+ }
+ else if (*s == ':')
+ {
+ /* Parse the file location string. The appropriate callback will be
+ invoked. */
+ po_parse_comment_filepos (s + 1);
+ }
+ else if (*s == ',' || *s == '!')
+ {
+ /* Get all entries in the special comment line. */
+ po_callback_comment_special (s + 1);
+ }
+ else
+ {
+ /* It looks like a plain vanilla comment, but Solaris-style file
+ position lines do, too. Try to parse the lot. If the parse
+ succeeds, the appropriate callback will be invoked. */
+ if (po_parse_comment_solaris_filepos (s))
+ /* Do nothing, it is a Sun-style file pos line. */ ;
+ else
+ {
+ /* There is usually a space before the comment. People don't
+ consider it part of the comment, therefore remove it here. */
+ if (*s == ' ')
+ s++;
+ po_callback_comment (s);
+ }
+ }
+}
diff --git a/gettext-tools/src/read-catalog-abstract.h b/gettext-tools/src/read-catalog-abstract.h
new file mode 100644
index 0000000..c3fc84f
--- /dev/null
+++ b/gettext-tools/src/read-catalog-abstract.h
@@ -0,0 +1,195 @@
+/* Reading PO files, abstract class.
+ Copyright (C) 1995-1996, 1998, 2000-2003, 2005-2006, 2008-2009, 2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_CATALOG_ABSTRACT_H
+#define _READ_CATALOG_ABSTRACT_H
+
+#include "po-lex.h"
+#include "message.h"
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Note: the _t suffix is reserved by ANSI C, so the _ty suffix is
+ used to indicate a type name. */
+
+/* The following pair of structures cooperate to create an "Object" in
+ the OO sense. We are simply doing it manually, rather than with the
+ help of an OO compiler. This implementation allows polymorphism
+ and inheritance - more than enough for the immediate needs. */
+
+/* Forward declaration. */
+struct abstract_catalog_reader_ty;
+
+
+/* This first structure, playing the role of the "Class" in OO sense,
+ contains pointers to functions. Each function is a method for the
+ class (base or derived). Use a NULL pointer where no action is
+ required. */
+
+typedef struct abstract_catalog_reader_class_ty
+ abstract_catalog_reader_class_ty;
+struct abstract_catalog_reader_class_ty
+{
+ /* how many bytes to malloc for an instance of this class */
+ size_t size;
+
+ /* what to do immediately after the instance is malloc()ed */
+ void (*constructor) (struct abstract_catalog_reader_ty *pop);
+
+ /* what to do immediately before the instance is free()ed */
+ void (*destructor) (struct abstract_catalog_reader_ty *pop);
+
+ /* This method is invoked before the parse, but after the file is
+ opened by the lexer. */
+ void (*parse_brief) (struct abstract_catalog_reader_ty *pop);
+
+ /* This method is invoked after the parse, but before the file is
+ closed by the lexer. The intention is to make consistency checks
+ against the file here, and emit the errors through the lex_error*
+ functions. */
+ void (*parse_debrief) (struct abstract_catalog_reader_ty *pop);
+
+ /* what to do with a domain directive */
+ void (*directive_domain) (struct abstract_catalog_reader_ty *pop, char *name);
+
+ /* what to do with a message directive */
+ void (*directive_message) (struct abstract_catalog_reader_ty *pop,
+ char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid, char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete);
+
+ /* What to do with a plain-vanilla comment - the expectation is that
+ they will be accumulated, and added to the next message
+ definition seen. Or completely ignored. */
+ void (*comment) (struct abstract_catalog_reader_ty *pop, const char *s);
+
+ /* What to do with a comment that starts with a dot (i.e. extracted
+ by xgettext) - the expectation is that they will be accumulated,
+ and added to the next message definition seen. Or completely
+ ignored. */
+ void (*comment_dot) (struct abstract_catalog_reader_ty *pop, const char *s);
+
+ /* What to do with a file position seen in a comment (i.e. a message
+ location comment extracted by xgettext) - the expectation is that
+ they will be accumulated, and added to the next message
+ definition seen. Or completely ignored. */
+ void (*comment_filepos) (struct abstract_catalog_reader_ty *pop,
+ const char *s, size_t line);
+
+ /* What to do with a comment that starts with a ',' or '!' - this is a
+ special comment. One of the possible uses is to indicate a
+ inexact translation. */
+ void (*comment_special) (struct abstract_catalog_reader_ty *pop,
+ const char *s);
+};
+
+
+/* This next structure defines the base class passed to the methods.
+ Derived methods will often need to cast their first argument before
+ using it (this corresponds to the implicit 'this' argument in C++).
+
+ When declaring derived classes, use the ABSTRACT_CATALOG_READER_TY define
+ at the start of the structure, to declare inherited instance variables,
+ etc. */
+
+#define ABSTRACT_CATALOG_READER_TY \
+ abstract_catalog_reader_class_ty *methods;
+
+typedef struct abstract_catalog_reader_ty abstract_catalog_reader_ty;
+struct abstract_catalog_reader_ty
+{
+ ABSTRACT_CATALOG_READER_TY
+};
+
+
+/* This structure describes a textual catalog input format. */
+struct catalog_input_format
+{
+ /* Parses the contents of FP, invoking the appropriate callbacks. */
+ void (*parse) (abstract_catalog_reader_ty *pop, FILE *fp,
+ const char *real_filename, const char *logical_filename);
+
+ /* Whether the parse function always produces messages encoded in UTF-8
+ encoding. */
+ bool produces_utf8;
+};
+
+typedef const struct catalog_input_format * catalog_input_format_ty;
+
+
+/* Allocate a fresh abstract_catalog_reader_ty (or derived class) instance and
+ call its constructor. */
+extern abstract_catalog_reader_ty *
+ catalog_reader_alloc (abstract_catalog_reader_class_ty *method_table);
+
+/* Read a PO file from a stream, and dispatch to the various
+ abstract_catalog_reader_class_ty methods. */
+extern void
+ catalog_reader_parse (abstract_catalog_reader_ty *pop, FILE *fp,
+ const char *real_filename,
+ const char *logical_filename,
+ catalog_input_format_ty input_syntax);
+
+/* Call the destructor and deallocate a abstract_catalog_reader_ty (or derived
+ class) instance. */
+extern void
+ catalog_reader_free (abstract_catalog_reader_ty *pop);
+
+
+/* Callbacks used by po-gram.y or po-lex.c, indirectly from
+ catalog_reader_parse. */
+extern void po_callback_domain (char *name);
+extern void po_callback_message (char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid, char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete);
+extern void po_callback_comment (const char *s);
+extern void po_callback_comment_dot (const char *s);
+extern void po_callback_comment_filepos (const char *s, size_t line);
+extern void po_callback_comment_special (const char *s);
+extern void po_callback_comment_dispatcher (const char *s);
+
+/* Parse a special comment and put the result in *fuzzyp, formatp, *rangep,
+ *wrapp. */
+extern void po_parse_comment_special (const char *s, bool *fuzzyp,
+ enum is_format formatp[NFORMATS],
+ struct argument_range *rangep,
+ enum is_wrap *wrapp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _READ_CATALOG_ABSTRACT_H */
diff --git a/gettext-tools/src/read-catalog.c b/gettext-tools/src/read-catalog.c
new file mode 100644
index 0000000..4642249
--- /dev/null
+++ b/gettext-tools/src/read-catalog.c
@@ -0,0 +1,494 @@
+/* Reading PO files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009 Free Software Foundation, Inc.
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-catalog.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "open-catalog.h"
+#include "po-charset.h"
+#include "po-xerror.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* ========================================================================= */
+/* Inline functions to invoke the methods. */
+
+static inline void
+call_set_domain (struct default_catalog_reader_ty *this, char *name)
+{
+ default_catalog_reader_class_ty *methods =
+ (default_catalog_reader_class_ty *) this->methods;
+
+ if (methods->set_domain)
+ methods->set_domain (this, name);
+}
+
+static inline void
+call_add_message (struct default_catalog_reader_ty *this,
+ char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt, char *prev_msgid, char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ default_catalog_reader_class_ty *methods =
+ (default_catalog_reader_class_ty *) this->methods;
+
+ if (methods->add_message)
+ methods->add_message (this, msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+static inline void
+call_frob_new_message (struct default_catalog_reader_ty *this, message_ty *mp,
+ const lex_pos_ty *msgid_pos,
+ const lex_pos_ty *msgstr_pos)
+{
+ default_catalog_reader_class_ty *methods =
+ (default_catalog_reader_class_ty *) this->methods;
+
+ if (methods->frob_new_message)
+ methods->frob_new_message (this, mp, msgid_pos, msgstr_pos);
+}
+
+
+/* ========================================================================= */
+/* Implementation of default_catalog_reader_ty's methods. */
+
+
+/* Implementation of methods declared in the superclass. */
+
+
+/* Prepare for first message. */
+void
+default_constructor (abstract_catalog_reader_ty *that)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+ size_t i;
+
+ this->domain = MESSAGE_DOMAIN_DEFAULT;
+ this->comment = NULL;
+ this->comment_dot = NULL;
+ this->filepos_count = 0;
+ this->filepos = NULL;
+ this->is_fuzzy = false;
+ for (i = 0; i < NFORMATS; i++)
+ this->is_format[i] = undecided;
+ this->range.min = -1;
+ this->range.max = -1;
+ this->do_wrap = undecided;
+}
+
+
+void
+default_destructor (abstract_catalog_reader_ty *that)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+ size_t j;
+
+ /* Do not free this->mdlp and this->mlp. */
+ if (this->handle_comments)
+ {
+ if (this->comment != NULL)
+ string_list_free (this->comment);
+ if (this->comment_dot != NULL)
+ string_list_free (this->comment_dot);
+ }
+
+ for (j = 0; j < this->filepos_count; ++j)
+ free (this->filepos[j].file_name);
+ if (this->filepos != NULL)
+ free (this->filepos);
+}
+
+
+void
+default_parse_brief (abstract_catalog_reader_ty *that)
+{
+ /* We need to parse comments, because even if this->handle_comments
+ is false, we need to know which messages are fuzzy. */
+ po_lex_pass_comments (true);
+}
+
+
+void
+default_parse_debrief (abstract_catalog_reader_ty *that)
+{
+}
+
+
+/* Add the accumulated comments to the message. */
+static void
+default_copy_comment_state (default_catalog_reader_ty *this, message_ty *mp)
+{
+ size_t j, i;
+
+ if (this->handle_comments)
+ {
+ if (this->comment != NULL)
+ for (j = 0; j < this->comment->nitems; ++j)
+ message_comment_append (mp, this->comment->item[j]);
+ if (this->comment_dot != NULL)
+ for (j = 0; j < this->comment_dot->nitems; ++j)
+ message_comment_dot_append (mp, this->comment_dot->item[j]);
+ }
+ for (j = 0; j < this->filepos_count; ++j)
+ {
+ lex_pos_ty *pp;
+
+ pp = &this->filepos[j];
+ message_comment_filepos (mp, pp->file_name, pp->line_number);
+ }
+ mp->is_fuzzy = this->is_fuzzy;
+ for (i = 0; i < NFORMATS; i++)
+ mp->is_format[i] = this->is_format[i];
+ mp->range = this->range;
+ mp->do_wrap = this->do_wrap;
+}
+
+
+static void
+default_reset_comment_state (default_catalog_reader_ty *this)
+{
+ size_t j, i;
+
+ if (this->handle_comments)
+ {
+ if (this->comment != NULL)
+ {
+ string_list_free (this->comment);
+ this->comment = NULL;
+ }
+ if (this->comment_dot != NULL)
+ {
+ string_list_free (this->comment_dot);
+ this->comment_dot = NULL;
+ }
+ }
+ for (j = 0; j < this->filepos_count; ++j)
+ free (this->filepos[j].file_name);
+ if (this->filepos != NULL)
+ free (this->filepos);
+ this->filepos_count = 0;
+ this->filepos = NULL;
+ this->is_fuzzy = false;
+ for (i = 0; i < NFORMATS; i++)
+ this->is_format[i] = undecided;
+ this->range.min = -1;
+ this->range.max = -1;
+ this->do_wrap = undecided;
+}
+
+
+/* Process 'domain' directive from .po file. */
+void
+default_directive_domain (abstract_catalog_reader_ty *that, char *name)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+
+ call_set_domain (this, name);
+
+ /* If there are accumulated comments, throw them away, they are
+ probably part of the file header, or about the domain directive,
+ and will be unrelated to the next message. */
+ default_reset_comment_state (this);
+}
+
+
+/* Process ['msgctxt'/]'msgid'/'msgstr' pair from .po file. */
+void
+default_directive_message (abstract_catalog_reader_ty *that,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid, char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+
+ call_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+
+ /* Prepare for next message. */
+ default_reset_comment_state (this);
+}
+
+
+void
+default_comment (abstract_catalog_reader_ty *that, const char *s)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+
+ if (this->handle_comments)
+ {
+ if (this->comment == NULL)
+ this->comment = string_list_alloc ();
+ string_list_append (this->comment, s);
+ }
+}
+
+
+void
+default_comment_dot (abstract_catalog_reader_ty *that, const char *s)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+
+ if (this->handle_comments)
+ {
+ if (this->comment_dot == NULL)
+ this->comment_dot = string_list_alloc ();
+ string_list_append (this->comment_dot, s);
+ }
+}
+
+
+void
+default_comment_filepos (abstract_catalog_reader_ty *that,
+ const char *name, size_t line)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+ size_t nbytes;
+ lex_pos_ty *pp;
+
+ nbytes = (this->filepos_count + 1) * sizeof (this->filepos[0]);
+ this->filepos = xrealloc (this->filepos, nbytes);
+ pp = &this->filepos[this->filepos_count++];
+ pp->file_name = xstrdup (name);
+ pp->line_number = line;
+}
+
+
+/* Test for '#, fuzzy' comments and warn. */
+void
+default_comment_special (abstract_catalog_reader_ty *that, const char *s)
+{
+ default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
+
+ po_parse_comment_special (s, &this->is_fuzzy, this->is_format, &this->range,
+ &this->do_wrap);
+}
+
+
+/* Default implementation of methods not inherited from the superclass. */
+
+
+void
+default_set_domain (default_catalog_reader_ty *this, char *name)
+{
+ if (this->allow_domain_directives)
+ /* Override current domain name. Don't free memory. */
+ this->domain = name;
+ else
+ {
+ po_gram_error_at_line (&gram_pos,
+ _("this file may not contain domain directives"));
+
+ /* NAME was allocated in po-gram-gen.y but is not used anywhere. */
+ free (name);
+ }
+}
+
+void
+default_add_message (default_catalog_reader_ty *this,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ message_ty *mp;
+
+ if (this->mdlp != NULL)
+ /* Select the appropriate sublist of this->mdlp. */
+ this->mlp = msgdomain_list_sublist (this->mdlp, this->domain, true);
+
+ if (this->allow_duplicates && msgid[0] != '\0')
+ /* Doesn't matter if this message ID has been seen before. */
+ mp = NULL;
+ else
+ /* See if this message ID has been seen before. */
+ mp = message_list_search (this->mlp, msgctxt, msgid);
+
+ if (mp)
+ {
+ if (!(this->allow_duplicates_if_same_msgstr
+ && msgstr_len == mp->msgstr_len
+ && memcmp (msgstr, mp->msgstr, msgstr_len) == 0))
+ {
+ /* We give a fatal error about this, regardless whether the
+ translations are equal or different. This is for consistency
+ with msgmerge, msgcat and others. The user can use the
+ msguniq program to get rid of duplicates. */
+ po_xerror2 (PO_SEVERITY_ERROR,
+ NULL, msgid_pos->file_name, msgid_pos->line_number,
+ (size_t)(-1), false, _("duplicate message definition"),
+ mp, NULL, 0, 0, false,
+ _("this is the location of the first definition"));
+ }
+ /* We don't need the just constructed entries' parameter string
+ (allocated in po-gram-gen.y). */
+ free (msgid);
+ if (msgid_plural != NULL)
+ free (msgid_plural);
+ free (msgstr);
+ if (msgctxt != NULL)
+ free (msgctxt);
+ if (prev_msgctxt != NULL)
+ free (prev_msgctxt);
+ if (prev_msgid != NULL)
+ free (prev_msgid);
+ if (prev_msgid_plural != NULL)
+ free (prev_msgid_plural);
+
+ /* Add the accumulated comments to the message. */
+ default_copy_comment_state (this, mp);
+ }
+ else
+ {
+ /* Construct message to add to the list.
+ Obsolete message go into the list at least for duplicate checking.
+ It's the caller's responsibility to ignore obsolete messages when
+ appropriate. */
+ mp = message_alloc (msgctxt, msgid, msgid_plural, msgstr, msgstr_len,
+ msgstr_pos);
+ mp->prev_msgctxt = prev_msgctxt;
+ mp->prev_msgid = prev_msgid;
+ mp->prev_msgid_plural = prev_msgid_plural;
+ mp->obsolete = obsolete;
+ default_copy_comment_state (this, mp);
+ if (force_fuzzy)
+ mp->is_fuzzy = true;
+
+ call_frob_new_message (this, mp, msgid_pos, msgstr_pos);
+
+ message_list_append (this->mlp, mp);
+ }
+}
+
+
+/* So that the one parser can be used for multiple programs, and also
+ use good data hiding and encapsulation practices, an object
+ oriented approach has been taken. An object instance is allocated,
+ and all actions resulting from the parse will be through
+ invocations of method functions of that object. */
+
+static default_catalog_reader_class_ty default_methods =
+{
+ {
+ sizeof (default_catalog_reader_ty),
+ default_constructor,
+ default_destructor,
+ default_parse_brief,
+ default_parse_debrief,
+ default_directive_domain,
+ default_directive_message,
+ default_comment,
+ default_comment_dot,
+ default_comment_filepos,
+ default_comment_special
+ },
+ default_set_domain, /* set_domain */
+ default_add_message, /* add_message */
+ NULL /* frob_new_message */
+};
+
+
+default_catalog_reader_ty *
+default_catalog_reader_alloc (default_catalog_reader_class_ty *method_table)
+{
+ return
+ (default_catalog_reader_ty *) catalog_reader_alloc (&method_table->super);
+}
+
+
+/* ========================================================================= */
+/* Exported functions. */
+
+
+/* If false, duplicate msgids in the same domain and file generate an error.
+ If true, such msgids are allowed; the caller should treat them
+ appropriately. Defaults to false. */
+bool allow_duplicates = false;
+
+
+msgdomain_list_ty *
+read_catalog_stream (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ catalog_input_format_ty input_syntax)
+{
+ default_catalog_reader_ty *pop;
+ msgdomain_list_ty *mdlp;
+
+ pop = default_catalog_reader_alloc (&default_methods);
+ pop->handle_comments = true;
+ pop->allow_domain_directives = true;
+ pop->allow_duplicates = allow_duplicates;
+ pop->allow_duplicates_if_same_msgstr = false;
+ pop->file_name = real_filename;
+ pop->mdlp = msgdomain_list_alloc (!pop->allow_duplicates);
+ pop->mlp = msgdomain_list_sublist (pop->mdlp, pop->domain, true);
+ if (input_syntax->produces_utf8)
+ /* We know a priori that input_syntax->parse convert strings to UTF-8. */
+ pop->mdlp->encoding = po_charset_utf8;
+ po_lex_pass_obsolete_entries (true);
+ catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename,
+ logical_filename, input_syntax);
+ mdlp = pop->mdlp;
+ catalog_reader_free ((abstract_catalog_reader_ty *) pop);
+ return mdlp;
+}
+
+
+msgdomain_list_ty *
+read_catalog_file (const char *filename, catalog_input_format_ty input_syntax)
+{
+ char *real_filename;
+ FILE *fp = open_catalog_file (filename, &real_filename, true);
+ msgdomain_list_ty *result;
+
+ result = read_catalog_stream (fp, real_filename, filename, input_syntax);
+
+ if (fp != stdin)
+ fclose (fp);
+
+ return result;
+}
diff --git a/gettext-tools/src/read-catalog.h b/gettext-tools/src/read-catalog.h
new file mode 100644
index 0000000..f567d78
--- /dev/null
+++ b/gettext-tools/src/read-catalog.h
@@ -0,0 +1,192 @@
+/* Reading PO files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009 Free Software Foundation, Inc.
+ This file was written by Bruno Haible <haible@clisp.cons.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_CATALOG_H
+#define _READ_CATALOG_H
+
+#include "message.h"
+#include "read-catalog-abstract.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+
+
+/* For including this file in C++ mode. */
+#ifdef __cplusplus
+# define this thiss
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* The following pair of structures cooperate to create a derived class from
+ class abstract_catalog_reader_ty. (See read-catalog-abstract.h for an
+ explanation.) It implements the default behaviour of reading a PO file
+ and converting it to an 'msgdomain_list_ty *'. */
+
+/* Forward declaration. */
+struct default_catalog_reader_ty;
+
+
+typedef struct default_catalog_reader_class_ty default_catalog_reader_class_ty;
+struct default_catalog_reader_class_ty
+{
+ /* Methods inherited from superclass. */
+ struct abstract_catalog_reader_class_ty super;
+
+ /* How to change the current domain. */
+ void (*set_domain) (struct default_catalog_reader_ty *pop, char *name);
+
+ /* How to add a message to the list. */
+ void (*add_message) (struct default_catalog_reader_ty *pop,
+ char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete);
+
+ /* How to modify a new message before adding it to the list. */
+ void (*frob_new_message) (struct default_catalog_reader_ty *pop,
+ message_ty *mp,
+ const lex_pos_ty *msgid_pos,
+ const lex_pos_ty *msgstr_pos);
+};
+
+
+#define DEFAULT_CATALOG_READER_TY \
+ ABSTRACT_CATALOG_READER_TY \
+ \
+ /* If true, pay attention to comments and filepos comments. */ \
+ bool handle_comments; \
+ \
+ /* If false, domain directives lead to an error messsage. */ \
+ bool allow_domain_directives; \
+ \
+ /* If false, duplicate msgids in the same domain and file generate an \
+ error. If true, such msgids are allowed; the caller should treat \
+ them appropriately. */ \
+ bool allow_duplicates; \
+ \
+ /* If true, allow duplicates if they have the same translation. */ \
+ bool allow_duplicates_if_same_msgstr; \
+ \
+ /* File name used in error messages. */ \
+ const char *file_name; \
+ \
+ /* List of messages already appeared in the current file. */ \
+ msgdomain_list_ty *mdlp; \
+ \
+ /* Name of domain we are currently examining. */ \
+ const char *domain; \
+ \
+ /* List of messages belonging to the current domain. */ \
+ message_list_ty *mlp; \
+ \
+ /* Accumulate comments for next message directive. */ \
+ string_list_ty *comment; \
+ string_list_ty *comment_dot; \
+ \
+ /* Accumulate filepos comments for the next message directive. */ \
+ size_t filepos_count; \
+ lex_pos_ty *filepos; \
+ \
+ /* Flags transported in special comments. */ \
+ bool is_fuzzy; \
+ enum is_format is_format[NFORMATS]; \
+ struct argument_range range; \
+ enum is_wrap do_wrap; \
+
+typedef struct default_catalog_reader_ty default_catalog_reader_ty;
+struct default_catalog_reader_ty
+{
+ DEFAULT_CATALOG_READER_TY
+};
+
+extern void default_constructor (abstract_catalog_reader_ty *that);
+extern void default_destructor (abstract_catalog_reader_ty *that);
+extern void default_parse_brief (abstract_catalog_reader_ty *that);
+extern void default_parse_debrief (abstract_catalog_reader_ty *that);
+extern void default_directive_domain (abstract_catalog_reader_ty *that,
+ char *name);
+extern void default_directive_message (abstract_catalog_reader_ty *that,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete);
+extern void default_comment (abstract_catalog_reader_ty *that, const char *s);
+extern void default_comment_dot (abstract_catalog_reader_ty *that,
+ const char *s);
+extern void default_comment_filepos (abstract_catalog_reader_ty *that,
+ const char *name, size_t line);
+extern void default_comment_special (abstract_catalog_reader_ty *that,
+ const char *s);
+extern void default_set_domain (default_catalog_reader_ty *this, char *name);
+extern void default_add_message (default_catalog_reader_ty *this,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete);
+
+/* Allocate a fresh default_catalog_reader_ty (or derived class) instance and
+ call its constructor. */
+extern default_catalog_reader_ty *
+ default_catalog_reader_alloc (default_catalog_reader_class_ty *method_table);
+
+
+/* If false, duplicate msgids in the same domain and file generate an error.
+ If true, such msgids are allowed; the caller should treat them
+ appropriately. Defaults to false. */
+extern DLL_VARIABLE bool allow_duplicates;
+
+/* Read the input file from a stream. Returns a list of messages. */
+extern msgdomain_list_ty *
+ read_catalog_stream (FILE *fp,
+ const char *real_filename,
+ const char *logical_filename,
+ catalog_input_format_ty input_syntax);
+
+/* Read the input file with the name INPUT_NAME. The ending .po is added
+ if necessary. If INPUT_NAME is not an absolute file name and the file is
+ not found, the list of directories in "dir-list.h" is searched. Returns
+ a list of messages. */
+extern msgdomain_list_ty *
+ read_catalog_file (const char *input_name,
+ catalog_input_format_ty input_syntax);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _READ_CATALOG_H */
diff --git a/gettext-tools/src/read-csharp.c b/gettext-tools/src/read-csharp.c
new file mode 100644
index 0000000..df5ca83
--- /dev/null
+++ b/gettext-tools/src/read-csharp.c
@@ -0,0 +1,168 @@
+/* Reading C# satellite assemblies.
+ Copyright (C) 2003-2004, 2006-2008, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-csharp.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "msgunfmt.h"
+#include "relocatable.h"
+#include "csharpexec.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "xalloc.h"
+#include "concat-filename.h"
+#include "error.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* A C# satellite assembly can only be manipulated by a C# execution engine.
+ So we start a C# process to execute the DumpResource program, and read its
+ output, which is .po format without comments. */
+
+struct locals
+{
+ /* OUT */
+ msgdomain_list_ty *mdlp;
+};
+
+static bool
+execute_and_read_po_output (const char *progname,
+ const char *prog_path, char **prog_argv,
+ void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ int exitstatus;
+
+ /* Open a pipe to the C# execution engine. */
+ child = create_pipe_in (progname, prog_path, prog_argv, DEV_NULL, false,
+ true, true, fd);
+
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+ /* Read the message list. */
+ l->mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po);
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, progname, false, false, true, true, NULL);
+ if (exitstatus != 0)
+ error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+ progname, exitstatus);
+
+ return false;
+}
+
+
+msgdomain_list_ty *
+msgdomain_read_csharp (const char *resource_name, const char *locale_name,
+ const char *directory)
+{
+ char *culture_name;
+ const char *args[4];
+ const char *gettextexedir;
+ const char *gettextlibdir;
+ char *assembly_path;
+ const char *libdirs[1];
+ struct locals locals;
+
+ /* Assign a default value to the resource name. */
+ if (resource_name == NULL)
+ resource_name = "Messages";
+
+ /* Convert the locale name to a .NET specific culture name. */
+ culture_name = xstrdup (locale_name);
+ {
+ char *p;
+ for (p = culture_name; *p != '\0'; p++)
+ if (*p == '_')
+ *p = '-';
+ if (strncmp (culture_name, "sr-CS", 5) == 0)
+ memcpy (culture_name, "sr-SP", 5);
+ p = strchr (culture_name, '@');
+ if (p != NULL)
+ {
+ if (strcmp (p, "@latin") == 0)
+ strcpy (p, "-Latn");
+ else if (strcmp (p, "@cyrillic") == 0)
+ strcpy (p, "-Cyrl");
+ }
+ if (strcmp (culture_name, "sr-SP") == 0)
+ {
+ free (culture_name);
+ culture_name = xstrdup ("sr-SP-Latn");
+ }
+ else if (strcmp (culture_name, "uz-UZ") == 0)
+ {
+ free (culture_name);
+ culture_name = xstrdup ("uz-UZ-Latn");
+ }
+ }
+
+ /* Prepare arguments. */
+ args[0] = directory;
+ args[1] = resource_name;
+ args[2] = culture_name;
+ args[3] = NULL;
+
+ /* Make it possible to override the .exe location. This is
+ necessary for running the testsuite before "make install". */
+ gettextexedir = getenv ("GETTEXTCSHARPEXEDIR");
+ if (gettextexedir == NULL || gettextexedir[0] == '\0')
+ gettextexedir = relocate (LIBDIR "/gettext");
+
+ /* Make it possible to override the .dll location. This is
+ necessary for running the testsuite before "make install". */
+ gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR");
+ if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
+ gettextlibdir = relocate (LIBDIR);
+
+ /* Dump the resource and retrieve the resulting output. */
+ assembly_path =
+ xconcatenated_filename (gettextexedir, "msgunfmt.net", ".exe");
+ libdirs[0] = gettextlibdir;
+ if (execute_csharp_program (assembly_path, libdirs, 1,
+ args,
+ verbose, false,
+ execute_and_read_po_output, &locals))
+ /* An error message should already have been provided. */
+ exit (EXIT_FAILURE);
+
+ free (assembly_path);
+ free (culture_name);
+
+ return locals.mdlp;
+}
diff --git a/gettext-tools/src/read-csharp.h b/gettext-tools/src/read-csharp.h
new file mode 100644
index 0000000..f8b66b3
--- /dev/null
+++ b/gettext-tools/src/read-csharp.h
@@ -0,0 +1,30 @@
+/* Reading C# satellite assemblies.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_CSHARP_H
+#define _READ_CSHARP_H
+
+#include "message.h"
+
+/* Read the Java resource given by resource_name and locale_name.
+ Returns a list of messages. */
+extern msgdomain_list_ty *
+ msgdomain_read_csharp (const char *resource_name,
+ const char *locale_name,
+ const char *directory);
+
+#endif /* _READ_CSHARP_H */
diff --git a/gettext-tools/src/read-desktop.c b/gettext-tools/src/read-desktop.c
new file mode 100644
index 0000000..8f9243b
--- /dev/null
+++ b/gettext-tools/src/read-desktop.c
@@ -0,0 +1,645 @@
+/* Reading Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 Free Software Foundation, Inc.
+ This file was written by Daiki Ueno <ueno@gnu.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-desktop.h"
+
+#include "xalloc.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "c-ctype.h"
+#include "po-lex.h"
+#include "po-xerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* The syntax of a Desktop Entry file is defined at
+ http://standards.freedesktop.org/desktop-entry-spec/latest/index.html. */
+
+desktop_reader_ty *
+desktop_reader_alloc (desktop_reader_class_ty *method_table)
+{
+ desktop_reader_ty *reader;
+
+ reader = (desktop_reader_ty *) xmalloc (method_table->size);
+ reader->methods = method_table;
+ if (method_table->constructor)
+ method_table->constructor (reader);
+ return reader;
+}
+
+void
+desktop_reader_free (desktop_reader_ty *reader)
+{
+ if (reader->methods->destructor)
+ reader->methods->destructor (reader);
+ free (reader);
+}
+
+void
+desktop_reader_handle_group (desktop_reader_ty *reader, const char *group)
+{
+ if (reader->methods->handle_group)
+ reader->methods->handle_group (reader, group);
+}
+
+void
+desktop_reader_handle_pair (desktop_reader_ty *reader,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ if (reader->methods->handle_pair)
+ reader->methods->handle_pair (reader, key_pos, key, locale, value);
+}
+
+void
+desktop_reader_handle_comment (desktop_reader_ty *reader, const char *s)
+{
+ if (reader->methods->handle_comment)
+ reader->methods->handle_comment (reader, s);
+}
+
+void
+desktop_reader_handle_blank (desktop_reader_ty *reader, const char *s)
+{
+ if (reader->methods->handle_blank)
+ reader->methods->handle_blank (reader, s);
+}
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* File name and line number. */
+extern lex_pos_ty gram_pos;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while reading \"%s\""),
+ real_file_name),
+ errno_description));
+ }
+ return EOF;
+ }
+
+ return c;
+}
+
+static inline void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ ungetc (c, fp);
+}
+
+
+static unsigned char phase2_pushback[2];
+static int phase2_pushback_length;
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ c = phase2_pushback[--phase2_pushback_length];
+ else
+ {
+ c = phase1_getc ();
+
+ if (c == '\r')
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '\n')
+ c = c2;
+ else
+ phase1_ungetc (c2);
+ }
+ }
+
+ if (c == '\n')
+ gram_pos.line_number++;
+
+ return c;
+}
+
+static void
+phase2_ungetc (int c)
+{
+ if (c == '\n')
+ --gram_pos.line_number;
+ if (c != EOF)
+ phase2_pushback[phase2_pushback_length++] = c;
+}
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_group,
+ token_type_pair,
+ /* Unlike other scanners, preserve comments and blank lines for
+ merging translations back into a desktop file, with msgfmt. */
+ token_type_comment,
+ token_type_blank,
+ token_type_other
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string;
+ const char *value;
+ const char *locale;
+};
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_group || tp->type == token_type_pair
+ || tp->type == token_type_comment || tp->type == token_type_blank)
+ free (tp->string);
+}
+
+static void
+desktop_lex (token_ty *tp)
+{
+ static char *buffer;
+ static size_t bufmax;
+ size_t bufpos;
+
+#undef APPEND
+#define APPEND(c) \
+ do \
+ { \
+ if (bufpos >= bufmax) \
+ { \
+ bufmax += 100; \
+ buffer = xrealloc (buffer, bufmax); \
+ } \
+ buffer[bufpos++] = c; \
+ } \
+ while (0)
+
+ bufpos = 0;
+ for (;;)
+ {
+ int c;
+
+ c = phase2_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '[':
+ {
+ bool non_blank = false;
+
+ for (;;)
+ {
+ c = phase2_getc ();
+ switch (c)
+ {
+ default:
+ /* Group names may contain all ASCII characters
+ except for '[' and ']' and control characters. */
+ if (!(c_isascii (c) && c != '[') && !c_iscntrl (c))
+ break;
+ APPEND (c);
+ continue;
+ case '\n':
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ real_file_name, gram_pos.line_number, 0, false,
+ _("unterminated group name"));
+ break;
+ case EOF: case ']':
+ break;
+ }
+ break;
+ }
+ /* Skip until newline. */
+ if (c != '\n')
+ {
+ for (;;)
+ {
+ if (c == '\n' || c == EOF)
+ break;
+ if (!c_isspace (c))
+ non_blank = true;
+ c = phase2_getc ();
+ }
+ }
+ if (non_blank)
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ real_file_name, gram_pos.line_number, 0, false,
+ _("invalid non-blank character"));
+ APPEND (0);
+ tp->type = token_type_group;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+
+ case '#':
+ {
+ /* Read until newline. */
+ for (;;)
+ {
+ c = phase2_getc ();
+ switch (c)
+ {
+ default:
+ APPEND (c);
+ continue;
+ case EOF: case '\n':
+ break;
+ }
+ break;
+ }
+ APPEND (0);
+ tp->type = token_type_comment;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '-':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ const char *locale = NULL;
+ const char *value = NULL;
+ for (;;)
+ {
+ APPEND (c);
+
+ c = phase2_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '-':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+
+ case '[':
+ /* Finish the key part and start the locale part. */
+ APPEND (0);
+ locale = &buffer[bufpos];
+
+ for (;;)
+ {
+ int c2 = phase2_getc ();
+ switch (c2)
+ {
+ default:
+ APPEND (c2);
+ continue;
+ case EOF: case ']':
+ break;
+ }
+ break;
+ }
+ break;
+
+ default:
+ phase2_ungetc (c);
+ break;
+ }
+ break;
+ }
+ APPEND (0);
+
+ /* Skip any whitespace before '='. */
+ for (;;)
+ {
+ c = phase2_getc ();
+ switch (c)
+ {
+ default:
+ if (c_isspace (c))
+ continue;
+ phase2_ungetc (c);
+ break;
+ case EOF: case '\n':
+ break;
+ }
+ break;
+ }
+
+ c = phase2_getc ();
+ if (c != '=')
+ {
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ real_file_name, gram_pos.line_number, 0, false,
+ xasprintf (_("missing '=' after \"%s\""), buffer));
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ }
+ tp->type = token_type_other;
+ return;
+ }
+
+ /* Skip any whitespace after '='. */
+ for (;;)
+ {
+ c = phase2_getc ();
+ switch (c)
+ {
+ default:
+ if (c_isspace (c))
+ continue;
+ phase2_ungetc (c);
+ break;
+ case EOF: case '\n':
+ break;
+ }
+ break;
+ }
+
+ value = &buffer[bufpos];
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ APPEND (c);
+ }
+ APPEND (0);
+ tp->type = token_type_pair;
+ tp->string = xmemdup (buffer, bufpos);
+ tp->locale = locale;
+ tp->value = value;
+ return;
+ }
+ default:
+ {
+ bool non_blank = false;
+
+ for (;;)
+ {
+ if (c == '\n' || c == EOF)
+ break;
+
+ if (!c_isspace (c))
+ non_blank = true;
+ else
+ APPEND (c);
+
+ c = phase2_getc ();
+ }
+ if (non_blank)
+ {
+ po_xerror (PO_SEVERITY_WARNING, NULL,
+ real_file_name, gram_pos.line_number, 0, false,
+ _("invalid non-blank line"));
+ tp->type = token_type_other;
+ return;
+ }
+ APPEND (0);
+ tp->type = token_type_blank;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+ }
+ }
+#undef APPEND
+}
+
+void
+desktop_parse (desktop_reader_ty *reader, FILE *file,
+ const char *real_filename, const char *logical_filename)
+{
+ fp = file;
+ real_file_name = real_filename;
+ gram_pos.file_name = xstrdup (logical_filename);
+ gram_pos.line_number = 1;
+
+ for (;;)
+ {
+ struct token_ty token;
+ desktop_lex (&token);
+ switch (token.type)
+ {
+ case token_type_eof:
+ goto out;
+ case token_type_group:
+ desktop_reader_handle_group (reader, token.string);
+ break;
+ case token_type_comment:
+ desktop_reader_handle_comment (reader, token.string);
+ break;
+ case token_type_pair:
+ desktop_reader_handle_pair (reader, &gram_pos,
+ token.string, token.locale, token.value);
+ break;
+ case token_type_blank:
+ desktop_reader_handle_blank (reader, token.string);
+ break;
+ case token_type_other:
+ break;
+ }
+ free_token (&token);
+ }
+
+ out:
+ fp = NULL;
+ real_file_name = NULL;
+ gram_pos.line_number = 0;
+}
+
+char *
+desktop_escape_string (const char *s, bool is_list)
+{
+ char *buffer, *p;
+
+ p = buffer = XNMALLOC (strlen (s) * 2 + 1, char);
+
+ /* The first character must not be a whitespace. */
+ if (*s == ' ')
+ {
+ p = stpcpy (p, "\\s");
+ s++;
+ }
+ else if (*s == '\t')
+ {
+ p = stpcpy (p, "\\t");
+ s++;
+ }
+
+ for (;; s++)
+ {
+ if (*s == '\0')
+ {
+ *p = '\0';
+ break;
+ }
+
+ switch (*s)
+ {
+ case '\n':
+ p = stpcpy (p, "\\n");
+ break;
+ case '\r':
+ p = stpcpy (p, "\\r");
+ break;
+ case '\\':
+ if (is_list && *(s + 1) == ';')
+ {
+ p = stpcpy (p, "\\;");
+ s++;
+ }
+ else
+ p = stpcpy (p, "\\\\");
+ break;
+ default:
+ *p++ = *s;
+ break;
+ }
+ }
+
+ return buffer;
+}
+
+char *
+desktop_unescape_string (const char *s, bool is_list)
+{
+ char *buffer, *p;
+
+ p = buffer = XNMALLOC (strlen (s) + 1, char);
+ for (;; s++)
+ {
+ if (*s == '\0')
+ {
+ *p = '\0';
+ break;
+ }
+
+ if (*s == '\\')
+ {
+ s++;
+
+ if (*s == '\0')
+ {
+ *p = '\0';
+ break;
+ }
+
+ switch (*s)
+ {
+ case 's':
+ *p++ = ' ';
+ break;
+ case 'n':
+ *p++ = '\n';
+ break;
+ case 't':
+ *p++ = '\t';
+ break;
+ case 'r':
+ *p++ = '\r';
+ break;
+ case ';':
+ p = stpcpy (p, "\\;");
+ break;
+ default:
+ *p++ = *s;
+ break;
+ }
+ }
+ else
+ *p++ = *s;
+ }
+ return buffer;
+}
+
+void
+desktop_add_keyword (hash_table *keywords, const char *name, bool is_list)
+{
+ hash_insert_entry (keywords, name, strlen (name), (void *) is_list);
+}
+
+void
+desktop_add_default_keywords (hash_table *keywords)
+{
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ desktop_add_keyword (keywords, "Name", false);
+ desktop_add_keyword (keywords, "GenericName", false);
+ desktop_add_keyword (keywords, "Comment", false);
+ desktop_add_keyword (keywords, "Icon", false);
+ desktop_add_keyword (keywords, "Keywords", true);
+}
diff --git a/gettext-tools/src/read-desktop.h b/gettext-tools/src/read-desktop.h
new file mode 100644
index 0000000..19ad8d8
--- /dev/null
+++ b/gettext-tools/src/read-desktop.h
@@ -0,0 +1,121 @@
+/* Reading Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 Free Software Foundation, Inc.
+ This file was written by Daiki Ueno <ueno@gnu.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_DESKTOP_H
+#define _READ_DESKTOP_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "hash.h"
+#include "po-lex.h"
+#include "str-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Forward declaration. */
+struct desktop_reader_ty;
+
+
+/* This first structure, playing the role of the "Class" in OO sense,
+ contains pointers to functions. Each function is a method for the
+ class (base or derived). Use a NULL pointer where no action is
+ required. */
+
+typedef struct desktop_reader_class_ty desktop_reader_class_ty;
+struct desktop_reader_class_ty
+{
+ /* how many bytes to malloc for an instance of this class */
+ size_t size;
+
+ /* what to do immediately after the instance is malloc()ed */
+ void (*constructor) (struct desktop_reader_ty *pop);
+
+ /* what to do immediately before the instance is free()ed */
+ void (*destructor) (struct desktop_reader_ty *pop);
+
+ /* what to do with a group header */
+ void (*handle_group) (struct desktop_reader_ty *pop,
+ const char *group);
+
+ /* what to do with a key/value pair */
+ void (*handle_pair) (struct desktop_reader_ty *pop,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value);
+
+ /* what to do with a comment */
+ void (*handle_comment) (struct desktop_reader_ty *pop, const char *s);
+
+ /* what to do with a blank line */
+ void (*handle_blank) (struct desktop_reader_ty *pop, const char *s);
+};
+
+/* This next structure defines the base class passed to the methods.
+ Derived methods will often need to cast their first argument before
+ using it (this corresponds to the implicit 'this' argument in C++).
+
+ When declaring derived classes, use the DESKTOP_READER_TY define
+ at the start of the structure, to declare inherited instance variables,
+ etc. */
+
+#define DESKTOP_READER_TY \
+ desktop_reader_class_ty *methods;
+
+typedef struct desktop_reader_ty desktop_reader_ty;
+struct desktop_reader_ty
+{
+ DESKTOP_READER_TY
+};
+
+desktop_reader_ty *desktop_reader_alloc (desktop_reader_class_ty *methods);
+void desktop_reader_free (desktop_reader_ty *reader);
+
+void desktop_reader_handle_group (desktop_reader_ty *reader,
+ const char *group);
+
+void desktop_reader_handle_pair (desktop_reader_ty *reader,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value);
+
+void desktop_reader_handle_comment (desktop_reader_ty *reader,
+ const char *s);
+
+void desktop_reader_handle_blank (desktop_reader_ty *reader,
+ const char *s);
+
+
+void desktop_parse (desktop_reader_ty *reader, FILE *file,
+ const char *real_filename, const char *logical_filename);
+
+
+char *desktop_escape_string (const char *s, bool is_list);
+char *desktop_unescape_string (const char *s, bool is_list);
+
+void desktop_add_keyword (hash_table *keywords, const char *name, bool is_list);
+void desktop_add_default_keywords (hash_table *keywords);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _READ_DESKTOP_H */
diff --git a/gettext-tools/src/read-java.c b/gettext-tools/src/read-java.c
new file mode 100644
index 0000000..bc6f2f4
--- /dev/null
+++ b/gettext-tools/src/read-java.c
@@ -0,0 +1,138 @@
+/* Reading Java ResourceBundles.
+ Copyright (C) 2001-2003, 2006-2008, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-java.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "msgunfmt.h"
+#include "relocatable.h"
+#include "javaexec.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "error.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* A Java resource name can only be manipulated by a Java virtual machine.
+ So we start a JVM to execute the DumpResource program, and read its
+ output, which is .po format without comments. */
+
+struct locals
+{
+ /* OUT */
+ msgdomain_list_ty *mdlp;
+};
+
+static bool
+execute_and_read_po_output (const char *progname,
+ const char *prog_path, char **prog_argv,
+ void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ int exitstatus;
+
+ /* Open a pipe to the JVM. */
+ child = create_pipe_in (progname, prog_path, prog_argv, DEV_NULL, false,
+ true, true, fd);
+
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+ /* Read the message list. */
+ l->mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po);
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, progname, false, false, true, true, NULL);
+ if (exitstatus != 0)
+ error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+ progname, exitstatus);
+
+ return false;
+}
+
+
+msgdomain_list_ty *
+msgdomain_read_java (const char *resource_name, const char *locale_name)
+{
+ const char *class_name = "gnu.gettext.DumpResource";
+ const char *gettextjexedir;
+ const char *gettextjar;
+ const char *args[3];
+ struct locals locals;
+
+#if USEJEXE
+ /* Make it possible to override the executable's location. This is
+ necessary for running the testsuite before "make install". */
+ gettextjexedir = getenv ("GETTEXTJEXEDIR");
+ if (gettextjexedir == NULL || gettextjexedir[0] == '\0')
+ gettextjexedir = relocate (GETTEXTJEXEDIR);
+#else
+ gettextjexedir = NULL;
+#endif
+
+ /* Make it possible to override the gettext.jar location. This is
+ necessary for running the testsuite before "make install". */
+ gettextjar = getenv ("GETTEXTJAR");
+ if (gettextjar == NULL || gettextjar[0] == '\0')
+ gettextjar = relocate (GETTEXTJAR);
+
+ /* Assign a default value to the resource name. */
+ if (resource_name == NULL)
+ resource_name = "Messages";
+
+ /* Prepare arguments. */
+ args[0] = resource_name;
+ if (locale_name != NULL)
+ {
+ args[1] = locale_name;
+ args[2] = NULL;
+ }
+ else
+ args[1] = NULL;
+
+ /* Dump the resource and retrieve the resulting output.
+ Here we use the user's CLASSPATH, not a minimal one, so that the
+ resource can be found. */
+ if (execute_java_class (class_name, &gettextjar, 1, false, gettextjexedir,
+ args,
+ verbose, false,
+ execute_and_read_po_output, &locals))
+ /* An error message should already have been provided. */
+ exit (EXIT_FAILURE);
+
+ return locals.mdlp;
+}
diff --git a/gettext-tools/src/read-java.h b/gettext-tools/src/read-java.h
new file mode 100644
index 0000000..6b4d037
--- /dev/null
+++ b/gettext-tools/src/read-java.h
@@ -0,0 +1,29 @@
+/* Reading Java ResourceBundles.
+ Copyright (C) 2001-2002 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_JAVA_H
+#define _READ_JAVA_H
+
+#include "message.h"
+
+/* Read the Java resource given by resource_name and locale_name.
+ Returns a list of messages. */
+extern msgdomain_list_ty *
+ msgdomain_read_java (const char *resource_name,
+ const char *locale_name);
+
+#endif /* _READ_JAVA_H */
diff --git a/gettext-tools/src/read-mo.c b/gettext-tools/src/read-mo.c
new file mode 100644
index 0000000..b97bbad
--- /dev/null
+++ b/gettext-tools/src/read-mo.c
@@ -0,0 +1,467 @@
+/* Reading binary .mo files.
+ Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-mo.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* This include file describes the main part of binary .mo format. */
+#include "gmo.h"
+
+#include "error.h"
+#include "xalloc.h"
+#include "binary-io.h"
+#include "message.h"
+#include "format.h"
+#include "gettext.h"
+#include "xsize.h"
+
+#define _(str) gettext (str)
+
+
+enum mo_endianness
+{
+ MO_LITTLE_ENDIAN,
+ MO_BIG_ENDIAN
+};
+
+/* We read the file completely into memory. This is more efficient than
+ lots of lseek(). This struct represents the .mo file in memory. */
+struct binary_mo_file
+{
+ const char *filename;
+ char *data;
+ size_t size;
+ enum mo_endianness endian;
+};
+
+
+/* Read the contents of the given input stream. */
+static void
+read_binary_mo_file (struct binary_mo_file *bfp,
+ FILE *fp, const char *filename)
+{
+ char *buf = NULL;
+ size_t alloc = 0;
+ size_t size = 0;
+ size_t count;
+
+ while (!feof (fp))
+ {
+ const size_t increment = 4096;
+ if (size + increment > alloc)
+ {
+ alloc = alloc + alloc / 2;
+ if (alloc < size + increment)
+ alloc = size + increment;
+ buf = (char *) xrealloc (buf, alloc);
+ }
+ count = fread (buf + size, 1, increment, fp);
+ if (count == 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ filename);
+ }
+ else
+ size += count;
+ }
+ buf = (char *) xrealloc (buf, size);
+ bfp->filename = filename;
+ bfp->data = buf;
+ bfp->size = size;
+}
+
+/* Get a 32-bit number from the file, at the given file position. */
+static nls_uint32
+get_uint32 (const struct binary_mo_file *bfp, size_t offset)
+{
+ nls_uint32 b0, b1, b2, b3;
+ size_t end = xsum (offset, 4);
+
+ if (size_overflow_p (end) || end > bfp->size)
+ error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+
+ b0 = *(unsigned char *) (bfp->data + offset + 0);
+ b1 = *(unsigned char *) (bfp->data + offset + 1);
+ b2 = *(unsigned char *) (bfp->data + offset + 2);
+ b3 = *(unsigned char *) (bfp->data + offset + 3);
+ if (bfp->endian == MO_LITTLE_ENDIAN)
+ return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
+ else
+ return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+}
+
+/* Get a static string from the file, at the given file position. */
+static char *
+get_string (const struct binary_mo_file *bfp, size_t offset, size_t *lengthp)
+{
+ /* See 'struct string_desc'. */
+ nls_uint32 s_length = get_uint32 (bfp, offset);
+ nls_uint32 s_offset = get_uint32 (bfp, offset + 4);
+ size_t s_end = xsum3 (s_offset, s_length, 1);
+
+ if (size_overflow_p (s_end) || s_end > bfp->size)
+ error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+ if (bfp->data[s_offset + s_length] != '\0')
+ error (EXIT_FAILURE, 0,
+ _("file \"%s\" contains a not NUL terminated string"),
+ bfp->filename);
+
+ *lengthp = s_length + 1;
+ return bfp->data + s_offset;
+}
+
+/* Get a system dependent string from the file, at the given file position. */
+static char *
+get_sysdep_string (const struct binary_mo_file *bfp, size_t offset,
+ const struct mo_file_header *header, size_t *lengthp)
+{
+ /* See 'struct sysdep_string'. */
+ size_t length;
+ char *string;
+ size_t i;
+ char *p;
+ nls_uint32 s_offset;
+
+ /* Compute the length. */
+ length = 0;
+ for (i = 4; ; i += 8)
+ {
+ nls_uint32 segsize = get_uint32 (bfp, offset + i);
+ nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4);
+ nls_uint32 sysdep_segment_offset;
+ nls_uint32 ss_length;
+ nls_uint32 ss_offset;
+ size_t ss_end;
+ size_t n;
+
+ length += segsize;
+
+ if (sysdepref == SEGMENTS_END)
+ break;
+ if (sysdepref >= header->n_sysdep_segments)
+ /* Invalid. */
+ error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
+ bfp->filename);
+ /* See 'struct sysdep_segment'. */
+ sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8;
+ ss_length = get_uint32 (bfp, sysdep_segment_offset);
+ ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4);
+ ss_end = xsum (ss_offset, ss_length);
+ if (size_overflow_p (ss_end) || ss_end > bfp->size)
+ error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+ if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0'))
+ {
+ char location[30];
+ sprintf (location, "sysdep_segment[%u]", (unsigned int) sysdepref);
+ error (EXIT_FAILURE, 0,
+ _("file \"%s\" contains a not NUL terminated string, at %s"),
+ bfp->filename, location);
+ }
+ n = strlen (bfp->data + ss_offset);
+ length += (n > 1 ? 1 + n + 1 : n);
+ }
+
+ /* Allocate and fill the string. */
+ string = XNMALLOC (length, char);
+ p = string;
+ s_offset = get_uint32 (bfp, offset);
+ for (i = 4; ; i += 8)
+ {
+ nls_uint32 segsize = get_uint32 (bfp, offset + i);
+ nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4);
+ nls_uint32 sysdep_segment_offset;
+ nls_uint32 ss_length;
+ nls_uint32 ss_offset;
+ size_t s_end = xsum (s_offset, segsize);
+ size_t n;
+
+ if (size_overflow_p (s_end) || s_end > bfp->size)
+ error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+ memcpy (p, bfp->data + s_offset, segsize);
+ p += segsize;
+ s_offset += segsize;
+
+ if (sysdepref == SEGMENTS_END)
+ break;
+ if (sysdepref >= header->n_sysdep_segments)
+ abort ();
+ /* See 'struct sysdep_segment'. */
+ sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8;
+ ss_length = get_uint32 (bfp, sysdep_segment_offset);
+ ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4);
+ if (ss_offset + ss_length > bfp->size)
+ abort ();
+ if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0'))
+ abort ();
+ n = strlen (bfp->data + ss_offset);
+ if (n > 1)
+ *p++ = '<';
+ memcpy (p, bfp->data + ss_offset, n);
+ p += n;
+ if (n > 1)
+ *p++ = '>';
+ }
+
+ if (p != string + length)
+ abort ();
+
+ *lengthp = length;
+ return string;
+}
+
+/* Reads an existing .mo file and adds the messages to mlp. */
+void
+read_mo_file (message_list_ty *mlp, const char *filename)
+{
+ FILE *fp;
+ struct binary_mo_file bf;
+ struct mo_file_header header;
+ unsigned int i;
+ static lex_pos_ty pos = { __FILE__, __LINE__ };
+
+ if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
+ {
+ fp = stdin;
+ SET_BINARY (fileno (fp));
+ }
+ else
+ {
+ fp = fopen (filename, "rb");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno,
+ _("error while opening \"%s\" for reading"), filename);
+ }
+
+ /* Read the file contents into memory. */
+ read_binary_mo_file (&bf, fp, filename);
+
+ /* Get a 32-bit number from the file header. */
+# define GET_HEADER_FIELD(field) \
+ get_uint32 (&bf, offsetof (struct mo_file_header, field))
+
+ /* We must grope the file to determine which endian it is.
+ Perversity of the universe tends towards maximum, so it will
+ probably not match the currently executing architecture. */
+ bf.endian = MO_BIG_ENDIAN;
+ header.magic = GET_HEADER_FIELD (magic);
+ if (header.magic != _MAGIC)
+ {
+ bf.endian = MO_LITTLE_ENDIAN;
+ header.magic = GET_HEADER_FIELD (magic);
+ if (header.magic != _MAGIC)
+ {
+ unrecognised:
+ error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
+ filename);
+ }
+ }
+
+ header.revision = GET_HEADER_FIELD (revision);
+
+ /* We support only the major revisions 0 and 1. */
+ switch (header.revision >> 16)
+ {
+ case 0:
+ case 1:
+ /* Fill the header parts that apply to major revisions 0 and 1. */
+ header.nstrings = GET_HEADER_FIELD (nstrings);
+ header.orig_tab_offset = GET_HEADER_FIELD (orig_tab_offset);
+ header.trans_tab_offset = GET_HEADER_FIELD (trans_tab_offset);
+ header.hash_tab_size = GET_HEADER_FIELD (hash_tab_size);
+ header.hash_tab_offset = GET_HEADER_FIELD (hash_tab_offset);
+
+ for (i = 0; i < header.nstrings; i++)
+ {
+ message_ty *mp;
+ char *msgctxt;
+ char *msgid;
+ size_t msgid_len;
+ char *separator;
+ char *msgstr;
+ size_t msgstr_len;
+
+ /* Read the msgctxt and msgid. */
+ msgid = get_string (&bf, header.orig_tab_offset + i * 8,
+ &msgid_len);
+ /* Split into msgctxt and msgid. */
+ separator = strchr (msgid, MSGCTXT_SEPARATOR);
+ if (separator != NULL)
+ {
+ /* The part before the MSGCTXT_SEPARATOR is the msgctxt. */
+ *separator = '\0';
+ msgctxt = msgid;
+ msgid = separator + 1;
+ msgid_len -= msgid - msgctxt;
+ }
+ else
+ msgctxt = NULL;
+
+ /* Read the msgstr. */
+ msgstr = get_string (&bf, header.trans_tab_offset + i * 8,
+ &msgstr_len);
+
+ mp = message_alloc (msgctxt,
+ msgid,
+ (strlen (msgid) + 1 < msgid_len
+ ? msgid + strlen (msgid) + 1
+ : NULL),
+ msgstr, msgstr_len,
+ &pos);
+ message_list_append (mlp, mp);
+ }
+
+ switch (header.revision & 0xffff)
+ {
+ case 0:
+ break;
+ case 1:
+ default:
+ /* Fill the header parts that apply to minor revision >= 1. */
+ header.n_sysdep_segments = GET_HEADER_FIELD (n_sysdep_segments);
+ header.sysdep_segments_offset =
+ GET_HEADER_FIELD (sysdep_segments_offset);
+ header.n_sysdep_strings = GET_HEADER_FIELD (n_sysdep_strings);
+ header.orig_sysdep_tab_offset =
+ GET_HEADER_FIELD (orig_sysdep_tab_offset);
+ header.trans_sysdep_tab_offset =
+ GET_HEADER_FIELD (trans_sysdep_tab_offset);
+
+ for (i = 0; i < header.n_sysdep_strings; i++)
+ {
+ message_ty *mp;
+ char *msgctxt;
+ char *msgid;
+ size_t msgid_len;
+ char *separator;
+ char *msgstr;
+ size_t msgstr_len;
+ nls_uint32 offset;
+ size_t f;
+
+ /* Read the msgctxt and msgid. */
+ offset = get_uint32 (&bf, header.orig_sysdep_tab_offset + i * 4);
+ msgid = get_sysdep_string (&bf, offset, &header, &msgid_len);
+ /* Split into msgctxt and msgid. */
+ separator = strchr (msgid, MSGCTXT_SEPARATOR);
+ if (separator != NULL)
+ {
+ /* The part before the MSGCTXT_SEPARATOR is the msgctxt. */
+ *separator = '\0';
+ msgctxt = msgid;
+ msgid = separator + 1;
+ msgid_len -= msgid - msgctxt;
+ }
+ else
+ msgctxt = NULL;
+
+ /* Read the msgstr. */
+ offset = get_uint32 (&bf, header.trans_sysdep_tab_offset + i * 4);
+ msgstr = get_sysdep_string (&bf, offset, &header, &msgstr_len);
+
+ mp = message_alloc (msgctxt,
+ msgid,
+ (strlen (msgid) + 1 < msgid_len
+ ? msgid + strlen (msgid) + 1
+ : NULL),
+ msgstr, msgstr_len,
+ &pos);
+
+ /* Only messages with c-format or objc-format annotation are
+ recognized as having system-dependent strings by msgfmt.
+ Which one of the two, we don't know. We have to guess,
+ assuming that c-format is more probable than objc-format and
+ that the .mo was likely produced by "msgfmt -c". */
+ for (f = format_c; ; f = format_objc)
+ {
+ bool valid = true;
+ struct formatstring_parser *parser = formatstring_parsers[f];
+ const char *str_end;
+ const char *str;
+
+ str_end = msgid + msgid_len;
+ for (str = msgid; str < str_end; str += strlen (str) + 1)
+ {
+ char *invalid_reason = NULL;
+ void *descr =
+ parser->parse (str, false, NULL, &invalid_reason);
+
+ if (descr != NULL)
+ parser->free (descr);
+ else
+ {
+ free (invalid_reason);
+ valid = false;
+ break;
+ }
+ }
+ if (valid)
+ {
+ str_end = msgstr + msgstr_len;
+ for (str = msgstr; str < str_end; str += strlen (str) + 1)
+ {
+ char *invalid_reason = NULL;
+ void *descr =
+ parser->parse (str, true, NULL, &invalid_reason);
+
+ if (descr != NULL)
+ parser->free (descr);
+ else
+ {
+ free (invalid_reason);
+ valid = false;
+ break;
+ }
+ }
+ }
+
+ if (valid)
+ {
+ /* Found the most likely among c-format, objc-format. */
+ mp->is_format[f] = yes;
+ break;
+ }
+
+ /* Try next f. */
+ if (f == format_objc)
+ break;
+ }
+
+ message_list_append (mlp, mp);
+ }
+ break;
+ }
+ break;
+
+ default:
+ goto unrecognised;
+ }
+
+ if (fp != stdin)
+ fclose (fp);
+}
diff --git a/gettext-tools/src/read-mo.h b/gettext-tools/src/read-mo.h
new file mode 100644
index 0000000..c8e976c
--- /dev/null
+++ b/gettext-tools/src/read-mo.h
@@ -0,0 +1,26 @@
+/* Reading binary .mo files.
+ Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_MO_H
+#define _READ_MO_H
+
+#include "message.h"
+
+/* Reads an existing .mo file and adds the messages to mlp. */
+extern void read_mo_file (message_list_ty *mlp, const char *filename);
+
+#endif /* _READ_MO_H */
diff --git a/gettext-tools/src/read-po.c b/gettext-tools/src/read-po.c
new file mode 100644
index 0000000..04f1b74
--- /dev/null
+++ b/gettext-tools/src/read-po.c
@@ -0,0 +1,48 @@
+/* Reading PO files.
+ Copyright (C) 1995-1996, 1998, 2000-2006 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-po.h"
+
+#include "po-lex.h"
+#include "po-gram.h"
+
+/* For compiling this file in C++ mode. */
+#ifdef __cplusplus
+# define this thiss
+#endif
+
+
+/* Read a .po / .pot file from a stream, and dispatch to the various
+ abstract_catalog_reader_class_ty methods. */
+static void
+po_parse (abstract_catalog_reader_ty *this, FILE *fp,
+ const char *real_filename, const char *logical_filename)
+{
+ lex_start (fp, real_filename, logical_filename);
+ po_gram_parse ();
+ lex_end ();
+}
+
+const struct catalog_input_format input_format_po =
+{
+ po_parse, /* parse */
+ false /* produces_utf8 */
+};
diff --git a/gettext-tools/src/read-po.h b/gettext-tools/src/read-po.h
new file mode 100644
index 0000000..e426eaa
--- /dev/null
+++ b/gettext-tools/src/read-po.h
@@ -0,0 +1,26 @@
+/* Reading PO files.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_PO_H
+#define _READ_PO_H
+
+#include "read-catalog-abstract.h"
+
+/* Describes a .po / .pot file parser. */
+extern DLL_VARIABLE const struct catalog_input_format input_format_po;
+
+#endif /* _READ_PO_H */
diff --git a/gettext-tools/src/read-properties.c b/gettext-tools/src/read-properties.c
new file mode 100644
index 0000000..0c64730
--- /dev/null
+++ b/gettext-tools/src/read-properties.c
@@ -0,0 +1,560 @@
+/* Reading Java .properties files.
+ Copyright (C) 2003, 2005-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-properties.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error-progname.h"
+#include "message.h"
+#include "read-catalog-abstract.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "msgl-ascii.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* For compiling this file in C++ mode. */
+#ifdef __cplusplus
+# define this thiss
+#endif
+
+
+/* The format of the Java .properties files is documented in the JDK
+ documentation for class java.util.Properties. In the case of .properties
+ files for PropertyResourceBundle, each non-comment line contains a
+ key/value pair in the form "key = value" or "key : value" or "key value",
+ where the key is the msgid and the value is the msgstr. Messages with
+ plurals are not supported in this format. */
+
+/* Handling of comments: We copy all comments from the .properties file to
+ the PO file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .properties file. */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* File name and line number. */
+extern lex_pos_ty gram_pos;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Phase 1: Read an ISO-8859-1 character.
+ Max. 1 pushback character. */
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while reading \"%s\""),
+ real_file_name),
+ errno_description));
+ }
+ return EOF;
+ }
+
+ return c;
+}
+
+static inline void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ ungetc (c, fp);
+}
+
+
+/* Phase 2: Read an ISO-8859-1 character, treating CR/LF like a single LF.
+ Max. 2 pushback characters. */
+
+static unsigned char phase2_pushback[2];
+static int phase2_pushback_length;
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ c = phase2_pushback[--phase2_pushback_length];
+ else
+ {
+ c = phase1_getc ();
+
+ if (c == '\r')
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '\n')
+ c = c2;
+ else
+ phase1_ungetc (c2);
+ }
+ }
+
+ if (c == '\n')
+ gram_pos.line_number++;
+
+ return c;
+}
+
+static void
+phase2_ungetc (int c)
+{
+ if (c == '\n')
+ --gram_pos.line_number;
+ if (c != EOF)
+ phase2_pushback[phase2_pushback_length++] = c;
+}
+
+
+/* Phase 3: Read an ISO-8859-1 character, treating CR/LF like a single LF,
+ with handling of continuation lines.
+ Max. 1 pushback character. */
+
+static int
+phase3_getc ()
+{
+ int c = phase2_getc ();
+
+ for (;;)
+ {
+ if (c != '\\')
+ return c;
+
+ c = phase2_getc ();
+ if (c != '\n')
+ {
+ phase2_ungetc (c);
+ return '\\';
+ }
+
+ /* Skip the backslash-newline and all whitespace that follows it. */
+ do
+ c = phase2_getc ();
+ while (c == ' ' || c == '\t' || c == '\r' || c == '\f');
+ }
+}
+
+static inline void
+phase3_ungetc (int c)
+{
+ phase2_ungetc (c);
+}
+
+
+/* Phase 4: Read an UTF-16 codepoint, treating CR/LF like a single LF,
+ with handling of continuation lines and of \uxxxx sequences. */
+
+static int
+phase4_getuc ()
+{
+ int c = phase3_getc ();
+
+ if (c == EOF)
+ return -1;
+ if (c == '\\')
+ {
+ int c2 = phase3_getc ();
+
+ if (c2 == 't')
+ return '\t';
+ if (c2 == 'n')
+ return '\n';
+ if (c2 == 'r')
+ return '\r';
+ if (c2 == 'f')
+ return '\f';
+ if (c2 == 'u')
+ {
+ unsigned int n = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ int c1 = phase3_getc ();
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ {
+ phase3_ungetc (c1);
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1),
+ false, _("warning: invalid \\uxxxx syntax for Unicode character"));
+ return 'u';
+ }
+ }
+ return n;
+ }
+
+ return c2;
+ }
+ else
+ return c;
+}
+
+
+/* Converts a string from ISO-8859-1 encoding to UTF-8 encoding. */
+static char *
+conv_from_iso_8859_1 (char *string)
+{
+ if (is_ascii_string (string))
+ return string;
+ else
+ {
+ size_t length = strlen (string);
+ /* Each ISO-8859-1 character needs 2 bytes at worst. */
+ unsigned char *utf8_string = XNMALLOC (2 * length + 1, unsigned char);
+ unsigned char *q = utf8_string;
+ const char *str = string;
+ const char *str_limit = str + length;
+
+ while (str < str_limit)
+ {
+ unsigned int uc = (unsigned char) *str++;
+ int n = u8_uctomb (q, uc, 6);
+ assert (n > 0);
+ q += n;
+ }
+ *q = '\0';
+ assert (q - utf8_string <= 2 * length);
+
+ return (char *) utf8_string;
+ }
+}
+
+
+/* Converts a string from JAVA encoding (with \uxxxx sequences) to UTF-8
+ encoding. May destructively modify the argument string. */
+static char *
+conv_from_java (char *string)
+{
+ /* This conversion can only shrink the string, never increase its size.
+ So there is no need to xmalloc the result freshly. */
+ const char *p = string;
+ unsigned char *q = (unsigned char *) string;
+
+ while (*p != '\0')
+ {
+ if (p[0] == '\\' && p[1] == 'u')
+ {
+ unsigned int n = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ int c1 = (unsigned char) p[2 + i];
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ goto just_one_byte;
+ }
+
+ if (i == 4)
+ {
+ unsigned int uc;
+
+ if (n >= 0xd800 && n < 0xdc00)
+ {
+ if (p[6] == '\\' && p[7] == 'u')
+ {
+ unsigned int m = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ int c1 = (unsigned char) p[8 + i];
+
+ if (c1 >= '0' && c1 <= '9')
+ m = (m << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ m = (m << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ m = (m << 4) + (c1 - 'a' + 10);
+ else
+ goto just_one_byte;
+ }
+
+ if (i == 4 && (m >= 0xdc00 && m < 0xe000))
+ {
+ /* Combine two UTF-16 words to a character. */
+ uc = 0x10000 + ((n - 0xd800) << 10) + (m - 0xdc00);
+ p += 12;
+ }
+ else
+ goto just_one_byte;
+ }
+ else
+ goto just_one_byte;
+ }
+ else
+ {
+ uc = n;
+ p += 6;
+ }
+
+ q += u8_uctomb (q, uc, 6);
+ continue;
+ }
+ }
+ just_one_byte:
+ *q++ = (unsigned char) *p++;
+ }
+ *q = '\0';
+ return string;
+}
+
+
+/* Reads a key or value string.
+ Returns the string in UTF-8 encoding, or NULL if the end of the logical
+ line is reached.
+ Parsing ends:
+ - when returning NULL, after the end of the logical line,
+ - otherwise, if in_key is true, after the whitespace and possibly the
+ separator that follows after the string,
+ - otherwise, if in_key is false, after the end of the logical line. */
+
+static char *
+read_escaped_string (bool in_key)
+{
+ static unsigned short *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+ int c;
+
+ /* Skip whitespace before the string. */
+ do
+ c = phase3_getc ();
+ while (c == ' ' || c == '\t' || c == '\r' || c == '\f');
+
+ if (c == EOF || c == '\n')
+ /* Empty string. */
+ return NULL;
+
+ /* Start accumulating the string. We store the string in UTF-16 before
+ converting it to UTF-8. Why not converting every character directly to
+ UTF-8? Because a string can contain surrogates like \uD800\uDF00, and
+ we must combine them to a single UTF-8 character. */
+ buflen = 0;
+ for (;;)
+ {
+ if (in_key && (c == '=' || c == ':'
+ || c == ' ' || c == '\t' || c == '\r' || c == '\f'))
+ {
+ /* Skip whitespace after the string. */
+ while (c == ' ' || c == '\t' || c == '\r' || c == '\f')
+ c = phase3_getc ();
+ /* Skip '=' or ':' separator. */
+ if (!(c == '=' || c == ':'))
+ phase3_ungetc (c);
+ break;
+ }
+
+ phase3_ungetc (c);
+
+ /* Read the next UTF-16 codepoint. */
+ c = phase4_getuc ();
+ if (c < 0)
+ break;
+ /* Append it to the buffer. */
+ if (buflen >= bufmax)
+ {
+ bufmax += 100;
+ buffer = xrealloc (buffer, bufmax * sizeof (unsigned short));
+ }
+ buffer[buflen++] = c;
+
+ c = phase3_getc ();
+ if (c == EOF || c == '\n')
+ {
+ if (in_key)
+ phase3_ungetc (c);
+ break;
+ }
+ }
+
+ /* Now convert from UTF-16 to UTF-8. */
+ {
+ size_t pos;
+ unsigned char *utf8_string;
+ unsigned char *q;
+
+ /* Each UTF-16 word needs 3 bytes at worst. */
+ utf8_string = XNMALLOC (3 * buflen + 1, unsigned char);
+ for (pos = 0, q = utf8_string; pos < buflen; )
+ {
+ ucs4_t uc;
+ int n;
+
+ pos += u16_mbtouc (&uc, buffer + pos, buflen - pos);
+ n = u8_uctomb (q, uc, 6);
+ assert (n > 0);
+ q += n;
+ }
+ *q = '\0';
+ assert (q - utf8_string <= 3 * buflen);
+
+ return (char *) utf8_string;
+ }
+}
+
+
+/* Read a .properties file from a stream, and dispatch to the various
+ abstract_catalog_reader_class_ty methods. */
+static void
+properties_parse (abstract_catalog_reader_ty *this, FILE *file,
+ const char *real_filename, const char *logical_filename)
+{
+ fp = file;
+ real_file_name = real_filename;
+ gram_pos.file_name = xstrdup (real_file_name);
+ gram_pos.line_number = 1;
+
+ for (;;)
+ {
+ int c;
+ bool comment;
+ bool hidden;
+
+ c = phase2_getc ();
+
+ if (c == EOF)
+ break;
+
+ comment = false;
+ hidden = false;
+ if (c == '#')
+ comment = true;
+ else if (c == '!')
+ {
+ /* For compatibility with write-properties.c, we treat '!' not
+ followed by space as a fuzzy or untranslated message. */
+ int c2 = phase2_getc ();
+ if (c2 == ' ' || c2 == '\n' || c2 == EOF)
+ comment = true;
+ else
+ hidden = true;
+ phase2_ungetc (c2);
+ }
+ else
+ phase2_ungetc (c);
+
+ if (comment)
+ {
+ /* A comment line. */
+ static char *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+
+ buflen = 0;
+ for (;;)
+ {
+ c = phase2_getc ();
+
+ if (buflen >= bufmax)
+ {
+ bufmax += 100;
+ buffer = xrealloc (buffer, bufmax);
+ }
+
+ if (c == EOF || c == '\n')
+ break;
+
+ buffer[buflen++] = c;
+ }
+ buffer[buflen] = '\0';
+
+ po_callback_comment_dispatcher (conv_from_java (conv_from_iso_8859_1 (buffer)));
+ }
+ else
+ {
+ /* A key/value pair. */
+ char *msgid;
+ lex_pos_ty msgid_pos;
+
+ msgid_pos = gram_pos;
+ msgid = read_escaped_string (true);
+ if (msgid == NULL)
+ /* Skip blank line. */
+ ;
+ else
+ {
+ char *msgstr;
+ lex_pos_ty msgstr_pos;
+ bool force_fuzzy;
+
+ msgstr_pos = gram_pos;
+ msgstr = read_escaped_string (false);
+ if (msgstr == NULL)
+ msgstr = xstrdup ("");
+
+ /* Be sure to make the message fuzzy if it was commented out
+ and if it is not already header/fuzzy/untranslated. */
+ force_fuzzy = (hidden && msgid[0] != '\0' && msgstr[0] != '\0');
+
+ po_callback_message (NULL, msgid, &msgid_pos, NULL,
+ msgstr, strlen (msgstr) + 1, &msgstr_pos,
+ NULL, NULL, NULL,
+ force_fuzzy, false);
+ }
+ }
+ }
+
+ fp = NULL;
+ real_file_name = NULL;
+ gram_pos.line_number = 0;
+}
+
+const struct catalog_input_format input_format_properties =
+{
+ properties_parse, /* parse */
+ true /* produces_utf8 */
+};
diff --git a/gettext-tools/src/read-properties.h b/gettext-tools/src/read-properties.h
new file mode 100644
index 0000000..8ac7852
--- /dev/null
+++ b/gettext-tools/src/read-properties.h
@@ -0,0 +1,26 @@
+/* Reading Java .properties files.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_PROPERTIES_H
+#define _READ_PROPERTIES_H
+
+#include "read-catalog-abstract.h"
+
+/* Describes a .properties file parser. */
+extern DLL_VARIABLE const struct catalog_input_format input_format_properties;
+
+#endif /* _READ_PROPERTIES_H */
diff --git a/gettext-tools/src/read-resources.c b/gettext-tools/src/read-resources.c
new file mode 100644
index 0000000..a5eb8eb
--- /dev/null
+++ b/gettext-tools/src/read-resources.c
@@ -0,0 +1,138 @@
+/* Reading C# .resources files.
+ Copyright (C) 2003, 2006-2008, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-resources.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "msgunfmt.h"
+#include "relocatable.h"
+#include "csharpexec.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "message.h"
+#include "concat-filename.h"
+#include "error.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* A .resources file has such a complex format that it's most easily read
+ through the C# class ResourceReader. So we start a C# process to execute
+ the DumpResource program, and read its output, which is .po format without
+ comments. */
+
+struct locals
+{
+ /* OUT */
+ msgdomain_list_ty *mdlp;
+};
+
+static bool
+execute_and_read_po_output (const char *progname,
+ const char *prog_path, char **prog_argv,
+ void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ int exitstatus;
+
+ /* Open a pipe to the C# execution engine. */
+ child = create_pipe_in (progname, prog_path, prog_argv, NULL, false,
+ true, true, fd);
+
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+ /* Read the message list. */
+ l->mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po);
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, progname, false, false, true, true, NULL);
+ if (exitstatus != 0)
+ error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+ progname, exitstatus);
+
+ return false;
+}
+
+
+void
+read_resources_file (message_list_ty *mlp, const char *filename)
+{
+ const char *args[2];
+ const char *gettextexedir;
+ const char *gettextlibdir;
+ char *assembly_path;
+ const char *libdirs[1];
+ struct locals locals;
+
+ /* Prepare arguments. */
+ args[0] = filename;
+ args[1] = NULL;
+
+ /* Make it possible to override the .exe location. This is
+ necessary for running the testsuite before "make install". */
+ gettextexedir = getenv ("GETTEXTCSHARPEXEDIR");
+ if (gettextexedir == NULL || gettextexedir[0] == '\0')
+ gettextexedir = relocate (LIBDIR "/gettext");
+
+ /* Make it possible to override the .dll location. This is
+ necessary for running the testsuite before "make install". */
+ gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR");
+ if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
+ gettextlibdir = relocate (LIBDIR);
+
+ /* Dump the resource and retrieve the resulting output. */
+ assembly_path =
+ xconcatenated_filename (gettextexedir, "msgunfmt.net", ".exe");
+ libdirs[0] = gettextlibdir;
+ if (execute_csharp_program (assembly_path, libdirs, 1,
+ args,
+ verbose, false,
+ execute_and_read_po_output, &locals))
+ /* An error message should already have been provided. */
+ exit (EXIT_FAILURE);
+
+ /* Add the output to mlp. */
+ {
+ message_list_ty *read_mlp = locals.mdlp->item[0]->messages;
+ size_t j;
+
+ for (j = 0; j < read_mlp->nitems; j++)
+ message_list_append (mlp, read_mlp->item[j]);
+ }
+
+ free (assembly_path);
+}
diff --git a/gettext-tools/src/read-resources.h b/gettext-tools/src/read-resources.h
new file mode 100644
index 0000000..b3c6076
--- /dev/null
+++ b/gettext-tools/src/read-resources.h
@@ -0,0 +1,27 @@
+/* Reading C# .resources files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_RESOURCES_H
+#define _READ_RESOURCES_H
+
+#include "message.h"
+
+/* Reads an existing binary C# .resources file and adds the messages
+ to mlp. */
+extern void read_resources_file (message_list_ty *mlp, const char *filename);
+
+#endif /* _READ_RESOURCES_H */
diff --git a/gettext-tools/src/read-stringtable.c b/gettext-tools/src/read-stringtable.c
new file mode 100644
index 0000000..27b8860
--- /dev/null
+++ b/gettext-tools/src/read-stringtable.c
@@ -0,0 +1,963 @@
+/* Reading NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003, 2005-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "read-stringtable.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error-progname.h"
+#include "read-catalog-abstract.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* The format of NeXTstep/GNUstep .strings files is documented in
+ gnustep-base-1.8.0/Tools/make_strings/Using.txt
+ and in the comments of method propertyListFromStringsFileFormat in
+ gnustep-base-1.8.0/Source/NSString.m
+ In summary, it's a Objective-C like file with pseudo-assignments of the form
+ "key" = "value";
+ where the key is the msgid and the value is the msgstr.
+
+ The implementation of the parser of .strings files is in
+ gnustep-base-1.8.0/Source/NSString.m
+ function GSPropertyListFromStringsFormat
+ (indirectly called from NSBundle's method localizedStringForKey).
+
+ A test case is in
+ gnustep-base-1.8.0/Testing/English.lproj/NXStringTable.example
+ */
+
+/* Handling of comments: We copy all comments from the .strings file to
+ the PO file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .strings file. */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* File name and line number. */
+extern lex_pos_ty gram_pos;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Phase 1: Read a byte.
+ Max. 4 pushback characters. */
+
+static unsigned char phase1_pushback[4];
+static int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ return phase1_pushback[--phase1_pushback_length];
+
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while reading \"%s\""),
+ real_file_name),
+ errno_description));
+ }
+ return EOF;
+ }
+
+ return c;
+}
+
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ phase1_pushback[phase1_pushback_length++] = c;
+}
+
+
+/* Phase 2: Read an UCS-4 character.
+ Max. 2 pushback characters. */
+
+/* End-of-file indicator for functions returning an UCS-4 character. */
+#define UEOF -1
+
+static int phase2_pushback[4];
+static int phase2_pushback_length;
+
+/* The input file can be in Unicode encoding (UCS-2BE, UCS-2LE, UTF-8, each
+ with a BOM!), or otherwise the locale-dependent default encoding is used.
+ Since we don't want to depend on the locale here, we use ISO-8859-1
+ instead. */
+enum enc
+{
+ enc_undetermined,
+ enc_ucs2be,
+ enc_ucs2le,
+ enc_utf8,
+ enc_iso8859_1
+};
+static enum enc encoding;
+
+static int
+phase2_getc ()
+{
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (encoding == enc_undetermined)
+ {
+ /* Determine the input file's encoding. */
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ {
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ else if (c0 == 0xfe && c1 == 0xff)
+ encoding = enc_ucs2be;
+ else if (c0 == 0xff && c1 == 0xfe)
+ encoding = enc_ucs2le;
+ else
+ {
+ int c2;
+
+ c2 = phase1_getc ();
+ if (c2 == EOF)
+ {
+ phase1_ungetc (c1);
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ else if (c0 == 0xef && c1 == 0xbb && c2 == 0xbf)
+ encoding = enc_utf8;
+ else
+ {
+ phase1_ungetc (c2);
+ phase1_ungetc (c1);
+ phase1_ungetc (c0);
+ encoding = enc_iso8859_1;
+ }
+ }
+ }
+
+ switch (encoding)
+ {
+ case enc_ucs2be:
+ /* Read an UCS-2BE encoded character. */
+ {
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ return UEOF;
+ return (c0 << 8) + c1;
+ }
+
+ case enc_ucs2le:
+ /* Read an UCS-2LE encoded character. */
+ {
+ int c0, c1;
+
+ c0 = phase1_getc ();
+ if (c0 == EOF)
+ return UEOF;
+ c1 = phase1_getc ();
+ if (c1 == EOF)
+ return UEOF;
+ return c0 + (c1 << 8);
+ }
+
+ case enc_utf8:
+ /* Read an UTF-8 encoded character. */
+ {
+ unsigned char buf[6];
+ unsigned int count;
+ int c;
+ ucs4_t uc;
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = c;
+ count = 1;
+
+ if (buf[0] >= 0xc0)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[1] = c;
+ count = 2;
+
+ if (buf[0] >= 0xe0
+ && ((buf[1] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[2] = c;
+ count = 3;
+
+ if (buf[0] >= 0xf0
+ && ((buf[2] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[3] = c;
+ count = 4;
+
+ if (buf[0] >= 0xf8
+ && ((buf[3] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[4] = c;
+ count = 5;
+
+ if (buf[0] >= 0xfc
+ && ((buf[4] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[5] = c;
+ count = 6;
+ }
+ }
+ }
+ }
+ }
+
+ u8_mbtouc (&uc, buf, count);
+ return uc;
+ }
+
+ case enc_iso8859_1:
+ /* Read an ISO-8859-1 encoded character. */
+ {
+ int c = phase1_getc ();
+
+ if (c == EOF)
+ return UEOF;
+ return c;
+ }
+
+ default:
+ abort ();
+ }
+}
+
+static void
+phase2_ungetc (int c)
+{
+ if (c != UEOF)
+ phase2_pushback[phase2_pushback_length++] = c;
+}
+
+
+/* Phase 3: Read an UCS-4 character, with line number handling. */
+
+static int
+phase3_getc ()
+{
+ int c = phase2_getc ();
+
+ if (c == '\n')
+ gram_pos.line_number++;
+
+ return c;
+}
+
+static void
+phase3_ungetc (int c)
+{
+ if (c == '\n')
+ --gram_pos.line_number;
+ phase2_ungetc (c);
+}
+
+
+/* Convert from UCS-4 to UTF-8. */
+static char *
+conv_from_ucs4 (const int *buffer, size_t buflen)
+{
+ unsigned char *utf8_string;
+ size_t pos;
+ unsigned char *q;
+
+ /* Each UCS-4 word needs 6 bytes at worst. */
+ utf8_string = XNMALLOC (6 * buflen + 1, unsigned char);
+
+ for (pos = 0, q = utf8_string; pos < buflen; )
+ {
+ unsigned int uc;
+ int n;
+
+ uc = buffer[pos++];
+ n = u8_uctomb (q, uc, 6);
+ assert (n > 0);
+ q += n;
+ }
+ *q = '\0';
+ assert (q - utf8_string <= 6 * buflen);
+
+ return (char *) utf8_string;
+}
+
+
+/* Parse a string enclosed in double-quotes. Input is UCS-4 encoded.
+ Return the string in UTF-8 encoding, or NULL if the input doesn't represent
+ a valid string enclosed in double-quotes. */
+static char *
+parse_escaped_string (const int *string, size_t length)
+{
+ static int *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+ const int *string_limit = string + length;
+ int c;
+
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c != '"')
+ return NULL;
+ buflen = 0;
+ for (;;)
+ {
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c == '"')
+ break;
+ if (c == '\\')
+ {
+ if (string == string_limit)
+ return NULL;
+ c = *string++;
+ if (c >= '0' && c <= '7')
+ {
+ unsigned int n = 0;
+ int j = 0;
+ for (;;)
+ {
+ n = n * 8 + (c - '0');
+ if (++j == 3)
+ break;
+ if (string == string_limit)
+ break;
+ c = *string;
+ if (!(c >= '0' && c <= '7'))
+ break;
+ string++;
+ }
+ c = n;
+ }
+ else if (c == 'u' || c == 'U')
+ {
+ unsigned int n = 0;
+ int j;
+ for (j = 0; j < 4; j++)
+ {
+ if (string == string_limit)
+ break;
+ c = *string;
+ if (c >= '0' && c <= '9')
+ n = n * 16 + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = n * 16 + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = n * 16 + (c - 'a' + 10);
+ else
+ break;
+ string++;
+ }
+ c = n;
+ }
+ else
+ switch (c)
+ {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 't': c = '\t'; break;
+ case 'r': c = '\r'; break;
+ case 'n': c = '\n'; break;
+ case 'v': c = '\v'; break;
+ case 'f': c = '\f'; break;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+
+ return conv_from_ucs4 (buffer, buflen);
+}
+
+
+/* Accumulating flag comments. */
+
+static char *special_comment;
+
+static inline void
+special_comment_reset ()
+{
+ if (special_comment != NULL)
+ free (special_comment);
+ special_comment = NULL;
+}
+
+static void
+special_comment_add (const char *flag)
+{
+ if (special_comment == NULL)
+ special_comment = xstrdup (flag);
+ else
+ {
+ size_t total_len = strlen (special_comment) + 2 + strlen (flag) + 1;
+ special_comment = xrealloc (special_comment, total_len);
+ strcat (special_comment, ", ");
+ strcat (special_comment, flag);
+ }
+}
+
+static inline void
+special_comment_finish ()
+{
+ if (special_comment != NULL)
+ {
+ po_callback_comment_special (special_comment);
+ free (special_comment);
+ special_comment = NULL;
+ }
+}
+
+
+/* Accumulating comments. */
+
+static int *buffer;
+static size_t bufmax;
+static size_t buflen;
+static bool next_is_obsolete;
+static bool next_is_fuzzy;
+static char *fuzzy_msgstr;
+static bool expect_fuzzy_msgstr_as_c_comment;
+static bool expect_fuzzy_msgstr_as_cxx_comment;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove, bool test_for_fuzzy_msgstr)
+{
+ char *line;
+
+ buflen -= chars_to_remove;
+ /* Drop trailing white space, but not EOLs. */
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+
+ /* At special positions we interpret a comment of the form
+ = "escaped string"
+ with an optional trailing semicolon as being the fuzzy msgstr, not a
+ regular comment. */
+ if (test_for_fuzzy_msgstr
+ && buflen > 2 && buffer[0] == '=' && buffer[1] == ' '
+ && (fuzzy_msgstr =
+ parse_escaped_string (buffer + 2,
+ buflen - (buffer[buflen - 1] == ';') - 2)))
+ return;
+
+ line = conv_from_ucs4 (buffer, buflen);
+
+ if (strcmp (line, "Flag: untranslated") == 0)
+ {
+ special_comment_add ("fuzzy");
+ next_is_fuzzy = true;
+ }
+ else if (strcmp (line, "Flag: unmatched") == 0)
+ next_is_obsolete = true;
+ else if (strlen (line) >= 6 && memcmp (line, "Flag: ", 6) == 0)
+ special_comment_add (line + 6);
+ else if (strlen (line) >= 9 && memcmp (line, "Comment: ", 9) == 0)
+ /* A comment extracted from the source. */
+ po_callback_comment_dot (line + 9);
+ else
+ {
+ char *last_colon;
+ unsigned long number;
+ char *endp;
+
+ if (strlen (line) >= 6 && memcmp (line, "File: ", 6) == 0
+ && (last_colon = strrchr (line + 6, ':')) != NULL
+ && *(last_colon + 1) != '\0'
+ && (number = strtoul (last_colon + 1, &endp, 10), *endp == '\0'))
+ {
+ /* A "File: <filename>:<number>" type comment. */
+ *last_colon = '\0';
+ po_callback_comment_filepos (line + 6, number);
+ }
+ else
+ po_callback_comment (line);
+ }
+}
+
+
+/* Phase 4: Replace each comment that is not inside a string with a space
+ character. */
+
+static int
+phase4_getc ()
+{
+ int c;
+
+ c = phase3_getc ();
+ if (c != '/')
+ return c;
+ c = phase3_getc ();
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ return '/';
+
+ case '*':
+ /* C style comment. */
+ {
+ bool last_was_star;
+ size_t trailing_stars;
+ bool seen_newline;
+
+ comment_start ();
+ last_was_star = false;
+ trailing_stars = 0;
+ seen_newline = false;
+ /* Drop additional stars at the beginning of the comment. */
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c != '*')
+ break;
+ last_was_star = true;
+ }
+ phase3_ungetc (c);
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ seen_newline = true;
+ comment_line_end (1, false);
+ comment_start ();
+ last_was_star = false;
+ trailing_stars = 0;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ trailing_stars++;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ /* Drop additional stars at the end of the comment. */
+ comment_line_end (trailing_stars + 1,
+ expect_fuzzy_msgstr_as_c_comment
+ && !seen_newline);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ trailing_stars = 0;
+ continue;
+ }
+ break;
+ }
+ return ' ';
+ }
+
+ case '/':
+ /* C++ style comment. */
+ comment_start ();
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == '\n' || c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end (0, expect_fuzzy_msgstr_as_cxx_comment);
+ return '\n';
+ }
+}
+
+static inline void
+phase4_ungetc (int c)
+{
+ phase3_ungetc (c);
+}
+
+
+/* Return true if a character is considered as whitespace. */
+static bool
+is_whitespace (int c)
+{
+ return (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f'
+ || c == '\b');
+}
+
+/* Return true if a character needs quoting, i.e. cannot be used in unquoted
+ tokens. */
+static bool
+is_quotable (int c)
+{
+ if ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ return false;
+ switch (c)
+ {
+ case '!': case '#': case '$': case '%': case '&': case '*':
+ case '+': case '-': case '.': case '/': case ':': case '?':
+ case '@': case '|': case '~': case '_': case '^':
+ return false;
+ default:
+ return true;
+ }
+}
+
+
+/* Read a key or value string.
+ Return the string in UTF-8 encoding, or NULL if no string is seen.
+ Return the start position of the string in *pos. */
+static char *
+read_string (lex_pos_ty *pos)
+{
+ static int *buffer;
+ static size_t bufmax;
+ static size_t buflen;
+ int c;
+
+ /* Skip whitespace before the string. */
+ do
+ c = phase4_getc ();
+ while (is_whitespace (c));
+
+ if (c == UEOF)
+ /* No more string. */
+ return NULL;
+
+ *pos = gram_pos;
+ buflen = 0;
+ if (c == '"')
+ {
+ /* Read a string enclosed in double-quotes. */
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UEOF || c == '"')
+ break;
+ if (c == '\\')
+ {
+ c = phase3_getc ();
+ if (c == UEOF)
+ break;
+ if (c >= '0' && c <= '7')
+ {
+ unsigned int n = 0;
+ int j = 0;
+ for (;;)
+ {
+ n = n * 8 + (c - '0');
+ if (++j == 3)
+ break;
+ c = phase3_getc ();
+ if (!(c >= '0' && c <= '7'))
+ {
+ phase3_ungetc (c);
+ break;
+ }
+ }
+ c = n;
+ }
+ else if (c == 'u' || c == 'U')
+ {
+ unsigned int n = 0;
+ int j;
+ for (j = 0; j < 4; j++)
+ {
+ c = phase3_getc ();
+ if (c >= '0' && c <= '9')
+ n = n * 16 + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = n * 16 + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = n * 16 + (c - 'a' + 10);
+ else
+ {
+ phase3_ungetc (c);
+ break;
+ }
+ }
+ c = n;
+ }
+ else
+ switch (c)
+ {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 't': c = '\t'; break;
+ case 'r': c = '\r'; break;
+ case 'n': c = '\n'; break;
+ case 'v': c = '\v'; break;
+ case 'f': c = '\f'; break;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+ if (c == UEOF)
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1), false,
+ _("warning: unterminated string"));
+ }
+ else
+ {
+ /* Read a token outside quotes. */
+ if (is_quotable (c))
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1), false,
+ _("warning: syntax error"));
+ for (; c != UEOF && !is_quotable (c); c = phase4_getc ())
+ {
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax * sizeof (int));
+ }
+ buffer[buflen++] = c;
+ }
+ }
+
+ return conv_from_ucs4 (buffer, buflen);
+}
+
+
+/* Read a .strings file from a stream, and dispatch to the various
+ abstract_catalog_reader_class_ty methods. */
+static void
+stringtable_parse (abstract_catalog_reader_ty *pop, FILE *file,
+ const char *real_filename, const char *logical_filename)
+{
+ fp = file;
+ real_file_name = real_filename;
+ gram_pos.file_name = xstrdup (real_file_name);
+ gram_pos.line_number = 1;
+ encoding = enc_undetermined;
+ expect_fuzzy_msgstr_as_c_comment = false;
+ expect_fuzzy_msgstr_as_cxx_comment = false;
+
+ for (;;)
+ {
+ char *msgid;
+ lex_pos_ty msgid_pos;
+ char *msgstr;
+ lex_pos_ty msgstr_pos;
+ int c;
+
+ /* Prepare for next msgid/msgstr pair. */
+ special_comment_reset ();
+ next_is_obsolete = false;
+ next_is_fuzzy = false;
+ fuzzy_msgstr = NULL;
+
+ /* Read the key and all the comments preceding it. */
+ msgid = read_string (&msgid_pos);
+ if (msgid == NULL)
+ break;
+
+ special_comment_finish ();
+
+ /* Skip whitespace. */
+ do
+ c = phase4_getc ();
+ while (is_whitespace (c));
+
+ /* Expect a '=' or ';'. */
+ if (c == UEOF)
+ {
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1), false,
+ _("warning: unterminated key/value pair"));
+ break;
+ }
+ if (c == ';')
+ {
+ /* "key"; is an abbreviation for "key"=""; and does not
+ necessarily designate an untranslated entry. */
+ msgstr = xstrdup ("");
+ msgstr_pos = msgid_pos;
+ po_callback_message (NULL, msgid, &msgid_pos, NULL,
+ msgstr, strlen (msgstr) + 1, &msgstr_pos,
+ NULL, NULL, NULL,
+ false, next_is_obsolete);
+ }
+ else if (c == '=')
+ {
+ /* Read the value. */
+ msgstr = read_string (&msgstr_pos);
+ if (msgstr == NULL)
+ {
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1),
+ false, _("warning: unterminated key/value pair"));
+ break;
+ }
+
+ /* Skip whitespace. But for fuzzy key/value pairs, look for the
+ tentative msgstr in the form of a C style comment. */
+ expect_fuzzy_msgstr_as_c_comment = next_is_fuzzy;
+ do
+ {
+ c = phase4_getc ();
+ if (fuzzy_msgstr != NULL)
+ expect_fuzzy_msgstr_as_c_comment = false;
+ }
+ while (is_whitespace (c));
+ expect_fuzzy_msgstr_as_c_comment = false;
+
+ /* Expect a ';'. */
+ if (c == ';')
+ {
+ /* But for fuzzy key/value pairs, look for the tentative msgstr
+ in the form of a C++ style comment. */
+ if (fuzzy_msgstr == NULL && next_is_fuzzy)
+ {
+ do
+ c = phase3_getc ();
+ while (c == ' ');
+ phase3_ungetc (c);
+
+ expect_fuzzy_msgstr_as_cxx_comment = true;
+ c = phase4_getc ();
+ phase4_ungetc (c);
+ expect_fuzzy_msgstr_as_cxx_comment = false;
+ }
+ if (fuzzy_msgstr != NULL && strcmp (msgstr, msgid) == 0)
+ msgstr = fuzzy_msgstr;
+
+ /* A key/value pair. */
+ po_callback_message (NULL, msgid, &msgid_pos, NULL,
+ msgstr, strlen (msgstr) + 1, &msgstr_pos,
+ NULL, NULL, NULL,
+ false, next_is_obsolete);
+ }
+ else
+ {
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1),
+ false, _("\
+warning: syntax error, expected ';' after string"));
+ break;
+ }
+ }
+ else
+ {
+ po_xerror (PO_SEVERITY_ERROR, NULL,
+ real_file_name, gram_pos.line_number, (size_t)(-1), false,
+ _("\
+warning: syntax error, expected '=' or ';' after string"));
+ break;
+ }
+ }
+
+ fp = NULL;
+ real_file_name = NULL;
+ gram_pos.line_number = 0;
+}
+
+const struct catalog_input_format input_format_stringtable =
+{
+ stringtable_parse, /* parse */
+ true /* produces_utf8 */
+};
diff --git a/gettext-tools/src/read-stringtable.h b/gettext-tools/src/read-stringtable.h
new file mode 100644
index 0000000..d254434
--- /dev/null
+++ b/gettext-tools/src/read-stringtable.h
@@ -0,0 +1,26 @@
+/* Reading NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_STRINGTABLE_H
+#define _READ_STRINGTABLE_H
+
+#include "read-catalog-abstract.h"
+
+/* Describes a .strings file parser. */
+extern DLL_VARIABLE const struct catalog_input_format input_format_stringtable;
+
+#endif /* _READ_STRINGTABLE_H */
diff --git a/gettext-tools/src/read-tcl.c b/gettext-tools/src/read-tcl.c
new file mode 100644
index 0000000..039dcfb
--- /dev/null
+++ b/gettext-tools/src/read-tcl.c
@@ -0,0 +1,157 @@
+/* Reading tcl/msgcat .msg files.
+ Copyright (C) 2002-2003, 2005-2008, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "read-tcl.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "msgunfmt.h"
+#include "relocatable.h"
+#include "concat-filename.h"
+#include "sh-quote.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "xmalloca.h"
+#include "error.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* A Tcl .msg file contains Tcl commands. It is best interpreted by Tcl
+ itself. But we redirect the msgcat::mcset function so that it passes
+ the msgid/msgstr pair to us, instead of storing it in the hash table. */
+
+msgdomain_list_ty *
+msgdomain_read_tcl (const char *locale_name, const char *directory)
+{
+ const char *gettextdatadir;
+ char *tclscript;
+ size_t len;
+ char *frobbed_locale_name;
+ char *p;
+ char *file_name;
+ char *argv[4];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ msgdomain_list_ty *mdlp;
+ int exitstatus;
+ size_t k;
+
+ /* Make it possible to override the msgunfmt.tcl location. This is
+ necessary for running the testsuite before "make install". */
+ gettextdatadir = getenv ("GETTEXTDATADIR");
+ if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
+ gettextdatadir = relocate (GETTEXTDATADIR);
+
+ tclscript = xconcatenated_filename (gettextdatadir, "msgunfmt.tcl", NULL);
+
+ /* Convert the locale name to lowercase and remove any encoding. */
+ len = strlen (locale_name);
+ frobbed_locale_name = (char *) xmalloca (len + 1);
+ memcpy (frobbed_locale_name, locale_name, len + 1);
+ for (p = frobbed_locale_name; *p != '\0'; p++)
+ if (*p >= 'A' && *p <= 'Z')
+ *p = *p - 'A' + 'a';
+ else if (*p == '.')
+ {
+ *p = '\0';
+ break;
+ }
+
+ file_name = xconcatenated_filename (directory, frobbed_locale_name, ".msg");
+
+ freea (frobbed_locale_name);
+
+ /* Prepare arguments. */
+ argv[0] = "tclsh";
+ argv[1] = tclscript;
+ argv[2] = file_name;
+ argv[3] = NULL;
+
+ if (verbose)
+ {
+ char *command = shell_quote_argv (argv);
+ printf ("%s\n", command);
+ free (command);
+ }
+
+ /* Open a pipe to the Tcl interpreter. */
+ child = create_pipe_in ("tclsh", "tclsh", argv, DEV_NULL, false, true, true,
+ fd);
+
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+ /* Read the message list. */
+ mdlp = read_catalog_stream (fp, "(pipe)", "(pipe)", &input_format_po);
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, "tclsh", false, false, true, true, NULL);
+ if (exitstatus != 0)
+ {
+ if (exitstatus == 2)
+ /* Special exitcode provided by msgunfmt.tcl. */
+ error (EXIT_FAILURE, ENOENT,
+ _("error while opening \"%s\" for reading"), file_name);
+ else
+ error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+ "tclsh", exitstatus);
+ }
+
+ free (tclscript);
+
+ /* Move the header entry to the beginning. */
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]))
+ {
+ /* Found the header entry. */
+ if (j > 0)
+ {
+ message_ty *header = mlp->item[j];
+ size_t i;
+
+ for (i = j; i > 0; i--)
+ mlp->item[i] = mlp->item[i - 1];
+ mlp->item[0] = header;
+ }
+ break;
+ }
+ }
+
+ return mdlp;
+}
diff --git a/gettext-tools/src/read-tcl.h b/gettext-tools/src/read-tcl.h
new file mode 100644
index 0000000..a2c2d9e
--- /dev/null
+++ b/gettext-tools/src/read-tcl.h
@@ -0,0 +1,28 @@
+/* Reading tcl/msgcat .msg files.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _READ_TCL_H
+#define _READ_TCL_H
+
+#include "message.h"
+
+/* Read the Tcl msg file given by locale_name and directory.
+ Returns a list of messages. */
+extern msgdomain_list_ty *
+ msgdomain_read_tcl (const char *locale_name, const char *directory);
+
+#endif /* _READ_TCL_H */
diff --git a/gettext-tools/src/recode-sr-latin.c b/gettext-tools/src/recode-sr-latin.c
new file mode 100644
index 0000000..25b88f6
--- /dev/null
+++ b/gettext-tools/src/recode-sr-latin.c
@@ -0,0 +1,395 @@
+/* Recode Serbian text from Cyrillic to Latin script.
+ Copyright (C) 2006-2007, 2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#if HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "closeout.h"
+#include "error.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "xalloc.h"
+#include "localcharset.h"
+#include "c-strcase.h"
+#include "xstriconv.h"
+#include "filters.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void process (FILE *stream);
+
+int
+main (int argc, char *argv[])
+{
+ /* Default values for command line options. */
+ bool do_help = false;
+ bool do_version = false;
+
+ int opt;
+
+ /* Set program name for message texts. */
+ set_program_name (argv[0]);
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Parse command line options. */
+ while ((opt = getopt_long (argc, argv, "hV", long_options, NULL)) != EOF)
+ switch (opt)
+ {
+ case '\0': /* Long option. */
+ break;
+ case 'h':
+ do_help = true;
+ break;
+ case 'V':
+ do_version = true;
+ break;
+ default:
+ usage (EXIT_FAILURE);
+ }
+
+ /* Version information is requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2006-2007");
+ printf (_("Written by %s and %s.\n"),
+ /* TRANSLATORS: This is a proper name. The last name is
+ (with Unicode escapes) "\u0160egan" or (with HTML entities)
+ "&Scaron;egan". */
+ proper_name_utf8 ("Danilo Segan", "Danilo \305\240egan"),
+ proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ if (argc - optind > 0)
+ error (EXIT_FAILURE, 0, _("too many arguments"));
+
+ process (stdin);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ /* xgettext: no-wrap */
+ printf (_("\
+Usage: %s [OPTION]\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Recode Serbian text from Cyrillic to Latin script.\n"));
+ /* xgettext: no-wrap */
+ printf (_("\
+The input text is read from standard input. The converted text is output to\n\
+standard output.\n"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Informative output:\n"));
+ /* xgettext: no-wrap */
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ /* xgettext: no-wrap */
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
+ }
+
+ exit (status);
+}
+
+
+/* Routines for reading a line.
+ Don't use routines that drop NUL bytes. Don't use getline(), because it
+ doesn't provide a good error message in case of memory allocation failure.
+ The gnulib module 'linebuffer' is nearly the right thing, except that we
+ don't want an extra newline at the end of file. */
+
+/* A 'struct linebuffer' holds a line of text. */
+
+struct linebuffer
+{
+ size_t size; /* Allocated. */
+ size_t length; /* Used. */
+ char *buffer;
+};
+
+/* Initialize linebuffer LINEBUFFER for use. */
+static inline void
+init_linebuffer (struct linebuffer *lb)
+{
+ lb->size = 0;
+ lb->length = 0;
+ lb->buffer = NULL;
+}
+
+/* Read an arbitrarily long line of text from STREAM into linebuffer LB.
+ Keep the newline. Do not NUL terminate.
+ Return LINEBUFFER, except at end of file return NULL. */
+static struct linebuffer *
+read_linebuffer (struct linebuffer *lb, FILE *stream)
+{
+ if (feof (stream))
+ return NULL;
+ else
+ {
+ char *p = lb->buffer;
+ char *end = lb->buffer + lb->size;
+
+ for (;;)
+ {
+ int c = getc (stream);
+ if (c == EOF)
+ {
+ if (p == lb->buffer || ferror (stream))
+ return NULL;
+ break;
+ }
+ if (p == end)
+ {
+ size_t oldsize = lb->size; /* = p - lb->buffer */
+ size_t newsize = 2 * oldsize + 40;
+ lb->buffer = (char *) xrealloc (lb->buffer, newsize);
+ lb->size = newsize;
+ p = lb->buffer + oldsize;
+ end = lb->buffer + newsize;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+
+ lb->length = p - lb->buffer;
+ return lb;
+ }
+}
+
+/* Free linebuffer LB and its data, all allocated with malloc. */
+static inline void
+destroy_linebuffer (struct linebuffer *lb)
+{
+ if (lb->buffer != NULL)
+ free (lb->buffer);
+}
+
+
+/* Process the input and produce the output. */
+static void
+process (FILE *stream)
+{
+ struct linebuffer lb;
+ const char *locale_code = locale_charset ();
+ bool need_code_conversion = (c_strcasecmp (locale_code, "UTF-8") != 0);
+#if HAVE_ICONV
+ iconv_t conv_to_utf8 = (iconv_t)(-1);
+ iconv_t conv_from_utf8 = (iconv_t)(-1);
+ char *last_utf8_line;
+ size_t last_utf8_line_len;
+ char *last_backconv_line;
+ size_t last_backconv_line_len;
+#endif
+
+ init_linebuffer (&lb);
+
+ /* Initialize the conversion descriptors. */
+ if (need_code_conversion)
+ {
+#if HAVE_ICONV
+ /* Avoid glibc-2.1 bug with EUC-KR. */
+# if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
+ && !defined _LIBICONV_VERSION
+ if (strcmp (locale_code, "EUC-KR") != 0)
+# endif
+ {
+ conv_to_utf8 = iconv_open ("UTF-8", locale_code);
+ /* TODO: Maybe append //TRANSLIT here? */
+ conv_from_utf8 = iconv_open (locale_code, "UTF-8");
+ }
+ if (conv_to_utf8 == (iconv_t)(-1))
+ error (EXIT_FAILURE, 0, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
+and iconv() does not support this conversion."),
+ locale_code, "UTF-8", basename (program_name));
+ if (conv_from_utf8 == (iconv_t)(-1))
+ error (EXIT_FAILURE, 0, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
+and iconv() does not support this conversion."),
+ "UTF-8", locale_code, basename (program_name));
+ last_utf8_line = NULL;
+ last_utf8_line_len = 0;
+ last_backconv_line = NULL;
+ last_backconv_line_len = 0;
+#else
+ error (EXIT_FAILURE, 0, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \
+This version was built without iconv()."),
+ locale_code, "UTF-8", basename (program_name));
+#endif
+ }
+
+ /* Read the input line by line.
+ Processing it character by character is not possible, because some
+ filters need to look at adjacent characters. Processing the entire file
+ in a whole chunk would take an excessive amount of memory. */
+ for (;;)
+ {
+ char *line;
+ size_t line_len;
+ char *filtered_line;
+ size_t filtered_line_len;
+
+ /* Read a line. */
+ if (read_linebuffer (&lb, stream) == NULL)
+ break;
+ line = lb.buffer;
+ line_len = lb.length;
+ /* read_linebuffer always returns a non-void result. */
+ if (line_len == 0)
+ abort ();
+
+#if HAVE_ICONV
+ /* Convert it to UTF-8. */
+ if (need_code_conversion)
+ {
+ char *utf8_line = last_utf8_line;
+ size_t utf8_line_len = last_utf8_line_len;
+
+ if (xmem_cd_iconv (line, line_len, conv_to_utf8,
+ &utf8_line, &utf8_line_len) != 0)
+ error (EXIT_FAILURE, errno,
+ _("input is not valid in \"%s\" encoding"),
+ locale_code);
+ if (utf8_line != last_utf8_line)
+ {
+ if (last_utf8_line != NULL)
+ free (last_utf8_line);
+ last_utf8_line = utf8_line;
+ last_utf8_line_len = utf8_line_len;
+ }
+
+ line = utf8_line;
+ line_len = utf8_line_len;
+ }
+#endif
+
+ /* Apply the filter. */
+ serbian_to_latin (line, line_len, &filtered_line, &filtered_line_len);
+
+#if HAVE_ICONV
+ /* Convert it back to the original encoding. */
+ if (need_code_conversion)
+ {
+ char *backconv_line = last_backconv_line;
+ size_t backconv_line_len = last_backconv_line_len;
+
+ if (xmem_cd_iconv (filtered_line, filtered_line_len, conv_from_utf8,
+ &backconv_line, &backconv_line_len) != 0)
+ error (EXIT_FAILURE, errno,
+ _("error while converting from \"%s\" encoding to \"%s\" encoding"),
+ "UTF-8", locale_code);
+ if (backconv_line != last_backconv_line)
+ {
+ if (last_backconv_line != NULL)
+ free (last_backconv_line);
+ last_backconv_line = backconv_line;
+ last_backconv_line_len = backconv_line_len;
+ }
+
+ fwrite (backconv_line, 1, backconv_line_len, stdout);
+ }
+ else
+#endif
+ fwrite (filtered_line, 1, filtered_line_len, stdout);
+
+ free (filtered_line);
+ }
+
+#if HAVE_ICONV
+ if (need_code_conversion)
+ {
+ iconv_close (conv_from_utf8);
+ iconv_close (conv_to_utf8);
+ }
+#endif
+
+ destroy_linebuffer (&lb);
+}
diff --git a/gettext-tools/src/str-list.c b/gettext-tools/src/str-list.c
new file mode 100644
index 0000000..3f929c2
--- /dev/null
+++ b/gettext-tools/src/str-list.c
@@ -0,0 +1,236 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995, 1998, 2000-2004, 2006, 2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "str-list.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xalloc.h"
+
+
+/* Initialize an empty list of strings. */
+void
+string_list_init (string_list_ty *slp)
+{
+ slp->item = NULL;
+ slp->nitems = 0;
+ slp->nitems_max = 0;
+}
+
+
+/* Return a fresh, empty list of strings. */
+string_list_ty *
+string_list_alloc ()
+{
+ string_list_ty *slp;
+
+ slp = XMALLOC (string_list_ty);
+ slp->item = NULL;
+ slp->nitems = 0;
+ slp->nitems_max = 0;
+
+ return slp;
+}
+
+
+/* Append a single string to the end of a list of strings. */
+void
+string_list_append (string_list_ty *slp, const char *s)
+{
+ /* Grow the list. */
+ if (slp->nitems >= slp->nitems_max)
+ {
+ size_t nbytes;
+
+ slp->nitems_max = slp->nitems_max * 2 + 4;
+ nbytes = slp->nitems_max * sizeof (slp->item[0]);
+ slp->item = (const char **) xrealloc (slp->item, nbytes);
+ }
+
+ /* Add a copy of the string to the end of the list. */
+ slp->item[slp->nitems++] = xstrdup (s);
+}
+
+
+/* Append a single string to the end of a list of strings, unless it is
+ already contained in the list. */
+void
+string_list_append_unique (string_list_ty *slp, const char *s)
+{
+ size_t j;
+
+ /* Do not if the string is already in the list. */
+ for (j = 0; j < slp->nitems; ++j)
+ if (strcmp (slp->item[j], s) == 0)
+ return;
+
+ /* Grow the list. */
+ if (slp->nitems >= slp->nitems_max)
+ {
+ slp->nitems_max = slp->nitems_max * 2 + 4;
+ slp->item = (const char **) xrealloc (slp->item,
+ slp->nitems_max
+ * sizeof (slp->item[0]));
+ }
+
+ /* Add a copy of the string to the end of the list. */
+ slp->item[slp->nitems++] = xstrdup (s);
+}
+
+
+/* Destroy a list of strings. */
+void
+string_list_destroy (string_list_ty *slp)
+{
+ size_t j;
+
+ for (j = 0; j < slp->nitems; ++j)
+ free ((char *) slp->item[j]);
+ if (slp->item != NULL)
+ free (slp->item);
+}
+
+
+/* Free a list of strings. */
+void
+string_list_free (string_list_ty *slp)
+{
+ size_t j;
+
+ for (j = 0; j < slp->nitems; ++j)
+ free ((char *) slp->item[j]);
+ if (slp->item != NULL)
+ free (slp->item);
+ free (slp);
+}
+
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list. */
+char *
+string_list_concat (const string_list_ty *slp)
+{
+ size_t len;
+ size_t j;
+ char *result;
+ size_t pos;
+
+ len = 1;
+ for (j = 0; j < slp->nitems; ++j)
+ len += strlen (slp->item[j]);
+ result = XNMALLOC (len, char);
+ pos = 0;
+ for (j = 0; j < slp->nitems; ++j)
+ {
+ len = strlen (slp->item[j]);
+ memcpy (result + pos, slp->item[j], len);
+ pos += len;
+ }
+ result[pos] = '\0';
+ return result;
+}
+
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list, and destroy the list. */
+char *
+string_list_concat_destroy (string_list_ty *slp)
+{
+ char *result;
+
+ /* Optimize the most frequent case. */
+ if (slp->nitems == 1)
+ {
+ result = (char *) slp->item[0];
+ free (slp->item);
+ }
+ else
+ {
+ result = string_list_concat (slp);
+ string_list_destroy (slp);
+ }
+ return result;
+}
+
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list, separated by the separator string, terminated
+ by the terminator character. The terminator character is not added if
+ drop_redundant_terminator is true and the last string already ends with
+ the terminator. */
+char *
+string_list_join (const string_list_ty *slp, const char *separator,
+ char terminator, bool drop_redundant_terminator)
+{
+ size_t separator_len = strlen (separator);
+ size_t len;
+ size_t j;
+ char *result;
+ size_t pos;
+
+ len = 1;
+ for (j = 0; j < slp->nitems; ++j)
+ {
+ if (j > 0)
+ len += separator_len;
+ len += strlen (slp->item[j]);
+ }
+ if (terminator)
+ ++len;
+ result = XNMALLOC (len, char);
+ pos = 0;
+ for (j = 0; j < slp->nitems; ++j)
+ {
+ if (j > 0)
+ {
+ memcpy (result + pos, separator, separator_len);
+ pos += separator_len;
+ }
+ len = strlen (slp->item[j]);
+ memcpy (result + pos, slp->item[j], len);
+ pos += len;
+ }
+ if (terminator
+ && !(drop_redundant_terminator
+ && slp->nitems > 0
+ && (len = strlen (slp->item[slp->nitems - 1])) > 0
+ && slp->item[slp->nitems - 1][len - 1] == terminator))
+ result[pos++] = terminator;
+ result[pos] = '\0';
+ return result;
+}
+
+
+/* Return 1 if s is contained in the list of strings, 0 otherwise. */
+bool
+string_list_member (const string_list_ty *slp, const char *s)
+{
+ size_t j;
+
+ for (j = 0; j < slp->nitems; ++j)
+ if (strcmp (slp->item[j], s) == 0)
+ return true;
+ return false;
+}
diff --git a/gettext-tools/src/str-list.h b/gettext-tools/src/str-list.h
new file mode 100644
index 0000000..5b7b75d
--- /dev/null
+++ b/gettext-tools/src/str-list.h
@@ -0,0 +1,88 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1996, 1998, 2000-2004, 2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _STR_LIST_H
+#define _STR_LIST_H 1
+
+/* Get size_t and NULL. */
+#include <stddef.h>
+
+/* Get bool. */
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Type describing list of immutable strings,
+ implemented using a dynamic array. */
+typedef struct string_list_ty string_list_ty;
+struct string_list_ty
+{
+ const char **item;
+ size_t nitems;
+ size_t nitems_max;
+};
+
+/* Initialize an empty list of strings. */
+extern void string_list_init (string_list_ty *slp);
+
+/* Return a fresh, empty list of strings. */
+extern string_list_ty *string_list_alloc (void);
+
+/* Append a single string to the end of a list of strings. */
+extern void string_list_append (string_list_ty *slp, const char *s);
+
+/* Append a single string to the end of a list of strings, unless it is
+ already contained in the list. */
+extern void string_list_append_unique (string_list_ty *slp, const char *s);
+
+/* Destroy a list of strings. */
+extern void string_list_destroy (string_list_ty *slp);
+
+/* Free a list of strings. */
+extern void string_list_free (string_list_ty *slp);
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list. */
+extern char *string_list_concat (const string_list_ty *slp);
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list, and destroy the list. */
+extern char *string_list_concat_destroy (string_list_ty *slp);
+
+/* Return a freshly allocated string obtained by concatenating all the
+ strings in the list, separated by the separator string, terminated
+ by the terminator character. The terminator character is not added if
+ drop_redundant_terminator is true and the last string already ends with
+ the terminator. */
+extern char *string_list_join (const string_list_ty *slp, const char *separator,
+ char terminator, bool drop_redundant_terminator);
+
+/* Return 1 if s is contained in the list of strings, 0 otherwise. */
+extern bool string_list_member (const string_list_ty *slp, const char *s);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _STR_LIST_H */
diff --git a/gettext-tools/src/urlget.c b/gettext-tools/src/urlget.c
new file mode 100644
index 0000000..f3243e1
--- /dev/null
+++ b/gettext-tools/src/urlget.c
@@ -0,0 +1,448 @@
+/* Get the contents of an URL.
+ Copyright (C) 2001-2003, 2005-2010, 2012 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <unistd.h>
+
+#include "closeout.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "full-write.h"
+#include "execute.h"
+#include "javaexec.h"
+#include "binary-io.h"
+#include "propername.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+
+/* Only high-level toolkits, written in languages with exception handling,
+ have an URL datatype and operations to fetch an URL's contents. Such
+ toolkits are Java (class java.net.URL), Qt (classes QUrl and QUrlOperator).
+ We use the Java toolkit.
+ Note that this program doesn't handle redirection pages; programs which
+ wish to process HTML redirection tags need to include a HTML parser,
+ and only full-fledged browsers like w3m, lynx, links have have both
+ an URL fetcher (which covers at least the protocols "http", "ftp", "file")
+ and a HTML parser. [Well, this is not true: libxml2 and Java (see
+ <http://java.sun.com/products/jfc/tsc/articles/bookmarks/>) also contain
+ HTML parsers.] */
+
+
+/* Whether to output something on standard error.
+ This is true by default, because the user should know why we are trying to
+ establish an internet connection. Also, users get confused if a program
+ produces no output for more than 10 seconds for no apparent reason. */
+static bool verbose = true;
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "help", no_argument, NULL, 'h' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "silent", no_argument, NULL, 'q' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void fetch (const char *url, const char *file);
+
+
+int
+main (int argc, char *argv[])
+{
+ int optchar;
+ bool do_help;
+ bool do_version;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set default values for variables. */
+ do_help = false;
+ do_version = false;
+
+ /* Parse command line options. */
+ while ((optchar = getopt_long (argc, argv, "hqV", long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+ case 'h': /* --help */
+ do_help = true;
+ break;
+ case 'q': /* --quiet / --silent */
+ verbose = false;
+ break;
+ case 'V': /* --version */
+ do_version = true;
+ break;
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "2001-2003, 2005-2009");
+ printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Test argument count. */
+ if (optind + 2 != argc)
+ error (EXIT_FAILURE, 0, _("expected two arguments"));
+
+ /* Fetch the contents. */
+ fetch (argv[optind], argv[optind + 1]);
+
+ exit (EXIT_SUCCESS);
+}
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] URL FILE\n\
+"), program_name);
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Fetches and outputs the contents of an URL. If the URL cannot be accessed,\n\
+the locally accessible FILE is used instead.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf (_("\
+ -q, --quiet, --silent suppress progress indicators\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+/* Copy a file's contents to stdout. */
+static void
+cat_file (const char *src_filename)
+{
+ int src_fd;
+ char buf[4096];
+ const int buf_size = sizeof (buf);
+
+ src_fd = open (src_filename, O_RDONLY | O_BINARY);
+ if (src_fd < 0)
+ error (EXIT_FAILURE, errno, _("error while opening \"%s\" for reading"),
+ src_filename);
+
+ for (;;)
+ {
+ ssize_t n_read = read (src_fd, buf, buf_size);
+ if (n_read < 0)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ error (EXIT_FAILURE, errno, _("error reading \"%s\""), src_filename);
+ }
+ if (n_read == 0)
+ break;
+
+ if (full_write (STDOUT_FILENO, buf, n_read) < n_read)
+ error (EXIT_FAILURE, errno, _("error writing stdout"));
+ }
+
+ if (close (src_fd) < 0)
+ error (EXIT_FAILURE, errno, _("error after reading \"%s\""), src_filename);
+}
+
+/* Exit code of the Java program. */
+static int java_exitcode;
+
+static bool
+execute_it (const char *progname,
+ const char *prog_path, char **prog_argv,
+ void *private_data)
+{
+ (void) private_data;
+
+ java_exitcode =
+ execute (progname, prog_path, prog_argv, true, true, false, false, true,
+ false, NULL);
+ /* Exit code 0 means success, 2 means timed out. */
+ return !(java_exitcode == 0 || java_exitcode == 2);
+}
+
+/* Fetch the URL. Upon error, use the FILE as fallback. */
+static void
+fetch (const char *url, const char *file)
+{
+ if (verbose)
+ {
+ fprintf (stderr, _("Retrieving %s..."), url);
+ fflush (stderr);
+ }
+
+#if USEJAVA
+ /* First try: using Java. */
+ {
+ const char *class_name = "gnu.gettext.GetURL";
+ const char *gettextjexedir;
+ const char *gettextjar;
+ const char *args[2];
+
+# if USEJEXE
+ /* Make it possible to override the executable's location. This is
+ necessary for running the testsuite before "make install". */
+ gettextjexedir = getenv ("GETTEXTJEXEDIR");
+ if (gettextjexedir == NULL || gettextjexedir[0] == '\0')
+ gettextjexedir = relocate (GETTEXTJEXEDIR);
+# else
+ gettextjexedir = NULL;
+# endif
+
+ /* Make it possible to override the gettext.jar location. This is
+ necessary for running the testsuite before "make install". */
+ gettextjar = getenv ("GETTEXTJAR");
+ if (gettextjar == NULL || gettextjar[0] == '\0')
+ gettextjar = relocate (GETTEXTJAR);
+
+ /* Prepare arguments. */
+ args[0] = url;
+ args[1] = NULL;
+
+ /* Fetch the URL's contents. */
+ java_exitcode = 127;
+ if (!execute_java_class (class_name, &gettextjar, 1, true, gettextjexedir,
+ args,
+ false, true,
+ execute_it, NULL))
+ {
+ if (verbose)
+ {
+ if (java_exitcode == 0)
+ fprintf (stderr, _(" done.\n"));
+ else if (java_exitcode == 2)
+ fprintf (stderr, _(" timed out.\n"));
+ }
+ return;
+ }
+ }
+#endif
+
+ /* Second try: using "wget -q -O - -T 30 url". */
+ {
+ static bool wget_tested;
+ static bool wget_present;
+
+ if (!wget_tested)
+ {
+ /* Test for presence of wget: "wget --version > /dev/null" */
+ char *argv[3];
+ int exitstatus;
+
+ argv[0] = "wget";
+ argv[1] = "--version";
+ argv[2] = NULL;
+ exitstatus = execute ("wget", "wget", argv, false, false, true, true,
+ true, false, NULL);
+ wget_present = (exitstatus == 0);
+ wget_tested = true;
+ }
+
+ if (wget_present)
+ {
+ char *argv[8];
+ int exitstatus;
+
+ argv[0] = "wget";
+ argv[1] = "-q";
+ argv[2] = "-O"; argv[3] = "-";
+ argv[4] = "-T"; argv[5] = "30";
+ argv[6] = (char *) url;
+ argv[7] = NULL;
+ exitstatus = execute ("wget", "wget", argv, true, false, false, false,
+ true, false, NULL);
+ if (exitstatus != 127)
+ {
+ if (exitstatus != 0)
+ goto failed;
+ if (verbose)
+ fprintf (stderr, _(" done.\n"));
+ return;
+ }
+ }
+ }
+
+ /* Third try: using "lynx -source url". */
+ {
+ static bool lynx_tested;
+ static bool lynx_present;
+
+ if (!lynx_tested)
+ {
+ /* Test for presence of lynx: "lynx --version > /dev/null" */
+ char *argv[3];
+ int exitstatus;
+
+ argv[0] = "lynx";
+ argv[1] = "--version";
+ argv[2] = NULL;
+ exitstatus = execute ("lynx", "lynx", argv, false, false, true, true,
+ true, false, NULL);
+ lynx_present = (exitstatus == 0);
+ lynx_tested = true;
+ }
+
+ if (lynx_present)
+ {
+ char *argv[4];
+ int exitstatus;
+
+ argv[0] = "lynx";
+ argv[1] = "-source";
+ argv[2] = (char *) url;
+ argv[3] = NULL;
+ exitstatus = execute ("lynx", "lynx", argv, true, false, false, false,
+ true, false, NULL);
+ if (exitstatus != 127)
+ {
+ if (exitstatus != 0)
+ goto failed;
+ if (verbose)
+ fprintf (stderr, _(" done.\n"));
+ return;
+ }
+ }
+ }
+
+ /* Fourth try: using "curl --silent url". */
+ {
+ static bool curl_tested;
+ static bool curl_present;
+
+ if (!curl_tested)
+ {
+ /* Test for presence of curl: "curl --version > /dev/null" */
+ char *argv[3];
+ int exitstatus;
+
+ argv[0] = "curl";
+ argv[1] = "--version";
+ argv[2] = NULL;
+ exitstatus = execute ("curl", "curl", argv, false, false, true, true,
+ true, false, NULL);
+ curl_present = (exitstatus == 0 || exitstatus == 2);
+ curl_tested = true;
+ }
+
+ if (curl_present)
+ {
+ char *argv[4];
+ int exitstatus;
+
+ argv[0] = "curl";
+ argv[1] = "--silent";
+ argv[2] = (char *) url;
+ argv[3] = NULL;
+ exitstatus = execute ("curl", "curl", argv, true, false, false, false,
+ true, false, NULL);
+ if (exitstatus != 127)
+ {
+ if (exitstatus != 0)
+ goto failed;
+ if (verbose)
+ fprintf (stderr, _(" done.\n"));
+ return;
+ }
+ }
+ }
+
+ failed:
+ if (verbose)
+ fprintf (stderr, _(" failed.\n"));
+ /* Use the file as fallback. */
+ cat_file (file);
+}
diff --git a/gettext-tools/src/user-email.sh.in b/gettext-tools/src/user-email.sh.in
new file mode 100644
index 0000000..5a35405
--- /dev/null
+++ b/gettext-tools/src/user-email.sh.in
@@ -0,0 +1,435 @@
+#!/bin/sh
+# Prints the user's email address, with confirmation from the user.
+#
+# Copyright (C) 2001-2003, 2005 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 <http://www.gnu.org/licenses/>.
+
+# Prerequisites for using @libdir@ and @localedir@.
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+datarootdir="@datarootdir@"
+datadir="@datadir@"
+# Set variables libdir, localedir.
+libdir="@libdir@"
+localedir="@localedir@"
+
+# Support for relocatability.
+if test "@RELOCATABLE@" = yes; then
+ orig_installdir="$libdir"/gettext # see Makefile.am's install rule
+ # Determine curr_installdir without caring for symlinked callers.
+ curr_installdir=`echo "$0" | sed -e 's,/[^/]*$,,'`
+ curr_installdir=`cd "$curr_installdir" && pwd`
+ # Compute the original/current installation prefixes by stripping the
+ # trailing directories off the original/current installation directories.
+ while true; do
+ orig_last=`echo "$orig_installdir" | sed -n -e 's,^.*/\([^/]*\)$,\1,p'`
+ curr_last=`echo "$curr_installdir" | sed -n -e 's,^.*/\([^/]*\)$,\1,p'`
+ if test -z "$orig_last" || test -z "$curr_last"; then
+ break
+ fi
+ if test "$orig_last" != "$curr_last"; then
+ break
+ fi
+ orig_installdir=`echo "$orig_installdir" | sed -e 's,/[^/]*$,,'`
+ curr_installdir=`echo "$curr_installdir" | sed -e 's,/[^/]*$,,'`
+ done
+ # Now relocate the directory variables that we use.
+ libdir=`echo "$libdir/" | sed -e "s%^${orig_installdir}/%${curr_installdir}/%" | sed -e 's,/$,,'`
+ localedir=`echo "$localedir/" | sed -e "s%^${orig_installdir}/%${curr_installdir}/%" | sed -e 's,/$,,'`
+fi
+
+# Internationalization.
+. gettext.sh
+TEXTDOMAIN=gettext-tools
+export TEXTDOMAIN
+TEXTDOMAINDIR="$localedir"
+export TEXTDOMAINDIR
+
+# Redirect fileno 3 to interactive I/O.
+exec 3>/dev/tty
+
+# Output a prompt.
+if test $# != 0; then
+ echo "$1" 1>&3
+fi
+
+# Find the user name on the local machine.
+user=`id -u -n 2>/dev/null`
+if test -z "$user"; then
+ user="$USER"
+ if test -z "$user"; then
+ user="$LOGNAME"
+ if test -z "$user"; then
+ user=unknown
+ fi
+ fi
+fi
+
+# Find the hostname.
+# hostname on some systems (SVR3.2, old Linux) returns a bogus exit status,
+# so uname gets run too, so we keep only the first line of output.
+#host=`(hostname || uname -n) 2>/dev/null | sed 1q`
+host=`"$libdir"/gettext/hostname --short 2>/dev/null | sed 1q`
+
+# Find the hostname.
+hostfqdn=`"$libdir"/gettext/hostname --fqdn 2>/dev/null | sed 1q`
+
+# Find a list of email addresses from various mailer configuration files.
+# All mailers use configuration files under $HOME. We handle them in a
+# last-modified - first-priority order.
+cd $HOME
+files=""
+
+# ----------------------- BEGIN MAILER SPECIFIC CODE -----------------------
+
+# Mozilla Thunderbird addresses
+files="$files .thunderbird/*/prefs.js"
+
+# Mozilla addresses
+files="$files .mozilla/*/prefs.js"
+
+# Netscape 4 addresses
+files="$files .netscape/liprefs.js .netscape/preferences.js"
+
+# Netscape 3 addresses
+files="$files .netscape/preferences"
+
+# Emacs/XEmacs rmail, Emacs/XEmacs gnus, XEmacs vm addresses
+# XEmacs mew addresses
+files="$files .emacs .emacs.el"
+
+# KDE2 addresses
+files="$files .kde2/share/config/emaildefaults"
+
+# KDE kmail addresses
+files="$files .kde2/share/config/kmailrc"
+
+# GNOME evolution 2 addresses
+files="$files .gconf/apps/evolution/mail/%gconf.xml"
+
+# GNOME evolution 1 addresses
+files="$files evolution/config.xmldb"
+
+# GNOME balsa addresses
+files="$files .gnome/balsa"
+
+# StarOffice and OpenOffice addresses
+sed_dos2unix='s/\r$//'
+sed_soffice51='s,StarOffice 5\.1=\(.*\)$,\1/sofficerc,p'
+sed_soffice52='s,StarOffice 5\.2=\(.*\)$,\1/user/sofficerc,p'
+sed_ooffice='s,^OpenOffice[^=]*=\(.*\)$,\1/user/config/registry/instance/org/openoffice/UserProfile.xml,p'
+files="$files Office51/sofficerc Office52/user/sofficerc "`sed -n -e "$sed_dos2unix" -e "$sed_soffice51" -e "$sed_soffice52" -e "$sed_ooffice" .sversionrc 2>/dev/null | sed -e 's,^file://*,/,'`
+
+# mutt addresses
+files="$files .muttrc"
+
+# pine addresses
+files="$files .pinerc"
+
+# xfmail addresses
+files="$files .xfmail/.xfmailrc"
+
+# tkrat addresses
+files="$files .ratatosk/ratatoskrc"
+
+# ----------------------- END MAILER SPECIFIC CODE -----------------------
+
+# Expand wildcards and remove nonexistent files from the list.
+nfiles=""
+for file in $files; do
+ if test -r "$file" && test ! -d "$file"; then
+ nfiles="$nfiles $file"
+ fi
+done
+files="$nfiles"
+
+addresses=""
+
+if test -n "$files"; then
+
+ for file in `ls -t $files`; do
+
+ case "$file" in
+
+# ----------------------- BEGIN MAILER SPECIFIC CODE -----------------------
+
+ # Mozilla and Mozilla Thunderbird addresses
+ .mozilla/*/prefs.js | .thunderbird/*/prefs.js)
+ addresses="$addresses "`grep -h '^user_pref("mail\.identity\..*\.useremail", ".*");$' $file 2>/dev/null | sed -e 's/^user_pref("mail\.identity\..*\.useremail", "\(.*\)");$/\1/'`
+ ;;
+
+ # Netscape 4 addresses
+ .netscape/liprefs.js | .netscape/preferences.js)
+ addresses="$addresses "`grep -h '^user_pref("mail\.identity\.useremail", ".*");$' $file 2>/dev/null | sed -e 's/^user_pref("mail\.identity\.useremail", "\(.*\)");$/\1/'`
+ ;;
+
+ # Netscape 3 addresses
+ .netscape/preferences)
+ addresses="$addresses "`grep -h '^EMAIL_ADDRESS:' $file 2>/dev/null | sed -e 's/^EMAIL_ADDRESS:[ ]*//'`
+ ;;
+
+ .emacs | .emacs.el)
+ # Emacs/XEmacs rmail, Emacs/XEmacs gnus, XEmacs vm addresses
+ addresses="$addresses "`grep -h '[ (]user-mail-address "[^"]*"' $file 2>/dev/null | sed -e 's/^.*[ (]user-mail-address "\([^"]*\)".*$/\1/'`
+ # XEmacs mew addresses
+ domains=`grep -h '[ (]mew-mail-domain "[^"]*"' $file 2>/dev/null | sed -e 's/^.*[ (]mew-mail-domain "\([^"]*\)".*$/\1/'`
+ if test -n "$domains"; then
+ for domain in $domains; do
+ addresses="$addresses ${user}@$domain"
+ done
+ fi
+ ;;
+
+ # KDE2 addresses
+ .kde2/share/config/emaildefaults)
+ addresses="$addresses "`grep -h '^EmailAddress=' $file 2>/dev/null | sed -e 's/^EmailAddress=//'`
+ ;;
+
+ # KDE kmail addresses
+ .kde2/share/config/kmailrc)
+ addresses="$addresses "`grep -h '^Email Address=' $file 2>/dev/null | sed -e 's/^Email Address=//'`
+ ;;
+
+ # GNOME evolution 2 addresses
+ .gconf/apps/evolution/mail/%gconf.xml)
+ sedexpr0='s,^.*&lt;addr-spec&gt;\(.*\)&lt;/addr-spec&gt;.*$,\1,p'
+ addresses="$addresses "`sed -n -e "$sedexpr0" < $file`
+ ;;
+
+ # GNOME evolution 1 addresses
+ evolution/config.xmldb)
+ sedexpr0='s/^.*<entry name="identity_address_[0-9]*" type="string" value="\([^"]*\)".*$/\1/p'
+ sedexpr1='s/\(..\)/\\x\1/g'
+ sedexpr2='s,$,\\n,'
+ addresses="$addresses "`sed -n -e "$sedexpr0" < $file | while read hexstring; do printf \`echo "$hexstring" | sed -e "$sedexpr1" -e "$sedexpr2"\`; done`
+ ;;
+
+ # GNOME balsa addresses
+ .gnome/balsa)
+ addresses="$addresses "`grep -h '^Address=' $file 2>/dev/null | sed -e 's/^Address=//'`
+ ;;
+
+ # OpenOffice addresses
+ */UserProfile.xml)
+ addresses="$addresses "`sed -n -e 's,^.*<mail cfg:type="string">\(.*\)</mail>.*$,\1,p' $file 2>/dev/null`
+ ;;
+
+ # StarOffice addresses
+ # Not a typo. They really write "Adress" with a single d.
+ # German orthography...
+ */sofficerc)
+ addresses="$addresses "`grep -h '^User-Adress=' $file 2>/dev/null | sed -e 's/#[^#]*$//' -e 's/^.*#//'`
+ ;;
+
+ # mutt addresses
+ .muttrc)
+ mutt_addresses=`grep -h '^set from="[^"]*"[ ]*$' $file 2>/dev/null | sed -e 's/^set from="\([^"]*\)"[ ]*$/\1/'`
+ if test -n "$mutt_addresses"; then
+ addresses="$addresses $mutt_addresses"
+ else
+ # mutt uses $EMAIL as fallback.
+ if test -n "$EMAIL"; then
+ addresses="$addresses $EMAIL"
+ fi
+ fi
+ ;;
+
+ # pine addresses
+ .pinerc)
+ domains=`grep -h '^user-domain=' $file 2>/dev/null | sed -e 's/^user-domain=//'`
+ if test -n "$domains"; then
+ for domain in $domains; do
+ addresses="$addresses ${user}@$domain"
+ done
+ else
+ # The use-only-domain-name option is only used if the user-domain option is not present.
+ domains=`grep -h '^use-only-domain-name=' $file 2>/dev/null | sed -e 's/^use-only-domain-name=//'`
+ if test "Yes" = "$domains"; then
+ addresses="$addresses ${user}@"`echo "$hostfqdn" | sed -e 's/^[^.]*\.//'`
+ fi
+ fi
+ ;;
+
+ # xfmail addresses
+ .xfmail/.xfmailrc)
+ addresses="$addresses "`grep -h '^from=.*<.*>' $file 2>/dev/null | sed -e 's/^.*<\([^<>]*\)>.*$/\1/'`
+ ;;
+
+ # tkrat addresses
+ .ratatosk/ratatoskrc)
+ domains=`grep -h '^set option(masquerade_as) ' $file 2>/dev/null | sed -e 's/^set option(masquerade_as) //'`
+ if test -n "$domains"; then
+ for domain in $domains; do
+ addresses="$addresses ${user}@$domain"
+ done
+ else
+ # The domain option is used only if the masquerade_as option is not present.
+ domains=`grep -h '^set option(domain) ' $file 2>/dev/null | sed -e 's/^set option(domain) //'`
+ if test -n "$domains"; then
+ for domain in $domains; do
+ addresses="$addresses ${user}@${host}.$domain"
+ done
+ fi
+ fi
+ ;;
+
+# ----------------------- END MAILER SPECIFIC CODE -----------------------
+
+ esac
+
+ done
+
+fi
+
+# Some Debian systems have a file /etc/mailname.
+if test -r /etc/mailname; then
+ hostmailname=`cat /etc/mailname`
+ if test -n "$hostmailname"; then
+ addresses="$addresses ${user}@$hostmailname"
+ fi
+fi
+
+# SuSE Linux >= 8.0 systems have a file /etc/sysconfig/mail.
+if test -r /etc/sysconfig/mail; then
+ hostmailname=`. /etc/sysconfig/mail && echo "$FROM_HEADER"`
+ if test -n "$hostmailname"; then
+ addresses="$addresses ${user}@$hostmailname"
+ fi
+fi
+
+# elm has no user-defined addresses.
+# mailx has no user-defined addresses.
+# mh has no user-defined addresses.
+# They use the system default.
+addresses="$addresses ${user}@$hostfqdn"
+
+# Normalize addresses: remove addresses without @, lowercase the part after @,
+# and remove duplicates.
+lowercase_sed='{
+h
+s/^[^@]*@\(.*\)$/\1/
+y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+x
+s/^\([^@]*\)@.*/\1@/
+G
+s/\n//
+p
+}'
+naddresses=""
+for addr in $addresses; do
+ case "$addr" in
+ "<"*">") addr=`echo "$addr" | sed -e 's/^<//' -e 's/>$//'` ;;
+ esac
+ case "$addr" in
+ *@*)
+ addr=`echo "$addr" | sed -n -e "$lowercase_sed"`
+ case " $naddresses " in
+ *" $addr "*) ;;
+ *) naddresses="$naddresses $addr" ;;
+ esac
+ ;;
+ esac
+done
+addresses="$naddresses"
+
+# Now it's time to ask the user.
+case "$addresses" in
+ " "*" "*)
+ # At least two addresses.
+ lines=""
+ i=0
+ for addr in $addresses; do
+ i=`expr $i + 1`
+ lines="${lines}${i} ${addr}
+"
+ done
+ while true; do
+ { gettext "Which is your email address?"; echo; } 1>&3
+ echo "$lines" 1>&3
+ { gettext "Please choose the number, or enter your email address."; echo; } 1>&3
+ read answer < /dev/tty
+ case "$answer" in
+ *@*) ;;
+ [0-9]*)
+ i=0
+ for addr in $addresses; do
+ i=`expr $i + 1`
+ if test "$i" = "$answer"; then
+ break 2
+ fi
+ done
+ ;;
+ esac
+ case "$answer" in
+ "<"*">") answer=`echo "$answer" | sed -e 's/^<//' -e 's/>$//'` ;;
+ esac
+ case "$answer" in
+ *" "*) { gettext "Invalid email address: invalid character."; echo; echo; } 1>&3 ; continue ;;
+ *@*.*) ;;
+ *@*) { gettext "Invalid email address: need a fully qualified host name or domain name."; echo; echo; } 1>&3 ; continue ;;
+ *) { gettext "Invalid email address: missing @"; echo; echo; } 1>&3 ; continue ;;
+ esac
+ addr=`echo "$answer" | sed -n -e "$lowercase_sed"`
+ break
+ done
+ ;;
+ " "*)
+ # One address.
+ while true; do
+ { gettext "Is the following your email address?"; echo; } 1>&3
+ echo " $addresses" 1>&3
+ { gettext "Please confirm by pressing Return, or enter your email address."; echo; } 1>&3
+ read answer < /dev/tty
+ if test -z "$answer"; then
+ addr=`echo "$addresses" | sed -e 's/^ //'`
+ break
+ fi
+ case "$answer" in
+ "<"*">") answer=`echo "$answer" | sed -e 's/^<//' -e 's/>$//'` ;;
+ esac
+ case "$answer" in
+ *" "*) { gettext "Invalid email address: invalid character."; echo; echo; } 1>&3 ; continue ;;
+ *@*.*) ;;
+ *@*) { gettext "Invalid email address: need a fully qualified host name or domain name."; echo; echo; } 1>&3 ; continue ;;
+ *) { gettext "Invalid email address: missing @"; echo; echo; } 1>&3 ; continue ;;
+ esac
+ addr=`echo "$answer" | sed -n -e "$lowercase_sed"`
+ break
+ done
+ ;;
+ "")
+ # No address.
+ { gettext "Couldn't find out about your email address."; echo; } 1>&3
+ while true; do
+ { gettext "Please enter your email address."; echo; } 1>&3
+ read answer < /dev/tty
+ case "$answer" in
+ "<"*">") answer=`echo "$answer" | sed -e 's/^<//' -e 's/>$//'` ;;
+ esac
+ case "$answer" in
+ *" "*) { gettext "Invalid email address: invalid character."; echo; echo; } 1>&3 ; continue ;;
+ *@*.*) ;;
+ *@*) { gettext "Invalid email address: need a fully qualified host name or domain name."; echo; echo; } 1>&3 ; continue ;;
+ *) { gettext "Invalid email address: missing @"; echo; echo; } 1>&3 ; continue ;;
+ esac
+ addr=`echo "$answer" | sed -n -e "$lowercase_sed"`
+ break
+ done
+ ;;
+ *) echo "internal error" 1>&3 ; exit 1 ;;
+esac
+
+# Print to standard output.
+echo "$addr"
diff --git a/gettext-tools/src/write-catalog.c b/gettext-tools/src/write-catalog.c
new file mode 100644
index 0000000..bab8c36
--- /dev/null
+++ b/gettext-tools/src/write-catalog.c
@@ -0,0 +1,469 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2008, 2012 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-catalog.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+#include "ostream.h"
+#include "file-ostream.h"
+#include "fwriteerror.h"
+#include "error-progname.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "gettext.h"
+
+/* Our regular abbreviation. */
+#define _(str) gettext (str)
+
+/* When compiled in src, enable color support.
+ When compiled in libgettextpo, don't enable color support. */
+#ifdef GETTEXTDATADIR
+
+# define ENABLE_COLOR 1
+
+# include "styled-ostream.h"
+# include "term-styled-ostream.h"
+# include "html-styled-ostream.h"
+# include "fd-ostream.h"
+
+# include "color.h"
+# include "po-charset.h"
+# include "msgl-iconv.h"
+
+#endif
+
+
+/* =========== Some parameters for use by 'msgdomain_list_print'. ========== */
+
+
+/* This variable controls the page width when printing messages.
+ Defaults to PAGE_WIDTH if not set. Zero (0) given to message_page_-
+ width_set will result in no wrapping being performed. */
+static size_t page_width = PAGE_WIDTH;
+
+void
+message_page_width_set (size_t n)
+{
+ if (n == 0)
+ {
+ page_width = INT_MAX;
+ return;
+ }
+
+ if (n < 20)
+ n = 20;
+
+ page_width = n;
+}
+
+
+/* ======================== msgdomain_list_print() ======================== */
+
+
+void
+msgdomain_list_print (msgdomain_list_ty *mdlp, const char *filename,
+ catalog_output_format_ty output_syntax,
+ bool force, bool debug)
+{
+ bool to_stdout;
+
+ /* We will not write anything if, for every domain, we have no message
+ or only the header entry. */
+ if (!force)
+ {
+ bool found_nonempty = false;
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ if (!(mlp->nitems == 0
+ || (mlp->nitems == 1 && is_header (mlp->item[0]))))
+ {
+ found_nonempty = true;
+ break;
+ }
+ }
+
+ if (!found_nonempty)
+ return;
+ }
+
+ /* Check whether the output format can accommodate all messages. */
+ if (!output_syntax->supports_multiple_domains && mdlp->nitems > 1)
+ {
+ if (output_syntax->alternative_is_po)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, _("\
+Cannot output multiple translation domains into a single file with the specified output format. Try using PO file syntax instead."));
+ else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false, _("\
+Cannot output multiple translation domains into a single file with the specified output format."));
+ }
+ else
+ {
+ if (!output_syntax->supports_contexts)
+ {
+ const lex_pos_ty *has_context;
+ size_t k;
+
+ has_context = NULL;
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgctxt != NULL)
+ {
+ has_context = &mp->pos;
+ break;
+ }
+ }
+ }
+
+ if (has_context != NULL)
+ {
+ error_with_progname = false;
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
+ has_context->file_name, has_context->line_number,
+ (size_t)(-1), false, _("\
+message catalog has context dependent translations, but the output format does not support them."));
+ error_with_progname = true;
+ }
+ }
+
+ if (!output_syntax->supports_plurals)
+ {
+ const lex_pos_ty *has_plural;
+ size_t k;
+
+ has_plural = NULL;
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural != NULL)
+ {
+ has_plural = &mp->pos;
+ break;
+ }
+ }
+ }
+
+ if (has_plural != NULL)
+ {
+ error_with_progname = false;
+ if (output_syntax->alternative_is_java_class)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
+ has_plural->file_name, has_plural->line_number,
+ (size_t)(-1), false, _("\
+message catalog has plural form translations, but the output format does not support them. Try generating a Java class using \"msgfmt --java\", instead of a properties file."));
+ else
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
+ has_plural->file_name, has_plural->line_number,
+ (size_t)(-1), false, _("\
+message catalog has plural form translations, but the output format does not support them."));
+ error_with_progname = true;
+ }
+ }
+ }
+
+ to_stdout = (filename == NULL || strcmp (filename, "-") == 0
+ || strcmp (filename, "/dev/stdout") == 0);
+
+#if ENABLE_COLOR
+ if (output_syntax->supports_color
+ && (color_mode == color_yes
+ || (color_mode == color_tty && to_stdout && isatty (STDOUT_FILENO))))
+ {
+ int fd;
+ ostream_t stream;
+
+ /* Open the output file. */
+ if (!to_stdout)
+ {
+ fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC,
+ /* 0666 in portable POSIX notation: */
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (fd < 0)
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("cannot create output file \"%s\""),
+ filename),
+ errno_description));
+ }
+ }
+ else
+ {
+ fd = STDOUT_FILENO;
+ filename = _("standard output");
+ }
+
+ style_file_prepare ();
+ stream = term_styled_ostream_create (fd, filename, style_file_name);
+ if (stream == NULL)
+ stream = fd_ostream_create (fd, filename, true);
+ output_syntax->print (mdlp, stream, page_width, debug);
+ ostream_free (stream);
+
+ /* Make sure nothing went wrong. */
+ if (close (fd) < 0)
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while writing \"%s\" file"),
+ filename),
+ errno_description));
+ }
+ }
+ else
+#endif
+ {
+ FILE *fp;
+ file_ostream_t stream;
+
+ /* Open the output file. */
+ if (!to_stdout)
+ {
+ fp = fopen (filename, "wb");
+ if (fp == NULL)
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("cannot create output file \"%s\""),
+ filename),
+ errno_description));
+ }
+ }
+ else
+ {
+ fp = stdout;
+ filename = _("standard output");
+ }
+
+ stream = file_ostream_create (fp);
+
+#if ENABLE_COLOR
+ if (output_syntax->supports_color && color_mode == color_html)
+ {
+ html_styled_ostream_t html_stream;
+
+ /* Convert mdlp to UTF-8 encoding. */
+ if (mdlp->encoding != po_charset_utf8)
+ {
+ mdlp = msgdomain_list_copy (mdlp, 0);
+ mdlp = iconv_msgdomain_list (mdlp, po_charset_utf8, false, NULL);
+ }
+
+ style_file_prepare ();
+ html_stream = html_styled_ostream_create (stream, style_file_name);
+ output_syntax->print (mdlp, html_stream, page_width, debug);
+ ostream_free (html_stream);
+ }
+ else
+#endif
+ {
+ output_syntax->print (mdlp, stream, page_width, debug);
+ }
+
+ ostream_free (stream);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (fp))
+ {
+ const char *errno_description = strerror (errno);
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
+ xasprintf ("%s: %s",
+ xasprintf (_("error while writing \"%s\" file"),
+ filename),
+ errno_description));
+ }
+ }
+}
+
+
+/* =============================== Sorting. ================================ */
+
+
+static int
+cmp_by_msgid (const void *va, const void *vb)
+{
+ const message_ty *a = *(const message_ty **) va;
+ const message_ty *b = *(const message_ty **) vb;
+
+ /* Because msgids normally contain only ASCII characters or are UTF-8
+ encoded, it is OK to sort them as if we were in a C.UTF-8 locale. And
+ strcoll() in a C.UTF-8 locale is the same as strcmp(). */
+ int cmp = strcmp (a->msgid, b->msgid);
+ if (cmp != 0)
+ return cmp;
+
+ /* If the msgids are equal, disambiguate by comparing the contexts. */
+ if (a->msgctxt == b->msgctxt)
+ return 0;
+ if (a->msgctxt == NULL)
+ return -1;
+ if (b->msgctxt == NULL)
+ return 1;
+ return strcmp (a->msgctxt, b->msgctxt);
+}
+
+
+void
+msgdomain_list_sort_by_msgid (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ if (mlp->nitems > 0)
+ qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_msgid);
+ }
+}
+
+
+/* Sort the file positions of every message. */
+
+static int
+cmp_filepos (const void *va, const void *vb)
+{
+ const lex_pos_ty *a = (const lex_pos_ty *) va;
+ const lex_pos_ty *b = (const lex_pos_ty *) vb;
+ int cmp;
+
+ cmp = strcmp (a->file_name, b->file_name);
+ if (cmp == 0)
+ cmp = (int) a->line_number - (int) b->line_number;
+
+ return cmp;
+}
+
+static void
+msgdomain_list_sort_filepos (msgdomain_list_ty *mdlp)
+{
+ size_t j, k;
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->filepos_count > 0)
+ qsort (mp->filepos, mp->filepos_count, sizeof (mp->filepos[0]),
+ cmp_filepos);
+ }
+ }
+}
+
+
+/* Sort the messages according to the file position. */
+
+static int
+cmp_by_filepos (const void *va, const void *vb)
+{
+ const message_ty *a = *(const message_ty **) va;
+ const message_ty *b = *(const message_ty **) vb;
+ int cmp;
+
+ /* No filepos is smaller than any other filepos. */
+ if (a->filepos_count == 0)
+ {
+ if (b->filepos_count != 0)
+ return -1;
+ }
+ if (b->filepos_count == 0)
+ return 1;
+
+ /* Compare on the file names... */
+ cmp = strcmp (a->filepos[0].file_name, b->filepos[0].file_name);
+ if (cmp != 0)
+ return cmp;
+
+ /* If they are equal, compare on the line numbers... */
+ cmp = a->filepos[0].line_number - b->filepos[0].line_number;
+ if (cmp != 0)
+ return cmp;
+
+ /* If they are equal, compare on the msgid strings. */
+ /* Because msgids normally contain only ASCII characters or are UTF-8
+ encoded, it is OK to sort them as if we were in a C.UTF-8 locale. And
+ strcoll() in a C.UTF-8 locale is the same as strcmp(). */
+ cmp = strcmp (a->msgid, b->msgid);
+ if (cmp != 0)
+ return cmp;
+
+ /* If the msgids are equal, disambiguate by comparing the contexts. */
+ if (a->msgctxt == b->msgctxt)
+ return 0;
+ if (a->msgctxt == NULL)
+ return -1;
+ if (b->msgctxt == NULL)
+ return 1;
+ return strcmp (a->msgctxt, b->msgctxt);
+}
+
+
+void
+msgdomain_list_sort_by_filepos (msgdomain_list_ty *mdlp)
+{
+ size_t k;
+
+ /* It makes sense to compare filepos[0] of different messages only after
+ the filepos[] array of each message has been sorted. Sort it now. */
+ msgdomain_list_sort_filepos (mdlp);
+
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp = mdlp->item[k]->messages;
+
+ if (mlp->nitems > 0)
+ qsort (mlp->item, mlp->nitems, sizeof (mlp->item[0]), cmp_by_filepos);
+ }
+}
diff --git a/gettext-tools/src/write-catalog.h b/gettext-tools/src/write-catalog.h
new file mode 100644
index 0000000..804592c
--- /dev/null
+++ b/gettext-tools/src/write-catalog.h
@@ -0,0 +1,91 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2003, 2006, 2008 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_CATALOG_H
+#define _WRITE_CATALOG_H
+
+#include <stdbool.h>
+
+#include "ostream.h"
+#include "message.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* This structure describes a textual catalog output format. */
+struct catalog_output_format
+{
+ /* Outputs a list of domains of messages to a stream. */
+ void (*print) (msgdomain_list_ty *mdlp, ostream_t stream, size_t page_width, bool debug);
+
+ /* Whether the print function requires the MDLP to be encoded in UTF-8
+ encoding. */
+ bool requires_utf8;
+
+ /* Whether the print function supports styled output. */
+ bool supports_color;
+
+ /* Whether the format supports multiple domains in a single file. */
+ bool supports_multiple_domains;
+
+ /* Whether the format supports contexts. */
+ bool supports_contexts;
+
+ /* Whether the format supports plurals. */
+ bool supports_plurals;
+
+ /* Whether the formats sorts obsolete messages at the end. */
+ bool sorts_obsoletes_to_end;
+
+ /* Whether the PO file format is a suitable alternative output format for
+ this one. */
+ bool alternative_is_po;
+
+ /* Whether a Java class is a suitable alternative output format for this
+ one. */
+ bool alternative_is_java_class;
+};
+
+typedef const struct catalog_output_format * catalog_output_format_ty;
+
+/* These functions set some parameters for use by 'msgdomain_list_print'. */
+extern void
+ message_page_width_set (size_t width);
+
+/* Output MDLP into a PO file with the given FILENAME, according to the
+ parameters set by the functions above. */
+extern void
+ msgdomain_list_print (msgdomain_list_ty *mdlp,
+ const char *filename,
+ catalog_output_format_ty output_syntax,
+ bool force, bool debug);
+
+/* Sort MDLP destructively according to the given criterion. */
+extern void
+ msgdomain_list_sort_by_msgid (msgdomain_list_ty *mdlp);
+extern void
+ msgdomain_list_sort_by_filepos (msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WRITE_CATALOG_H */
diff --git a/gettext-tools/src/write-csharp.c b/gettext-tools/src/write-csharp.c
new file mode 100644
index 0000000..047361a
--- /dev/null
+++ b/gettext-tools/src/write-csharp.c
@@ -0,0 +1,783 @@
+/* Writing C# satellite assemblies.
+ Copyright (C) 2003-2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "write-csharp.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#if !defined S_ISDIR && defined S_IFDIR
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !S_IRUSR && S_IREAD
+# define S_IRUSR S_IREAD
+#endif
+#if !S_IRUSR
+# define S_IRUSR 00400
+#endif
+#if !S_IWUSR && S_IWRITE
+# define S_IWUSR S_IWRITE
+#endif
+#if !S_IWUSR
+# define S_IWUSR 00200
+#endif
+#if !S_IXUSR && S_IEXEC
+# define S_IXUSR S_IEXEC
+#endif
+#if !S_IXUSR
+# define S_IXUSR 00100
+#endif
+#if !S_IRGRP
+# define S_IRGRP (S_IRUSR >> 3)
+#endif
+#if !S_IWGRP
+# define S_IWGRP (S_IWUSR >> 3)
+#endif
+#if !S_IXGRP
+# define S_IXGRP (S_IXUSR >> 3)
+#endif
+#if !S_IROTH
+# define S_IROTH (S_IRUSR >> 6)
+#endif
+#if !S_IWOTH
+# define S_IWOTH (S_IWUSR >> 6)
+#endif
+#if !S_IXOTH
+# define S_IXOTH (S_IXUSR >> 6)
+#endif
+
+#include "c-ctype.h"
+#include "relocatable.h"
+#include "error.h"
+#include "xerror.h"
+#include "csharpcomp.h"
+#include "message.h"
+#include "msgfmt.h"
+#include "msgl-iconv.h"
+#include "plural-exp.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "concat-filename.h"
+#include "fwriteerror.h"
+#include "clean-temp.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Convert a resource name to a class name.
+ Return a nonempty string consisting of alphanumerics and underscores
+ and starting with a letter or underscore. */
+static char *
+construct_class_name (const char *resource_name)
+{
+ /* This code must be kept consistent with intl.cs, function
+ GettextResourceManager.ConstructClassName. */
+ /* We could just return an arbitrary fixed class name, like "Messages",
+ assuming that every assembly will only ever contain one
+ GettextResourceSet subclass, but this assumption would break the day
+ we want to support multi-domain PO files in the same format... */
+ bool valid;
+ const char *p;
+
+ /* Test for a valid ASCII identifier:
+ - nonempty,
+ - first character is A..Za..z_ - see x-csharp.c:is_identifier_start.
+ - next characters are A..Za..z_0..9 - see x-csharp.c:is_identifier_part.
+ */
+ valid = (resource_name[0] != '\0');
+ for (p = resource_name; valid && *p != '\0'; p++)
+ {
+ char c = *p;
+ if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
+ || (p > resource_name && c >= '0' && c <= '9')))
+ valid = false;
+ }
+ if (valid)
+ return xstrdup (resource_name);
+ else
+ {
+ static const char hexdigit[] = "0123456789abcdef";
+ const char *str = resource_name;
+ const char *str_limit = str + strlen (str);
+ char *class_name = XNMALLOC (12 + 6 * (str_limit - str) + 1, char);
+ char *b;
+
+ b = class_name;
+ memcpy (b, "__UESCAPED__", 12); b += 12;
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc >= 0x10000)
+ {
+ *b++ = '_';
+ *b++ = 'U';
+ *b++ = hexdigit[(uc >> 28) & 0x0f];
+ *b++ = hexdigit[(uc >> 24) & 0x0f];
+ *b++ = hexdigit[(uc >> 20) & 0x0f];
+ *b++ = hexdigit[(uc >> 16) & 0x0f];
+ *b++ = hexdigit[(uc >> 12) & 0x0f];
+ *b++ = hexdigit[(uc >> 8) & 0x0f];
+ *b++ = hexdigit[(uc >> 4) & 0x0f];
+ *b++ = hexdigit[uc & 0x0f];
+ }
+ else if (!((uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z')
+ || (uc >= '0' && uc <= '9')))
+ {
+ *b++ = '_';
+ *b++ = 'u';
+ *b++ = hexdigit[(uc >> 12) & 0x0f];
+ *b++ = hexdigit[(uc >> 8) & 0x0f];
+ *b++ = hexdigit[(uc >> 4) & 0x0f];
+ *b++ = hexdigit[uc & 0x0f];
+ }
+ else
+ *b++ = uc;
+ }
+ *b++ = '\0';
+ return (char *) xrealloc (class_name, b - class_name);
+ }
+}
+
+
+/* Write a string in C# Unicode notation to the given stream. */
+static void
+write_csharp_string (FILE *stream, const char *str)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ const char *str_limit = str + strlen (str);
+
+ fprintf (stream, "\"");
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc == 0x0000)
+ fprintf (stream, "\\0");
+ else if (uc == 0x0007)
+ fprintf (stream, "\\a");
+ else if (uc == 0x0008)
+ fprintf (stream, "\\b");
+ else if (uc == 0x0009)
+ fprintf (stream, "\\t");
+ else if (uc == 0x000a)
+ fprintf (stream, "\\n");
+ else if (uc == 0x000b)
+ fprintf (stream, "\\v");
+ else if (uc == 0x000c)
+ fprintf (stream, "\\f");
+ else if (uc == 0x000d)
+ fprintf (stream, "\\r");
+ else if (uc == 0x0022)
+ fprintf (stream, "\\\"");
+ else if (uc == 0x005c)
+ fprintf (stream, "\\\\");
+ else if (uc >= 0x0020 && uc < 0x007f)
+ fprintf (stream, "%c", (int) uc);
+ else if (uc < 0x10000)
+ fprintf (stream, "\\u%c%c%c%c",
+ hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
+ hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
+ else
+ fprintf (stream, "\\U%c%c%c%c%c%c%c%c",
+ hexdigit[(uc >> 28) & 0x0f], hexdigit[(uc >> 24) & 0x0f],
+ hexdigit[(uc >> 20) & 0x0f], hexdigit[(uc >> 16) & 0x0f],
+ hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
+ hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
+ }
+ fprintf (stream, "\"");
+}
+
+
+/* Write a (msgctxt, msgid) pair as a string in C# Unicode notation to the
+ given stream. */
+static void
+write_csharp_msgid (FILE *stream, message_ty *mp)
+{
+ const char *msgctxt = mp->msgctxt;
+ const char *msgid = mp->msgid;
+
+ if (msgctxt == NULL)
+ write_csharp_string (stream, msgid);
+ else
+ {
+ size_t msgctxt_len = strlen (msgctxt);
+ size_t msgid_len = strlen (msgid);
+ size_t combined_len = msgctxt_len + 1 + msgid_len;
+ char *combined;
+
+ combined = (char *) xmalloca (combined_len);
+ memcpy (combined, msgctxt, msgctxt_len);
+ combined[msgctxt_len] = MSGCTXT_SEPARATOR;
+ memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
+
+ write_csharp_string (stream, combined);
+
+ freea (combined);
+ }
+}
+
+
+/* Write C# code that returns the value for a message. If the message
+ has plural forms, it is an expression of type System.String[], otherwise it
+ is an expression of type System.String. */
+static void
+write_csharp_msgstr (FILE *stream, message_ty *mp)
+{
+ if (mp->msgid_plural != NULL)
+ {
+ bool first;
+ const char *p;
+
+ fprintf (stream, "new System.String[] { ");
+ for (p = mp->msgstr, first = true;
+ p < mp->msgstr + mp->msgstr_len;
+ p += strlen (p) + 1, first = false)
+ {
+ if (!first)
+ fprintf (stream, ", ");
+ write_csharp_string (stream, p);
+ }
+ fprintf (stream, " }");
+ }
+ else
+ {
+ if (mp->msgstr_len != strlen (mp->msgstr) + 1)
+ abort ();
+
+ write_csharp_string (stream, mp->msgstr);
+ }
+}
+
+
+/* Tests whether a plural expression, evaluated according to the C rules,
+ can only produce the values 0 and 1. */
+static bool
+is_expression_boolean (struct expression *exp)
+{
+ switch (exp->operation)
+ {
+ case var:
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ return false;
+ case lnot:
+ case less_than:
+ case greater_than:
+ case less_or_equal:
+ case greater_or_equal:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ return true;
+ case num:
+ return (exp->val.num == 0 || exp->val.num == 1);
+ case qmop:
+ return is_expression_boolean (exp->val.args[1])
+ && is_expression_boolean (exp->val.args[2]);
+ default:
+ abort ();
+ }
+}
+
+
+/* Write C# code that evaluates a plural expression according to the C rules.
+ The variable is called 'n'. */
+static void
+write_csharp_expression (FILE *stream, const struct expression *exp, bool as_boolean)
+{
+ /* We use parentheses everywhere. This frees us from tracking the priority
+ of arithmetic operators. */
+ if (as_boolean)
+ {
+ /* Emit a C# expression of type 'bool'. */
+ switch (exp->operation)
+ {
+ case num:
+ fprintf (stream, "%s", exp->val.num ? "true" : "false");
+ return;
+ case lnot:
+ fprintf (stream, "(!");
+ write_csharp_expression (stream, exp->val.args[0], true);
+ fprintf (stream, ")");
+ return;
+ case less_than:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " < ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case greater_than:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " > ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case less_or_equal:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " <= ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case greater_or_equal:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " >= ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case equal:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " == ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case not_equal:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " != ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case land:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " && ");
+ write_csharp_expression (stream, exp->val.args[1], true);
+ fprintf (stream, ")");
+ return;
+ case lor:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " || ");
+ write_csharp_expression (stream, exp->val.args[1], true);
+ fprintf (stream, ")");
+ return;
+ case qmop:
+ if (is_expression_boolean (exp->val.args[1])
+ && is_expression_boolean (exp->val.args[2]))
+ {
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " ? ");
+ write_csharp_expression (stream, exp->val.args[1], true);
+ fprintf (stream, " : ");
+ write_csharp_expression (stream, exp->val.args[2], true);
+ fprintf (stream, ")");
+ return;
+ }
+ /*FALLTHROUGH*/
+ case var:
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp, false);
+ fprintf (stream, " != 0)");
+ return;
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ /* Emit a C# expression of type 'long'. */
+ switch (exp->operation)
+ {
+ case var:
+ fprintf (stream, "n");
+ return;
+ case num:
+ fprintf (stream, "%lu", exp->val.num);
+ return;
+ case mult:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " * ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case divide:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " / ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case module:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " %% ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case plus:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " + ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case minus:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " - ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case qmop:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " ? ");
+ write_csharp_expression (stream, exp->val.args[1], false);
+ fprintf (stream, " : ");
+ write_csharp_expression (stream, exp->val.args[2], false);
+ fprintf (stream, ")");
+ return;
+ case lnot:
+ case less_than:
+ case greater_than:
+ case less_or_equal:
+ case greater_or_equal:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ fprintf (stream, "(");
+ write_csharp_expression (stream, exp, true);
+ fprintf (stream, " ? 1 : 0)");
+ return;
+ default:
+ abort ();
+ }
+ }
+}
+
+
+/* Write the C# code for the GettextResourceSet subclass to the given stream.
+ Note that we use fully qualified class names and no "using" statements,
+ because applications can have their own classes called X.Y.Hashtable or
+ X.Y.String. */
+static void
+write_csharp_code (FILE *stream, const char *culture_name, const char *class_name, message_list_ty *mlp)
+{
+ const char *last_dot;
+ const char *class_name_last_part;
+ unsigned int plurals;
+ size_t j;
+
+ fprintf (stream,
+ "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
+
+ /* We have to use a "using" statement here, to avoid a bug in the pnet-0.6.0
+ compiler. */
+ fprintf (stream, "using GNU.Gettext;\n");
+
+ /* Assign a strong name to the assembly, so that two different localizations
+ of the same domain can be loaded one after the other. This strong name
+ tells the Global Assembly Cache that they are meant to be different. */
+ fprintf (stream, "[assembly: System.Reflection.AssemblyCulture(");
+ write_csharp_string (stream, culture_name);
+ fprintf (stream, ")]\n");
+
+ last_dot = strrchr (class_name, '.');
+ if (last_dot != NULL)
+ {
+ fprintf (stream, "namespace ");
+ fwrite (class_name, 1, last_dot - class_name, stream);
+ fprintf (stream, " {\n");
+ class_name_last_part = last_dot + 1;
+ }
+ else
+ class_name_last_part = class_name;
+ fprintf (stream, "public class %s : GettextResourceSet {\n",
+ class_name_last_part);
+
+ /* Determine whether there are plural messages. */
+ plurals = 0;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ plurals++;
+
+ /* Emit the constructor. */
+ fprintf (stream, " public %s ()\n", class_name_last_part);
+ fprintf (stream, " : base () {\n");
+ fprintf (stream, " }\n");
+
+ /* Emit the TableInitialized field. */
+ fprintf (stream, " private bool TableInitialized;\n");
+
+ /* Emit the ReadResources method. */
+ fprintf (stream, " protected override void ReadResources () {\n");
+ /* In some implementations, such as mono < 2009-02-27, the ReadResources
+ method is called just once, when Table == null. In other implementations,
+ such as mono >= 2009-02-27, it is called at every GetObject call, and it
+ is responsible for doing the initialization only once, even when called
+ simultaneously from multiple threads. */
+ fprintf (stream, " if (!TableInitialized) {\n");
+ fprintf (stream, " lock (this) {\n");
+ fprintf (stream, " if (!TableInitialized) {\n");
+ /* In some implementations, the ResourceSet constructor initializes Table
+ before calling ReadResources(). In other implementations, the
+ ReadResources() method is expected to initialize the Table. */
+ fprintf (stream, " if (Table == null)\n");
+ fprintf (stream, " Table = new System.Collections.Hashtable();\n");
+ fprintf (stream, " System.Collections.Hashtable t = Table;\n");
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ fprintf (stream, " t.Add(");
+ write_csharp_msgid (stream, mlp->item[j]);
+ fprintf (stream, ",");
+ write_csharp_msgstr (stream, mlp->item[j]);
+ fprintf (stream, ");\n");
+ }
+ fprintf (stream, " TableInitialized = true;\n");
+ fprintf (stream, " }\n");
+ fprintf (stream, " }\n");
+ fprintf (stream, " }\n");
+ fprintf (stream, " }\n");
+
+ /* Emit the msgid_plural strings. Only used by msgunfmt. */
+ if (plurals)
+ {
+ fprintf (stream, " public static System.Collections.Hashtable GetMsgidPluralTable () {\n");
+ fprintf (stream, " System.Collections.Hashtable t = new System.Collections.Hashtable();\n");
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ {
+ fprintf (stream, " t.Add(");
+ write_csharp_msgid (stream, mlp->item[j]);
+ fprintf (stream, ",");
+ write_csharp_string (stream, mlp->item[j]->msgid_plural);
+ fprintf (stream, ");\n");
+ }
+ fprintf (stream, " return t;\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the PluralEval function. It is a subroutine for GetPluralString. */
+ if (plurals)
+ {
+ message_ty *header_entry;
+ const struct expression *plural;
+ unsigned long int nplurals;
+
+ header_entry = message_list_search (mlp, NULL, "");
+ extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
+ &plural, &nplurals);
+
+ fprintf (stream, " protected override long PluralEval (long n) {\n");
+ fprintf (stream, " return ");
+ write_csharp_expression (stream, plural, false);
+ fprintf (stream, ";\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Terminate the class. */
+ fprintf (stream, "}\n");
+
+ if (last_dot != NULL)
+ /* Terminate the namespace. */
+ fprintf (stream, "}\n");
+}
+
+
+int
+msgdomain_write_csharp (message_list_ty *mlp, const char *canon_encoding,
+ const char *resource_name, const char *locale_name,
+ const char *directory)
+{
+ int retval;
+ struct temp_dir *tmpdir;
+ char *culture_name;
+ char *output_file;
+ char *class_name;
+ char *csharp_file_name;
+ FILE *csharp_file;
+ const char *gettextlibdir;
+ const char *csharp_sources[1];
+ const char *libdirs[1];
+ const char *libraries[1];
+
+ /* If no entry for this resource/domain, don't even create the file. */
+ if (mlp->nitems == 0)
+ return 0;
+
+ retval = 1;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Create a temporary directory where we can put the C# file.
+ A simple temporary file would also be possible but would require us to
+ define our own variant of mkstemp(): On one hand the functions mktemp(),
+ tmpnam(), tempnam() present a security risk, and on the other hand the
+ function mkstemp() doesn't allow to specify a fixed suffix of the file.
+ It is simpler to create a temporary directory. */
+ tmpdir = create_temp_dir ("msg", NULL, false);
+ if (tmpdir == NULL)
+ goto quit1;
+
+ /* Assign a default value to the resource name. */
+ if (resource_name == NULL)
+ resource_name = "Messages";
+
+ /* Convert the locale name to a .NET specific culture name. */
+ culture_name = xstrdup (locale_name);
+ {
+ char *p;
+ for (p = culture_name; *p != '\0'; p++)
+ if (*p == '_')
+ *p = '-';
+ if (strncmp (culture_name, "sr-CS", 5) == 0)
+ memcpy (culture_name, "sr-SP", 5);
+ p = strchr (culture_name, '@');
+ if (p != NULL)
+ {
+ if (strcmp (p, "@latin") == 0)
+ strcpy (p, "-Latn");
+ else if (strcmp (p, "@cyrillic") == 0)
+ strcpy (p, "-Cyrl");
+ }
+ if (strcmp (culture_name, "sr-SP") == 0)
+ {
+ free (culture_name);
+ culture_name = xstrdup ("sr-SP-Latn");
+ }
+ else if (strcmp (culture_name, "uz-UZ") == 0)
+ {
+ free (culture_name);
+ culture_name = xstrdup ("uz-UZ-Latn");
+ }
+ }
+
+ /* Compute the output file name. This code must be kept consistent with
+ intl.cs, function GetSatelliteAssembly(). */
+ {
+ char *output_dir = xconcatenated_filename (directory, culture_name, NULL);
+ struct stat statbuf;
+
+ /* Try to create the output directory if it does not yet exist. */
+ if (stat (output_dir, &statbuf) < 0 && errno == ENOENT)
+ if (mkdir (output_dir, S_IRUSR | S_IWUSR | S_IXUSR
+ | S_IRGRP | S_IWGRP | S_IXGRP
+ | S_IROTH | S_IWOTH | S_IXOTH) < 0)
+ {
+ error (0, errno, _("failed to create directory \"%s\""), output_dir);
+ free (output_dir);
+ goto quit2;
+ }
+
+ output_file =
+ xconcatenated_filename (output_dir, resource_name, ".resources.dll");
+
+ free (output_dir);
+ }
+
+ /* Compute the class name. This code must be kept consistent with intl.cs,
+ function InstantiateResourceSet(). */
+ {
+ char *class_name_part1 = construct_class_name (resource_name);
+ char *p;
+
+ class_name =
+ XNMALLOC (strlen (class_name_part1) + 1 + strlen (culture_name) + 1, char);
+ sprintf (class_name, "%s_%s", class_name_part1, culture_name);
+ for (p = class_name + strlen (class_name_part1) + 1; *p != '\0'; p++)
+ if (*p == '-')
+ *p = '_';
+ free (class_name_part1);
+ }
+
+ /* Compute the temporary C# file name. It must end in ".cs", so that
+ the C# compiler recognizes that it is C# source code. */
+ csharp_file_name =
+ xconcatenated_filename (tmpdir->dir_name, "resset.cs", NULL);
+
+ /* Create the C# file. */
+ register_temp_file (tmpdir, csharp_file_name);
+ csharp_file = fopen_temp (csharp_file_name, "w");
+ if (csharp_file == NULL)
+ {
+ error (0, errno, _("failed to create \"%s\""), csharp_file_name);
+ unregister_temp_file (tmpdir, csharp_file_name);
+ goto quit3;
+ }
+
+ write_csharp_code (csharp_file, culture_name, class_name, mlp);
+
+ if (fwriteerror_temp (csharp_file))
+ {
+ error (0, errno, _("error while writing \"%s\" file"), csharp_file_name);
+ goto quit3;
+ }
+
+ /* Make it possible to override the .dll location. This is
+ necessary for running the testsuite before "make install". */
+ gettextlibdir = getenv ("GETTEXTCSHARPLIBDIR");
+ if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
+ gettextlibdir = relocate (LIBDIR);
+
+ /* Compile the C# file to a .dll file. */
+ csharp_sources[0] = csharp_file_name;
+ libdirs[0] = gettextlibdir;
+ libraries[0] = "GNU.Gettext";
+ if (compile_csharp_class (csharp_sources, 1, libdirs, 1, libraries, 1,
+ output_file, true, false, verbose > 0))
+ {
+ if (!verbose)
+ error (0, 0, _("compilation of C# class failed, please try --verbose"));
+ else
+ error (0, 0, _("compilation of C# class failed"));
+ goto quit3;
+ }
+
+ retval = 0;
+
+ quit3:
+ free (csharp_file_name);
+ free (class_name);
+ free (output_file);
+ quit2:
+ free (culture_name);
+ cleanup_temp_dir (tmpdir);
+ quit1:
+ return retval;
+}
diff --git a/gettext-tools/src/write-csharp.h b/gettext-tools/src/write-csharp.h
new file mode 100644
index 0000000..55b71fb
--- /dev/null
+++ b/gettext-tools/src/write-csharp.h
@@ -0,0 +1,35 @@
+/* Writing C# satellite assemblies.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_CSHARP_H
+#define _WRITE_CSHARP_H
+
+#include "message.h"
+
+/* Write a C# satellite assembly DLL file. mlp is a list containing the
+ messages to be output. resource_name is the name of the resource
+ (with dot separators), locale_name is the locale name (with underscore
+ separators) or NULL, directory is the base directory.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_csharp (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *resource_name,
+ const char *locale_name,
+ const char *directory);
+
+#endif /* _WRITE_CSHARP_H */
diff --git a/gettext-tools/src/write-desktop.c b/gettext-tools/src/write-desktop.c
new file mode 100644
index 0000000..dd3fb60
--- /dev/null
+++ b/gettext-tools/src/write-desktop.c
@@ -0,0 +1,225 @@
+/* Writing Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 Free Software Foundation, Inc.
+ This file was written by Daiki Ueno <ueno@gnu.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-desktop.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "error.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-desktop.h"
+#include "fwriteerror.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+typedef struct msgfmt_desktop_reader_ty msgfmt_desktop_reader_ty;
+struct msgfmt_desktop_reader_ty
+{
+ DESKTOP_READER_TY
+ string_list_ty *languages;
+ message_list_ty **messages;
+ hash_table *keywords;
+ FILE *output_file;
+};
+
+static void
+msgfmt_desktop_handle_group (struct desktop_reader_ty *reader,
+ const char *group)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fprintf (msgfmt_reader->output_file, "[%s]\n", group);
+}
+
+static void
+msgfmt_desktop_handle_pair (desktop_reader_ty *reader,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+ void *keyword_value;
+
+ if (!locale)
+ {
+ /* Write translated pair, if any. */
+ if (hash_find_entry (msgfmt_reader->keywords, key, strlen (key),
+ &keyword_value) == 0)
+ {
+ bool is_list = (bool) keyword_value;
+ char *unescaped = desktop_unescape_string (value, is_list);
+ size_t i;
+
+ for (i = 0; i < msgfmt_reader->languages->nitems; i++)
+ {
+ const char *language = msgfmt_reader->languages->item[i];
+ message_list_ty *mlp = msgfmt_reader->messages[i];
+ message_ty *mp;
+
+ mp = message_list_search (mlp, NULL, unescaped);
+ if (mp && *mp->msgstr != '\0')
+ {
+ char *escaped;
+
+ escaped = desktop_escape_string (mp->msgstr, is_list);
+ fprintf (msgfmt_reader->output_file,
+ "%s[%s]=%s\n",
+ key, language, escaped);
+ free (escaped);
+ }
+ }
+ free (unescaped);
+ }
+
+ /* Write untranslated pair. */
+ fprintf (msgfmt_reader->output_file, "%s=%s\n", key, value);
+ }
+ else
+ /* Preserve already translated pair. */
+ fprintf (msgfmt_reader->output_file, "%s[%s]=%s\n", key, locale, value);
+}
+
+static void
+msgfmt_desktop_handle_comment (struct desktop_reader_ty *reader, const char *s)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fputc ('#', msgfmt_reader->output_file);
+ fputs (s, msgfmt_reader->output_file);
+ fputc ('\n', msgfmt_reader->output_file);
+}
+
+static void
+msgfmt_desktop_handle_blank (struct desktop_reader_ty *reader, const char *s)
+{
+ msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ fputs (s, msgfmt_reader->output_file);
+ fputc ('\n', msgfmt_reader->output_file);
+}
+
+desktop_reader_class_ty msgfmt_methods =
+ {
+ sizeof (msgfmt_desktop_reader_ty),
+ NULL,
+ NULL,
+ msgfmt_desktop_handle_group,
+ msgfmt_desktop_handle_pair,
+ msgfmt_desktop_handle_comment,
+ msgfmt_desktop_handle_blank
+ };
+
+int
+msgdomain_write_desktop_bulk (string_list_ty *languages,
+ message_list_ty **messages,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ desktop_reader_ty *reader;
+ msgfmt_desktop_reader_ty *msgfmt_reader;
+ FILE *template_file;
+
+ reader = desktop_reader_alloc (&msgfmt_methods);
+ msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
+
+ msgfmt_reader->languages = languages;
+ msgfmt_reader->messages = messages;
+ msgfmt_reader->keywords = keywords;
+
+ if (strcmp (file_name, "-") == 0)
+ msgfmt_reader->output_file = stdout;
+ else
+ {
+ msgfmt_reader->output_file = fopen (file_name, "w");
+ if (msgfmt_reader->output_file == NULL)
+ {
+ desktop_reader_free (reader);
+ error (EXIT_SUCCESS,
+ errno, _("error while opening \"%s\" for writing"),
+ file_name);
+ return 1;
+ }
+ }
+
+ template_file = fopen (template_file_name, "r");
+ if (template_file == NULL)
+ {
+ desktop_reader_free (reader);
+ error (EXIT_SUCCESS,
+ errno, _("error while opening \"%s\" for reading"),
+ template_file_name);
+ return 1;
+ }
+
+ desktop_parse (reader, template_file, template_file_name, template_file_name);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (msgfmt_reader->output_file))
+ error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ file_name);
+
+ desktop_reader_free (reader);
+
+ return 0;
+}
+
+int
+msgdomain_write_desktop (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ string_list_ty *languages;
+ message_list_ty **messages;
+ int retval;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ languages = string_list_alloc ();
+ string_list_append (languages, locale_name);
+
+ messages = XNMALLOC (1, message_list_ty *);
+ messages[0] = mlp;
+
+ retval = msgdomain_write_desktop_bulk (languages,
+ messages,
+ template_file_name,
+ keywords,
+ file_name);
+
+ string_list_free (languages);
+ free (messages);
+
+ return retval;
+}
diff --git a/gettext-tools/src/write-desktop.h b/gettext-tools/src/write-desktop.h
new file mode 100644
index 0000000..028b441
--- /dev/null
+++ b/gettext-tools/src/write-desktop.h
@@ -0,0 +1,51 @@
+/* Reading Desktop Entry files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014 Free Software Foundation, Inc.
+ This file was written by Daiki Ueno <ueno@gnu.org>.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_DESKTOP_H
+#define _WRITE_DESKTOP_H
+
+#include "message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Write a Desktop Entry file. mlp is a list containing the messages
+ to be output. locale_name is the locale name. template_file_name
+ is the template file. file_name is the output file. Return 0 if
+ ok, nonzero on error. */
+extern int
+ msgdomain_write_desktop (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *locale_name,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
+
+extern int
+ msgdomain_write_desktop_bulk (string_list_ty *languages,
+ message_list_ty **messages,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WRITE_DESKTOP_H */
diff --git a/gettext-tools/src/write-java.c b/gettext-tools/src/write-java.c
new file mode 100644
index 0000000..9cf4054
--- /dev/null
+++ b/gettext-tools/src/write-java.c
@@ -0,0 +1,1234 @@
+/* Writing Java ResourceBundles.
+ Copyright (C) 2001-2003, 2005-2010 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "write-java.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#if !defined S_ISDIR && defined S_IFDIR
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !S_IRUSR && S_IREAD
+# define S_IRUSR S_IREAD
+#endif
+#if !S_IRUSR
+# define S_IRUSR 00400
+#endif
+#if !S_IWUSR && S_IWRITE
+# define S_IWUSR S_IWRITE
+#endif
+#if !S_IWUSR
+# define S_IWUSR 00200
+#endif
+#if !S_IXUSR && S_IEXEC
+# define S_IXUSR S_IEXEC
+#endif
+#if !S_IXUSR
+# define S_IXUSR 00100
+#endif
+
+#include "c-ctype.h"
+#include "error.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "javacomp.h"
+#include "message.h"
+#include "msgfmt.h"
+#include "msgl-iconv.h"
+#include "plural-exp.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "minmax.h"
+#include "concat-filename.h"
+#include "fwriteerror.h"
+#include "clean-temp.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Check that the resource name is a valid Java class name. To simplify
+ things, we allow only ASCII characters in the class name.
+ Return the number of dots in the class name, or -1 if not OK. */
+static int
+check_resource_name (const char *name)
+{
+ int ndots = 0;
+ const char *p = name;
+
+ for (;;)
+ {
+ /* First character, see Character.isJavaIdentifierStart. */
+ if (!(c_isalpha (*p) || (*p == '$') || (*p == '_')))
+ return -1;
+ /* Following characters, see Character.isJavaIdentifierPart. */
+ do
+ p++;
+ while (c_isalpha (*p) || (*p == '$') || (*p == '_') || c_isdigit (*p));
+ if (*p == '\0')
+ break;
+ if (*p != '.')
+ return -1;
+ p++;
+ ndots++;
+ }
+ return ndots;
+}
+
+
+/* Return the Java hash code of a string mod 2^31.
+ The Java String.hashCode() function returns the same values across
+ Java implementations.
+ (See http://www.javasoft.com/docs/books/jls/clarify.html)
+ It returns a signed 32-bit integer. We add a mod 2^31 afterwards;
+ this removes one bit but greatly simplifies the following "mod hash_size"
+ and "mod (hash_size - 2)" operations. */
+static unsigned int
+string_hashcode (const char *str)
+{
+ const char *str_limit = str + strlen (str);
+ int hash = 0;
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc < 0x10000)
+ /* Single UCS-2 'char'. */
+ hash = 31 * hash + uc;
+ else
+ {
+ /* UTF-16 surrogate: two 'char's. */
+ ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
+ ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+ hash = 31 * hash + uc1;
+ hash = 31 * hash + uc2;
+ }
+ }
+ return hash & 0x7fffffff;
+}
+
+
+/* Return the Java hash code of a (msgctxt, msgid) pair mod 2^31. */
+static unsigned int
+msgid_hashcode (const char *msgctxt, const char *msgid)
+{
+ if (msgctxt == NULL)
+ return string_hashcode (msgid);
+ else
+ {
+ size_t msgctxt_len = strlen (msgctxt);
+ size_t msgid_len = strlen (msgid);
+ size_t combined_len = msgctxt_len + 1 + msgid_len;
+ char *combined;
+ unsigned int result;
+
+ combined = (char *) xmalloca (combined_len);
+ memcpy (combined, msgctxt, msgctxt_len);
+ combined[msgctxt_len] = MSGCTXT_SEPARATOR;
+ memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
+
+ result = string_hashcode (combined);
+
+ freea (combined);
+
+ return result;
+ }
+}
+
+
+/* Compute a good hash table size for the given set of msgids. */
+static unsigned int
+compute_hashsize (message_list_ty *mlp, bool *collisionp)
+{
+ /* This is an O(n^2) algorithm, but should be sufficient because few
+ programs have more than 1000 messages in a single domain. */
+#define XXN 3 /* can be tweaked */
+#define XXS 3 /* can be tweaked */
+ unsigned int n = mlp->nitems;
+ unsigned int *hashcodes =
+ (unsigned int *) xmalloca (n * sizeof (unsigned int));
+ unsigned int hashsize;
+ unsigned int best_hashsize;
+ unsigned int best_score;
+ size_t j;
+
+ for (j = 0; j < n; j++)
+ hashcodes[j] = msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
+
+ /* Try all numbers between n and 3*n. The score depends on the size of the
+ table -- the smaller the better -- and the number of collision lookups,
+ i.e. total number of times that 1 + (hashcode % (hashsize - 2))
+ is added to the index during lookup. If there are collisions, only odd
+ hashsize values are allowed. */
+ best_hashsize = 0;
+ best_score = UINT_MAX;
+ for (hashsize = n; hashsize <= XXN * n; hashsize++)
+ {
+ char *bitmap;
+ unsigned int score;
+
+ /* Premature end of the loop if all future scores are known to be
+ larger than the already reached best_score. This relies on the
+ ascending loop and on the fact that score >= hashsize. */
+ if (hashsize >= best_score)
+ break;
+
+ bitmap = XNMALLOC (hashsize, char);
+ memset (bitmap, 0, hashsize);
+
+ score = 0;
+ for (j = 0; j < n; j++)
+ {
+ unsigned int idx = hashcodes[j] % hashsize;
+
+ if (bitmap[idx] != 0)
+ {
+ /* Collision. Cannot deal with it if hashsize is even. */
+ if ((hashsize % 2) == 0)
+ /* Try next hashsize. */
+ goto bad_hashsize;
+ else
+ {
+ unsigned int idx0 = idx;
+ unsigned int incr = 1 + (hashcodes[j] % (hashsize - 2));
+ score += 2; /* Big penalty for the additional division */
+ do
+ {
+ score++; /* Small penalty for each loop round */
+ idx += incr;
+ if (idx >= hashsize)
+ idx -= hashsize;
+ if (idx == idx0)
+ /* Searching for a hole, we performed a whole round
+ across the table. This happens particularly
+ frequently if gcd(hashsize,incr) > 1. Try next
+ hashsize. */
+ goto bad_hashsize;
+ }
+ while (bitmap[idx] != 0);
+ }
+ }
+ bitmap[idx] = 1;
+ }
+
+ /* Big hashsize also gives a penalty. */
+ score = XXS * score + hashsize;
+
+ /* If for any incr between 1 and hashsize - 2, an whole round
+ (idx0, idx0 + incr, ...) is occupied, and the lookup function
+ must deal with collisions, then some inputs would lead to
+ an endless loop in the lookup function. */
+ if (score > hashsize)
+ {
+ unsigned int incr;
+
+ /* Since the set { idx0, idx0 + incr, ... } depends only on idx0
+ and gcd(hashsize,incr), we only need to conside incr that
+ divides hashsize. */
+ for (incr = 1; incr <= hashsize / 2; incr++)
+ if ((hashsize % incr) == 0)
+ {
+ unsigned int idx0;
+
+ for (idx0 = 0; idx0 < incr; idx0++)
+ {
+ bool full = true;
+ unsigned int idx;
+
+ for (idx = idx0; idx < hashsize; idx += incr)
+ if (bitmap[idx] == 0)
+ {
+ full = false;
+ break;
+ }
+ if (full)
+ /* A whole round is occupied. */
+ goto bad_hashsize;
+ }
+ }
+ }
+
+ if (false)
+ bad_hashsize:
+ score = UINT_MAX;
+
+ free (bitmap);
+
+ if (score < best_score)
+ {
+ best_score = score;
+ best_hashsize = hashsize;
+ }
+ }
+ if (best_hashsize == 0 || best_score < best_hashsize)
+ abort ();
+
+ freea (hashcodes);
+
+ /* There are collisions if and only if best_score > best_hashsize. */
+ *collisionp = (best_score > best_hashsize);
+ return best_hashsize;
+}
+
+
+struct table_item { unsigned int index; message_ty *mp; };
+
+static int
+compare_index (const void *pval1, const void *pval2)
+{
+ return (int)((const struct table_item *) pval1)->index
+ - (int)((const struct table_item *) pval2)->index;
+}
+
+/* Compute the list of messages and table indices, sorted according to the
+ indices. */
+static struct table_item *
+compute_table_items (message_list_ty *mlp, unsigned int hashsize)
+{
+ unsigned int n = mlp->nitems;
+ struct table_item *arr = XNMALLOC (n, struct table_item);
+ char *bitmap;
+ size_t j;
+
+ bitmap = XNMALLOC (hashsize, char);
+ memset (bitmap, 0, hashsize);
+
+ for (j = 0; j < n; j++)
+ {
+ unsigned int hashcode =
+ msgid_hashcode (mlp->item[j]->msgctxt, mlp->item[j]->msgid);
+ unsigned int idx = hashcode % hashsize;
+
+ if (bitmap[idx] != 0)
+ {
+ unsigned int incr = 1 + (hashcode % (hashsize - 2));
+ do
+ {
+ idx += incr;
+ if (idx >= hashsize)
+ idx -= hashsize;
+ }
+ while (bitmap[idx] != 0);
+ }
+ bitmap[idx] = 1;
+
+ arr[j].index = idx;
+ arr[j].mp = mlp->item[j];
+ }
+
+ free (bitmap);
+
+ qsort (arr, n, sizeof (arr[0]), compare_index);
+
+ return arr;
+}
+
+
+/* Write a string in Java Unicode notation to the given stream. */
+static void
+write_java_string (FILE *stream, const char *str)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ const char *str_limit = str + strlen (str);
+
+ fprintf (stream, "\"");
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc < 0x10000)
+ {
+ /* Single UCS-2 'char'. */
+ if (uc == 0x000a)
+ fprintf (stream, "\\n");
+ else if (uc == 0x000d)
+ fprintf (stream, "\\r");
+ else if (uc == 0x0022)
+ fprintf (stream, "\\\"");
+ else if (uc == 0x005c)
+ fprintf (stream, "\\\\");
+ else if (uc >= 0x0020 && uc < 0x007f)
+ fprintf (stream, "%c", (int) uc);
+ else
+ fprintf (stream, "\\u%c%c%c%c",
+ hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
+ hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
+ }
+ else
+ {
+ /* UTF-16 surrogate: two 'char's. */
+ ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
+ ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+ fprintf (stream, "\\u%c%c%c%c",
+ hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
+ hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
+ fprintf (stream, "\\u%c%c%c%c",
+ hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
+ hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
+ }
+ }
+ fprintf (stream, "\"");
+}
+
+
+/* Write a (msgctxt, msgid) pair as a string in Java Unicode notation to the
+ given stream. */
+static void
+write_java_msgid (FILE *stream, message_ty *mp)
+{
+ const char *msgctxt = mp->msgctxt;
+ const char *msgid = mp->msgid;
+
+ if (msgctxt == NULL)
+ write_java_string (stream, msgid);
+ else
+ {
+ size_t msgctxt_len = strlen (msgctxt);
+ size_t msgid_len = strlen (msgid);
+ size_t combined_len = msgctxt_len + 1 + msgid_len;
+ char *combined;
+
+ combined = (char *) xmalloca (combined_len);
+ memcpy (combined, msgctxt, msgctxt_len);
+ combined[msgctxt_len] = MSGCTXT_SEPARATOR;
+ memcpy (combined + msgctxt_len + 1, msgid, msgid_len + 1);
+
+ write_java_string (stream, combined);
+
+ freea (combined);
+ }
+}
+
+
+/* Write Java code that returns the value for a message. If the message
+ has plural forms, it is an expression of type String[], otherwise it is
+ an expression of type String. */
+static void
+write_java_msgstr (FILE *stream, message_ty *mp)
+{
+ if (mp->msgid_plural != NULL)
+ {
+ bool first;
+ const char *p;
+
+ fprintf (stream, "new java.lang.String[] { ");
+ for (p = mp->msgstr, first = true;
+ p < mp->msgstr + mp->msgstr_len;
+ p += strlen (p) + 1, first = false)
+ {
+ if (!first)
+ fprintf (stream, ", ");
+ write_java_string (stream, p);
+ }
+ fprintf (stream, " }");
+ }
+ else
+ {
+ if (mp->msgstr_len != strlen (mp->msgstr) + 1)
+ abort ();
+
+ write_java_string (stream, mp->msgstr);
+ }
+}
+
+
+/* Writes the body of the function which returns the local value for a key
+ named 'msgid'. */
+static void
+write_lookup_code (FILE *stream, unsigned int hashsize, bool collisions)
+{
+ fprintf (stream, " int hash_val = msgid.hashCode() & 0x7fffffff;\n");
+ fprintf (stream, " int idx = (hash_val %% %d) << 1;\n", hashsize);
+ if (collisions)
+ {
+ fprintf (stream, " {\n");
+ fprintf (stream, " java.lang.Object found = table[idx];\n");
+ fprintf (stream, " if (found == null)\n");
+ fprintf (stream, " return null;\n");
+ fprintf (stream, " if (msgid.equals(found))\n");
+ fprintf (stream, " return table[idx + 1];\n");
+ fprintf (stream, " }\n");
+ fprintf (stream, " int incr = ((hash_val %% %d) + 1) << 1;\n",
+ hashsize - 2);
+ fprintf (stream, " for (;;) {\n");
+ fprintf (stream, " idx += incr;\n");
+ fprintf (stream, " if (idx >= %d)\n", 2 * hashsize);
+ fprintf (stream, " idx -= %d;\n", 2 * hashsize);
+ fprintf (stream, " java.lang.Object found = table[idx];\n");
+ fprintf (stream, " if (found == null)\n");
+ fprintf (stream, " return null;\n");
+ fprintf (stream, " if (msgid.equals(found))\n");
+ fprintf (stream, " return table[idx + 1];\n");
+ fprintf (stream, " }\n");
+ }
+ else
+ {
+ fprintf (stream, " java.lang.Object found = table[idx];\n");
+ fprintf (stream, " if (found != null && msgid.equals(found))\n");
+ fprintf (stream, " return table[idx + 1];\n");
+ fprintf (stream, " return null;\n");
+ }
+}
+
+
+/* Tests whether a plural expression, evaluated according to the C rules,
+ can only produce the values 0 and 1. */
+static bool
+is_expression_boolean (struct expression *exp)
+{
+ switch (exp->operation)
+ {
+ case var:
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ return false;
+ case lnot:
+ case less_than:
+ case greater_than:
+ case less_or_equal:
+ case greater_or_equal:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ return true;
+ case num:
+ return (exp->val.num == 0 || exp->val.num == 1);
+ case qmop:
+ return is_expression_boolean (exp->val.args[1])
+ && is_expression_boolean (exp->val.args[2]);
+ default:
+ abort ();
+ }
+}
+
+
+/* Write Java code that evaluates a plural expression according to the C rules.
+ The variable is called 'n'. */
+static void
+write_java_expression (FILE *stream, const struct expression *exp, bool as_boolean)
+{
+ /* We use parentheses everywhere. This frees us from tracking the priority
+ of arithmetic operators. */
+ if (as_boolean)
+ {
+ /* Emit a Java expression of type 'boolean'. */
+ switch (exp->operation)
+ {
+ case num:
+ fprintf (stream, "%s", exp->val.num ? "true" : "false");
+ return;
+ case lnot:
+ fprintf (stream, "(!");
+ write_java_expression (stream, exp->val.args[0], true);
+ fprintf (stream, ")");
+ return;
+ case less_than:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " < ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case greater_than:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " > ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case less_or_equal:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " <= ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case greater_or_equal:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " >= ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case equal:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " == ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case not_equal:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " != ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case land:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " && ");
+ write_java_expression (stream, exp->val.args[1], true);
+ fprintf (stream, ")");
+ return;
+ case lor:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " || ");
+ write_java_expression (stream, exp->val.args[1], true);
+ fprintf (stream, ")");
+ return;
+ case qmop:
+ if (is_expression_boolean (exp->val.args[1])
+ && is_expression_boolean (exp->val.args[2]))
+ {
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " ? ");
+ write_java_expression (stream, exp->val.args[1], true);
+ fprintf (stream, " : ");
+ write_java_expression (stream, exp->val.args[2], true);
+ fprintf (stream, ")");
+ return;
+ }
+ /*FALLTHROUGH*/
+ case var:
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp, false);
+ fprintf (stream, " != 0)");
+ return;
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ /* Emit a Java expression of type 'long'. */
+ switch (exp->operation)
+ {
+ case var:
+ fprintf (stream, "n");
+ return;
+ case num:
+ fprintf (stream, "%lu", exp->val.num);
+ return;
+ case mult:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " * ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case divide:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " / ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case module:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " %% ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case plus:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " + ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case minus:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], false);
+ fprintf (stream, " - ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, ")");
+ return;
+ case qmop:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp->val.args[0], true);
+ fprintf (stream, " ? ");
+ write_java_expression (stream, exp->val.args[1], false);
+ fprintf (stream, " : ");
+ write_java_expression (stream, exp->val.args[2], false);
+ fprintf (stream, ")");
+ return;
+ case lnot:
+ case less_than:
+ case greater_than:
+ case less_or_equal:
+ case greater_or_equal:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ fprintf (stream, "(");
+ write_java_expression (stream, exp, true);
+ fprintf (stream, " ? 1 : 0)");
+ return;
+ default:
+ abort ();
+ }
+ }
+}
+
+
+/* Write the Java initialization statements for the Java 1.1.x case,
+ for items j, start_index <= j < end_index. */
+static void
+write_java1_init_statements (FILE *stream, message_list_ty *mlp,
+ size_t start_index, size_t end_index)
+{
+ size_t j;
+
+ for (j = start_index; j < end_index; j++)
+ {
+ fprintf (stream, " t.put(");
+ write_java_msgid (stream, mlp->item[j]);
+ fprintf (stream, ",");
+ write_java_msgstr (stream, mlp->item[j]);
+ fprintf (stream, ");\n");
+ }
+}
+
+
+/* Write the Java initialization statements for the Java 2 case,
+ for items j, start_index <= j < end_index. */
+static void
+write_java2_init_statements (FILE *stream, message_list_ty *mlp,
+ const struct table_item *table_items,
+ size_t start_index, size_t end_index)
+{
+ size_t j;
+
+ for (j = start_index; j < end_index; j++)
+ {
+ const struct table_item *ti = &table_items[j];
+
+ fprintf (stream, " t[%d] = ", 2 * ti->index);
+ write_java_msgid (stream, ti->mp);
+ fprintf (stream, ";\n");
+ fprintf (stream, " t[%d] = ", 2 * ti->index + 1);
+ write_java_msgstr (stream, ti->mp);
+ fprintf (stream, ";\n");
+ }
+}
+
+
+/* Write the Java code for the ResourceBundle subclass to the given stream.
+ Note that we use fully qualified class names and no "import" statements,
+ because applications can have their own classes called X.Y.ResourceBundle
+ or X.Y.String. */
+static void
+write_java_code (FILE *stream, const char *class_name, message_list_ty *mlp,
+ bool assume_java2)
+{
+ const char *last_dot;
+ unsigned int plurals;
+ size_t j;
+
+ fprintf (stream,
+ "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
+ last_dot = strrchr (class_name, '.');
+ if (last_dot != NULL)
+ {
+ fprintf (stream, "package ");
+ fwrite (class_name, 1, last_dot - class_name, stream);
+ fprintf (stream, ";\npublic class %s", last_dot + 1);
+ }
+ else
+ fprintf (stream, "public class %s", class_name);
+ fprintf (stream, " extends java.util.ResourceBundle {\n");
+
+ /* Determine whether there are plural messages. */
+ plurals = 0;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ plurals++;
+
+ if (assume_java2)
+ {
+ unsigned int hashsize;
+ bool collisions;
+ struct table_item *table_items;
+ const char *table_eltype;
+
+ /* Determine the hash table size and whether it leads to collisions. */
+ hashsize = compute_hashsize (mlp, &collisions);
+
+ /* Determines which indices in the table contain a message. The others
+ are null. */
+ table_items = compute_table_items (mlp, hashsize);
+
+ /* Emit the table of pairs (msgid, msgstr). If there are plurals,
+ it is of type Object[], otherwise of type String[]. We use a static
+ code block because that makes less code: The Java compilers also
+ generate code for the 'null' entries, which is dumb. */
+ table_eltype = (plurals ? "java.lang.Object" : "java.lang.String");
+ fprintf (stream, " private static final %s[] table;\n", table_eltype);
+ {
+ /* With the Sun javac compiler, each assignment takes 5 to 8 bytes
+ of bytecode, therefore for each message, up to 16 bytes are needed.
+ Since the bytecode of every method, including the <clinit> method
+ that contains the static initializers, is limited to 64 KB, only ca,
+ 65536 / 16 = 4096 messages can be initialized in a single method.
+ Account for other Java compilers and for plurals by limiting it to
+ 1000. */
+ const size_t max_items_per_method = 1000;
+
+ if (mlp->nitems > max_items_per_method)
+ {
+ unsigned int k;
+ size_t start_j;
+ size_t end_j;
+
+ for (k = 0, start_j = 0, end_j = start_j + max_items_per_method;
+ start_j < mlp->nitems;
+ k++, start_j = end_j, end_j = start_j + max_items_per_method)
+ {
+ fprintf (stream, " static void clinit_part_%u (%s[] t) {\n",
+ k, table_eltype);
+ write_java2_init_statements (stream, mlp, table_items,
+ start_j, MIN (end_j, mlp->nitems));
+ fprintf (stream, " }\n");
+ }
+ }
+ fprintf (stream, " static {\n");
+ fprintf (stream, " %s[] t = new %s[%d];\n", table_eltype,
+ table_eltype, 2 * hashsize);
+ if (mlp->nitems > max_items_per_method)
+ {
+ unsigned int k;
+ size_t start_j;
+
+ for (k = 0, start_j = 0;
+ start_j < mlp->nitems;
+ k++, start_j += max_items_per_method)
+ fprintf (stream, " clinit_part_%u(t);\n", k);
+ }
+ else
+ write_java2_init_statements (stream, mlp, table_items,
+ 0, mlp->nitems);
+ fprintf (stream, " table = t;\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the msgid_plural strings. Only used by msgunfmt. */
+ if (plurals)
+ {
+ bool first;
+ fprintf (stream, " public static final java.lang.String[] get_msgid_plural_table () {\n");
+ fprintf (stream, " return new java.lang.String[] { ");
+ first = true;
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ struct table_item *ti = &table_items[j];
+ if (ti->mp->msgid_plural != NULL)
+ {
+ if (!first)
+ fprintf (stream, ", ");
+ write_java_string (stream, ti->mp->msgid_plural);
+ first = false;
+ }
+ }
+ fprintf (stream, " };\n");
+ fprintf (stream, " }\n");
+ }
+
+ if (plurals)
+ {
+ /* Emit the lookup function. It is a common subroutine for
+ handleGetObject and ngettext. */
+ fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
+ write_lookup_code (stream, hashsize, collisions);
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the handleGetObject function. It is declared abstract in
+ ResourceBundle. It implements a local version of gettext. */
+ fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
+ if (plurals)
+ {
+ fprintf (stream, " java.lang.Object value = lookup(msgid);\n");
+ fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
+ }
+ else
+ write_lookup_code (stream, hashsize, collisions);
+ fprintf (stream, " }\n");
+
+ /* Emit the getKeys function. It is declared abstract in ResourceBundle.
+ The inner class is not avoidable. */
+ fprintf (stream, " public java.util.Enumeration getKeys () {\n");
+ fprintf (stream, " return\n");
+ fprintf (stream, " new java.util.Enumeration() {\n");
+ fprintf (stream, " private int idx = 0;\n");
+ fprintf (stream, " { while (idx < %d && table[idx] == null) idx += 2; }\n",
+ 2 * hashsize);
+ fprintf (stream, " public boolean hasMoreElements () {\n");
+ fprintf (stream, " return (idx < %d);\n", 2 * hashsize);
+ fprintf (stream, " }\n");
+ fprintf (stream, " public java.lang.Object nextElement () {\n");
+ fprintf (stream, " java.lang.Object key = table[idx];\n");
+ fprintf (stream, " do idx += 2; while (idx < %d && table[idx] == null);\n",
+ 2 * hashsize);
+ fprintf (stream, " return key;\n");
+ fprintf (stream, " }\n");
+ fprintf (stream, " };\n");
+ fprintf (stream, " }\n");
+ }
+ else
+ {
+ /* Java 1.1.x uses a different hash function. If compatibility with
+ this Java version is required, the hash table must be built at run time,
+ not at compile time. */
+ fprintf (stream, " private static final java.util.Hashtable table;\n");
+ {
+ /* With the Sun javac compiler, each 'put' call takes 9 to 11 bytes
+ of bytecode, therefore for each message, up to 11 bytes are needed.
+ Since the bytecode of every method, including the <clinit> method
+ that contains the static initializers, is limited to 64 KB, only ca,
+ 65536 / 11 = 5958 messages can be initialized in a single method.
+ Account for other Java compilers and for plurals by limiting it to
+ 1500. */
+ const size_t max_items_per_method = 1500;
+
+ if (mlp->nitems > max_items_per_method)
+ {
+ unsigned int k;
+ size_t start_j;
+ size_t end_j;
+
+ for (k = 0, start_j = 0, end_j = start_j + max_items_per_method;
+ start_j < mlp->nitems;
+ k++, start_j = end_j, end_j = start_j + max_items_per_method)
+ {
+ fprintf (stream, " static void clinit_part_%u (java.util.Hashtable t) {\n",
+ k);
+ write_java1_init_statements (stream, mlp,
+ start_j, MIN (end_j, mlp->nitems));
+ fprintf (stream, " }\n");
+ }
+ }
+ fprintf (stream, " static {\n");
+ fprintf (stream, " java.util.Hashtable t = new java.util.Hashtable();\n");
+ if (mlp->nitems > max_items_per_method)
+ {
+ unsigned int k;
+ size_t start_j;
+
+ for (k = 0, start_j = 0;
+ start_j < mlp->nitems;
+ k++, start_j += max_items_per_method)
+ fprintf (stream, " clinit_part_%u(t);\n", k);
+ }
+ else
+ write_java1_init_statements (stream, mlp, 0, mlp->nitems);
+ fprintf (stream, " table = t;\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the msgid_plural strings. Only used by msgunfmt. */
+ if (plurals)
+ {
+ fprintf (stream, " public static final java.util.Hashtable get_msgid_plural_table () {\n");
+ fprintf (stream, " java.util.Hashtable p = new java.util.Hashtable();\n");
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ {
+ fprintf (stream, " p.put(");
+ write_java_msgid (stream, mlp->item[j]);
+ fprintf (stream, ",");
+ write_java_string (stream, mlp->item[j]->msgid_plural);
+ fprintf (stream, ");\n");
+ }
+ fprintf (stream, " return p;\n");
+ fprintf (stream, " }\n");
+ }
+
+ if (plurals)
+ {
+ /* Emit the lookup function. It is a common subroutine for
+ handleGetObject and ngettext. */
+ fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
+ fprintf (stream, " return table.get(msgid);\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the handleGetObject function. It is declared abstract in
+ ResourceBundle. It implements a local version of gettext. */
+ fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
+ if (plurals)
+ {
+ fprintf (stream, " java.lang.Object value = table.get(msgid);\n");
+ fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
+ }
+ else
+ fprintf (stream, " return table.get(msgid);\n");
+ fprintf (stream, " }\n");
+
+ /* Emit the getKeys function. It is declared abstract in
+ ResourceBundle. */
+ fprintf (stream, " public java.util.Enumeration getKeys () {\n");
+ fprintf (stream, " return table.keys();\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the pluralEval function. It is a subroutine for ngettext. */
+ if (plurals)
+ {
+ message_ty *header_entry;
+ const struct expression *plural;
+ unsigned long int nplurals;
+
+ header_entry = message_list_search (mlp, NULL, "");
+ extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
+ &plural, &nplurals);
+
+ fprintf (stream, " public static long pluralEval (long n) {\n");
+ fprintf (stream, " return ");
+ write_java_expression (stream, plural, false);
+ fprintf (stream, ";\n");
+ fprintf (stream, " }\n");
+ }
+
+ /* Emit the getParent function. It is a subroutine for ngettext. */
+ fprintf (stream, " public java.util.ResourceBundle getParent () {\n");
+ fprintf (stream, " return parent;\n");
+ fprintf (stream, " }\n");
+
+ fprintf (stream, "}\n");
+}
+
+
+int
+msgdomain_write_java (message_list_ty *mlp, const char *canon_encoding,
+ const char *resource_name, const char *locale_name,
+ const char *directory,
+ bool assume_java2,
+ bool output_source)
+{
+ int retval;
+ struct temp_dir *tmpdir;
+ int ndots;
+ char *class_name;
+ char **subdirs;
+ char *java_file_name;
+ FILE *java_file;
+ const char *java_sources[1];
+ const char *source_dir_name;
+
+ /* If no entry for this resource/domain, don't even create the file. */
+ if (mlp->nitems == 0)
+ return 0;
+
+ retval = 1;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ if (output_source)
+ {
+ tmpdir = NULL;
+ source_dir_name = directory;
+ }
+ else
+ {
+ /* Create a temporary directory where we can put the Java file. */
+ tmpdir = create_temp_dir ("msg", NULL, false);
+ if (tmpdir == NULL)
+ goto quit1;
+ source_dir_name = tmpdir->dir_name;
+ }
+
+ /* Assign a default value to the resource name. */
+ if (resource_name == NULL)
+ resource_name = "Messages";
+
+ /* Prepare the list of subdirectories. */
+ ndots = check_resource_name (resource_name);
+ if (ndots < 0)
+ {
+ error (0, 0, _("not a valid Java class name: %s"), resource_name);
+ goto quit2;
+ }
+
+ if (locale_name != NULL)
+ class_name = xasprintf ("%s_%s", resource_name, locale_name);
+ else
+ class_name = xstrdup (resource_name);
+
+ subdirs = (ndots > 0 ? (char **) xmalloca (ndots * sizeof (char *)) : NULL);
+ {
+ const char *p;
+ const char *last_dir;
+ int i;
+
+ last_dir = source_dir_name;
+ p = resource_name;
+ for (i = 0; i < ndots; i++)
+ {
+ const char *q = strchr (p, '.');
+ size_t n = q - p;
+ char *part = (char *) xmalloca (n + 1);
+ memcpy (part, p, n);
+ part[n] = '\0';
+ subdirs[i] = xconcatenated_filename (last_dir, part, NULL);
+ freea (part);
+ last_dir = subdirs[i];
+ p = q + 1;
+ }
+
+ if (locale_name != NULL)
+ {
+ char *suffix = xasprintf ("_%s.java", locale_name);
+ java_file_name = xconcatenated_filename (last_dir, p, suffix);
+ free (suffix);
+ }
+ else
+ java_file_name = xconcatenated_filename (last_dir, p, ".java");
+ }
+
+ /* If OUTPUT_SOURCE, write the Java file in DIRECTORY and return. */
+ if (output_source)
+ {
+ int i;
+
+ for (i = 0; i < ndots; i++)
+ {
+ if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+ {
+ error (0, errno, _("failed to create \"%s\""), subdirs[i]);
+ goto quit3;
+ }
+ }
+
+ java_file = fopen (java_file_name, "w");
+ if (java_file == NULL)
+ {
+ error (0, errno, _("failed to create \"%s\""), java_file_name);
+ goto quit3;
+ }
+
+ write_java_code (java_file, class_name, mlp, assume_java2);
+
+ if (fwriteerror (java_file))
+ {
+ error (0, errno, _("error while writing \"%s\" file"),
+ java_file_name);
+ goto quit3;
+ }
+
+ retval = 0;
+ goto quit3;
+ }
+
+ /* Create the subdirectories. This is needed because some older Java
+ compilers verify that the source of class A.B.C really sits in a
+ directory whose name ends in /A/B. */
+ {
+ int i;
+
+ for (i = 0; i < ndots; i++)
+ {
+ register_temp_subdir (tmpdir, subdirs[i]);
+ if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+ {
+ error (0, errno, _("failed to create \"%s\""), subdirs[i]);
+ unregister_temp_subdir (tmpdir, subdirs[i]);
+ goto quit3;
+ }
+ }
+ }
+
+ /* Create the Java file. */
+ register_temp_file (tmpdir, java_file_name);
+ java_file = fopen_temp (java_file_name, "w");
+ if (java_file == NULL)
+ {
+ error (0, errno, _("failed to create \"%s\""), java_file_name);
+ unregister_temp_file (tmpdir, java_file_name);
+ goto quit3;
+ }
+
+ write_java_code (java_file, class_name, mlp, assume_java2);
+
+ if (fwriteerror_temp (java_file))
+ {
+ error (0, errno, _("error while writing \"%s\" file"), java_file_name);
+ goto quit3;
+ }
+
+ /* Compile the Java file to a .class file.
+ directory must be non-NULL, because when the -d option is omitted, the
+ Java compilers create the class files in the source file's directory -
+ which is in a temporary directory in our case. */
+ java_sources[0] = java_file_name;
+ if (compile_java_class (java_sources, 1, NULL, 0, "1.3", "1.1", directory,
+ true, false, true, verbose > 0))
+ {
+ if (!verbose)
+ error (0, 0, _("\
+compilation of Java class failed, please try --verbose or set $JAVAC"));
+ else
+ error (0, 0, _("\
+compilation of Java class failed, please try to set $JAVAC"));
+ goto quit3;
+ }
+
+ retval = 0;
+
+ quit3:
+ {
+ int i;
+ free (java_file_name);
+ for (i = 0; i < ndots; i++)
+ free (subdirs[i]);
+ }
+ freea (subdirs);
+ free (class_name);
+ quit2:
+ if (tmpdir != NULL)
+ cleanup_temp_dir (tmpdir);
+ quit1:
+ return retval;
+}
diff --git a/gettext-tools/src/write-java.h b/gettext-tools/src/write-java.h
new file mode 100644
index 0000000..4249edb
--- /dev/null
+++ b/gettext-tools/src/write-java.h
@@ -0,0 +1,39 @@
+/* Writing Java ResourceBundles.
+ Copyright (C) 2001-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_JAVA_H
+#define _WRITE_JAVA_H
+
+#include <stdbool.h>
+
+#include "message.h"
+
+/* Write a Java ResourceBundle class file. mlp is a list containing the
+ messages to be output. resource_name is the name of the resource
+ (with dot separators), locale_name is the locale name (with underscore
+ separators) or NULL, directory is the base directory.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_java (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *resource_name,
+ const char *locale_name,
+ const char *directory,
+ bool assume_java2,
+ bool output_source);
+
+#endif /* _WRITE_JAVA_H */
diff --git a/gettext-tools/src/write-mo.c b/gettext-tools/src/write-mo.c
new file mode 100644
index 0000000..ce27415
--- /dev/null
+++ b/gettext-tools/src/write-mo.c
@@ -0,0 +1,817 @@
+/* Writing binary .mo files.
+ Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "write-mo.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+/* These two include files describe the binary .mo format. */
+#include "gmo.h"
+#include "hash-string.h"
+
+#include "byteswap.h"
+#include "error.h"
+#include "hash.h"
+#include "message.h"
+#include "format.h"
+#include "xsize.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "binary-io.h"
+#include "fwriteerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define freea(p) /* nothing */
+
+/* Usually defined in <sys/param.h>. */
+#ifndef roundup
+# if defined __GNUC__ && __GNUC__ >= 2
+# define roundup(x, y) ({typeof(x) _x = (x); typeof(y) _y = (y); \
+ ((_x + _y - 1) / _y) * _y; })
+# else
+# define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+# endif /* GNU CC2 */
+#endif /* roundup */
+
+
+/* Alignment of strings in resulting .mo file. */
+size_t alignment;
+
+/* True if writing a .mo file in opposite endianness than the host. */
+bool byteswap;
+
+/* True if no hash table in .mo is wanted. */
+bool no_hash_table;
+
+
+/* Destructively changes the byte order of a 32-bit value in memory. */
+#define BSWAP32(x) (x) = bswap_32 (x)
+
+
+/* Indices into the strings contained in 'struct pre_message' and
+ 'struct pre_sysdep_message'. */
+enum
+{
+ M_ID = 0, /* msgid - the original string */
+ M_STR = 1 /* msgstr - the translated string */
+};
+
+/* An intermediate data structure representing a 'struct string_desc'. */
+struct pre_string
+{
+ size_t length;
+ const char *pointer;
+};
+
+/* An intermediate data structure representing a message. */
+struct pre_message
+{
+ struct pre_string str[2];
+ const char *id_plural;
+ size_t id_plural_len;
+};
+
+static int
+compare_id (const void *pval1, const void *pval2)
+{
+ return strcmp (((struct pre_message *) pval1)->str[M_ID].pointer,
+ ((struct pre_message *) pval2)->str[M_ID].pointer);
+}
+
+
+/* An intermediate data structure representing a 'struct sysdep_segment'. */
+struct pre_sysdep_segment
+{
+ size_t length;
+ const char *pointer;
+};
+
+/* An intermediate data structure representing a 'struct segment_pair'. */
+struct pre_segment_pair
+{
+ size_t segsize;
+ const char *segptr;
+ size_t sysdepref;
+};
+
+/* An intermediate data structure representing a 'struct sysdep_string'. */
+struct pre_sysdep_string
+{
+ unsigned int segmentcount;
+ struct pre_segment_pair segments[1];
+};
+
+/* An intermediate data structure representing a message with system dependent
+ strings. */
+struct pre_sysdep_message
+{
+ struct pre_sysdep_string *str[2];
+ const char *id_plural;
+ size_t id_plural_len;
+};
+
+/* Write the message list to the given open file. */
+static void
+write_table (FILE *output_file, message_list_ty *mlp)
+{
+ char **msgctid_arr;
+ size_t nstrings;
+ struct pre_message *msg_arr;
+ size_t n_sysdep_strings;
+ struct pre_sysdep_message *sysdep_msg_arr;
+ size_t n_sysdep_segments;
+ struct pre_sysdep_segment *sysdep_segments;
+ bool have_outdigits;
+ int major_revision;
+ int minor_revision;
+ bool omit_hash_table;
+ nls_uint32 hash_tab_size;
+ struct mo_file_header header; /* Header of the .mo file to be written. */
+ size_t header_size;
+ size_t offset;
+ struct string_desc *orig_tab;
+ struct string_desc *trans_tab;
+ size_t sysdep_tab_offset = 0;
+ size_t end_offset;
+ char *null;
+ size_t j, m;
+
+ /* First pass: Move the static string pairs into an array, for sorting,
+ and at the same time, compute the segments of the system dependent
+ strings. */
+ msgctid_arr = XNMALLOC (mlp->nitems, char *);
+ nstrings = 0;
+ msg_arr = XNMALLOC (mlp->nitems, struct pre_message);
+ n_sysdep_strings = 0;
+ sysdep_msg_arr = XNMALLOC (mlp->nitems, struct pre_sysdep_message);
+ n_sysdep_segments = 0;
+ sysdep_segments = NULL;
+ have_outdigits = false;
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+ size_t msgctlen;
+ char *msgctid;
+ struct interval *intervals[2];
+ size_t nintervals[2];
+
+ /* Concatenate mp->msgctxt and mp->msgid into msgctid. */
+ msgctlen = (mp->msgctxt != NULL ? strlen (mp->msgctxt) + 1 : 0);
+ msgctid = XNMALLOC (msgctlen + strlen (mp->msgid) + 1, char);
+ if (mp->msgctxt != NULL)
+ {
+ memcpy (msgctid, mp->msgctxt, msgctlen - 1);
+ msgctid[msgctlen - 1] = MSGCTXT_SEPARATOR;
+ }
+ strcpy (msgctid + msgctlen, mp->msgid);
+ msgctid_arr[j] = msgctid;
+
+ intervals[M_ID] = NULL;
+ nintervals[M_ID] = 0;
+ intervals[M_STR] = NULL;
+ nintervals[M_STR] = 0;
+
+ /* Test if mp contains system dependent strings and thus
+ requires the use of the .mo file minor revision 1. */
+ if (possible_format_p (mp->is_format[format_c])
+ || possible_format_p (mp->is_format[format_objc]))
+ {
+ /* Check whether msgid or msgstr contain ISO C 99 <inttypes.h>
+ format string directives. No need to check msgid_plural, because
+ it is not accessed by the [n]gettext() function family. */
+ const char *p_end;
+ const char *p;
+
+ get_sysdep_c_format_directives (mp->msgid, false,
+ &intervals[M_ID], &nintervals[M_ID]);
+ if (msgctlen > 0)
+ {
+ struct interval *id_intervals = intervals[M_ID];
+ size_t id_nintervals = nintervals[M_ID];
+
+ if (id_nintervals > 0)
+ {
+ unsigned int i;
+
+ for (i = 0; i < id_nintervals; i++)
+ {
+ id_intervals[i].startpos += msgctlen;
+ id_intervals[i].endpos += msgctlen;
+ }
+ }
+ }
+
+ p_end = mp->msgstr + mp->msgstr_len;
+ for (p = mp->msgstr; p < p_end; p += strlen (p) + 1)
+ {
+ struct interval *part_intervals;
+ size_t part_nintervals;
+
+ get_sysdep_c_format_directives (p, true,
+ &part_intervals,
+ &part_nintervals);
+ if (part_nintervals > 0)
+ {
+ size_t d = p - mp->msgstr;
+ unsigned int i;
+
+ intervals[M_STR] =
+ (struct interval *)
+ xrealloc (intervals[M_STR],
+ (nintervals[M_STR] + part_nintervals)
+ * sizeof (struct interval));
+ for (i = 0; i < part_nintervals; i++)
+ {
+ intervals[M_STR][nintervals[M_STR] + i].startpos =
+ d + part_intervals[i].startpos;
+ intervals[M_STR][nintervals[M_STR] + i].endpos =
+ d + part_intervals[i].endpos;
+ }
+ nintervals[M_STR] += part_nintervals;
+ }
+ }
+ }
+
+ if (nintervals[M_ID] > 0 || nintervals[M_STR] > 0)
+ {
+ /* System dependent string pair. */
+ for (m = 0; m < 2; m++)
+ {
+ struct pre_sysdep_string *pre =
+ (struct pre_sysdep_string *)
+ xmalloc (xsum (sizeof (struct pre_sysdep_string),
+ xtimes (nintervals[m],
+ sizeof (struct pre_segment_pair))));
+ const char *str;
+ size_t str_len;
+ size_t lastpos;
+ unsigned int i;
+
+ if (m == M_ID)
+ {
+ str = msgctid; /* concatenation of mp->msgctxt + mp->msgid */
+ str_len = strlen (msgctid) + 1;
+ }
+ else
+ {
+ str = mp->msgstr;
+ str_len = mp->msgstr_len;
+ }
+
+ lastpos = 0;
+ pre->segmentcount = nintervals[m];
+ for (i = 0; i < nintervals[m]; i++)
+ {
+ size_t length;
+ const char *pointer;
+ size_t r;
+
+ pre->segments[i].segptr = str + lastpos;
+ pre->segments[i].segsize = intervals[m][i].startpos - lastpos;
+
+ length = intervals[m][i].endpos - intervals[m][i].startpos;
+ pointer = str + intervals[m][i].startpos;
+ if (length >= 2
+ && pointer[0] == '<' && pointer[length - 1] == '>')
+ {
+ /* Skip the '<' and '>' markers. */
+ length -= 2;
+ pointer += 1;
+ }
+
+ for (r = 0; r < n_sysdep_segments; r++)
+ if (sysdep_segments[r].length == length
+ && memcmp (sysdep_segments[r].pointer, pointer, length)
+ == 0)
+ break;
+ if (r == n_sysdep_segments)
+ {
+ n_sysdep_segments++;
+ sysdep_segments =
+ (struct pre_sysdep_segment *)
+ xrealloc (sysdep_segments,
+ n_sysdep_segments
+ * sizeof (struct pre_sysdep_segment));
+ sysdep_segments[r].length = length;
+ sysdep_segments[r].pointer = pointer;
+ }
+
+ pre->segments[i].sysdepref = r;
+
+ if (length == 1 && *pointer == 'I')
+ have_outdigits = true;
+
+ lastpos = intervals[m][i].endpos;
+ }
+ pre->segments[i].segptr = str + lastpos;
+ pre->segments[i].segsize = str_len - lastpos;
+ pre->segments[i].sysdepref = SEGMENTS_END;
+
+ sysdep_msg_arr[n_sysdep_strings].str[m] = pre;
+ }
+
+ sysdep_msg_arr[n_sysdep_strings].id_plural = mp->msgid_plural;
+ sysdep_msg_arr[n_sysdep_strings].id_plural_len =
+ (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0);
+ n_sysdep_strings++;
+ }
+ else
+ {
+ /* Static string pair. */
+ msg_arr[nstrings].str[M_ID].pointer = msgctid;
+ msg_arr[nstrings].str[M_ID].length = strlen (msgctid) + 1;
+ msg_arr[nstrings].str[M_STR].pointer = mp->msgstr;
+ msg_arr[nstrings].str[M_STR].length = mp->msgstr_len;
+ msg_arr[nstrings].id_plural = mp->msgid_plural;
+ msg_arr[nstrings].id_plural_len =
+ (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0);
+ nstrings++;
+ }
+
+ for (m = 0; m < 2; m++)
+ if (intervals[m] != NULL)
+ free (intervals[m]);
+ }
+
+ /* Sort the table according to original string. */
+ if (nstrings > 0)
+ qsort (msg_arr, nstrings, sizeof (struct pre_message), compare_id);
+
+ /* We need major revision 1 if there are system dependent strings that use
+ "I" because older versions of gettext() crash when this occurs in a .mo
+ file. Otherwise use major revision 0. */
+ major_revision =
+ (have_outdigits ? MO_REVISION_NUMBER_WITH_SYSDEP_I : MO_REVISION_NUMBER);
+
+ /* We need minor revision 1 if there are system dependent strings.
+ Otherwise we choose minor revision 0 because it's supported by older
+ versions of libintl and revision 1 isn't. */
+ minor_revision = (n_sysdep_strings > 0 ? 1 : 0);
+
+ /* In minor revision >= 1, the hash table is obligatory. */
+ omit_hash_table = (no_hash_table && minor_revision == 0);
+
+ /* This should be explained:
+ Each string has an associate hashing value V, computed by a fixed
+ function. To locate the string we use open addressing with double
+ hashing. The first index will be V % M, where M is the size of the
+ hashing table. If no entry is found, iterating with a second,
+ independent hashing function takes place. This second value will
+ be 1 + V % (M - 2).
+ The approximate number of probes will be
+
+ for unsuccessful search: (1 - N / M) ^ -1
+ for successful search: - (N / M) ^ -1 * ln (1 - N / M)
+
+ where N is the number of keys.
+
+ If we now choose M to be the next prime bigger than 4 / 3 * N,
+ we get the values
+ 4 and 1.85 resp.
+ Because unsuccessful searches are unlikely this is a good value.
+ Formulas: [Knuth, The Art of Computer Programming, Volume 3,
+ Sorting and Searching, 1973, Addison Wesley] */
+ if (!omit_hash_table)
+ {
+ hash_tab_size = next_prime ((mlp->nitems * 4) / 3);
+ /* Ensure M > 2. */
+ if (hash_tab_size <= 2)
+ hash_tab_size = 3;
+ }
+ else
+ hash_tab_size = 0;
+
+
+ /* Second pass: Fill the structure describing the header. At the same time,
+ compute the sizes and offsets of the non-string parts of the file. */
+
+ /* Magic number. */
+ header.magic = _MAGIC;
+ /* Revision number of file format. */
+ header.revision = (major_revision << 16) + minor_revision;
+
+ header_size =
+ (minor_revision == 0
+ ? offsetof (struct mo_file_header, n_sysdep_segments)
+ : sizeof (struct mo_file_header));
+ offset = header_size;
+
+ /* Number of static string pairs. */
+ header.nstrings = nstrings;
+
+ /* Offset of table for original string offsets. */
+ header.orig_tab_offset = offset;
+ offset += nstrings * sizeof (struct string_desc);
+ orig_tab = XNMALLOC (nstrings, struct string_desc);
+
+ /* Offset of table for translated string offsets. */
+ header.trans_tab_offset = offset;
+ offset += nstrings * sizeof (struct string_desc);
+ trans_tab = XNMALLOC (nstrings, struct string_desc);
+
+ /* Size of hash table. */
+ header.hash_tab_size = hash_tab_size;
+ /* Offset of hash table. */
+ header.hash_tab_offset = offset;
+ offset += hash_tab_size * sizeof (nls_uint32);
+
+ if (minor_revision >= 1)
+ {
+ /* Size of table describing system dependent segments. */
+ header.n_sysdep_segments = n_sysdep_segments;
+ /* Offset of table describing system dependent segments. */
+ header.sysdep_segments_offset = offset;
+ offset += n_sysdep_segments * sizeof (struct sysdep_segment);
+
+ /* Number of system dependent string pairs. */
+ header.n_sysdep_strings = n_sysdep_strings;
+
+ /* Offset of table for original sysdep string offsets. */
+ header.orig_sysdep_tab_offset = offset;
+ offset += n_sysdep_strings * sizeof (nls_uint32);
+
+ /* Offset of table for translated sysdep string offsets. */
+ header.trans_sysdep_tab_offset = offset;
+ offset += n_sysdep_strings * sizeof (nls_uint32);
+
+ /* System dependent string descriptors. */
+ sysdep_tab_offset = offset;
+ for (m = 0; m < 2; m++)
+ for (j = 0; j < n_sysdep_strings; j++)
+ offset += sizeof (struct sysdep_string)
+ + sysdep_msg_arr[j].str[m]->segmentcount
+ * sizeof (struct segment_pair);
+ }
+
+ end_offset = offset;
+
+
+ /* Third pass: Write the non-string parts of the file. At the same time,
+ compute the offsets of each string, including the proper alignment. */
+
+ /* Write the header out. */
+ if (byteswap)
+ {
+ BSWAP32 (header.magic);
+ BSWAP32 (header.revision);
+ BSWAP32 (header.nstrings);
+ BSWAP32 (header.orig_tab_offset);
+ BSWAP32 (header.trans_tab_offset);
+ BSWAP32 (header.hash_tab_size);
+ BSWAP32 (header.hash_tab_offset);
+ if (minor_revision >= 1)
+ {
+ BSWAP32 (header.n_sysdep_segments);
+ BSWAP32 (header.sysdep_segments_offset);
+ BSWAP32 (header.n_sysdep_strings);
+ BSWAP32 (header.orig_sysdep_tab_offset);
+ BSWAP32 (header.trans_sysdep_tab_offset);
+ }
+ }
+ fwrite (&header, header_size, 1, output_file);
+
+ /* Table for original string offsets. */
+ /* Here output_file is at position header.orig_tab_offset. */
+
+ for (j = 0; j < nstrings; j++)
+ {
+ offset = roundup (offset, alignment);
+ orig_tab[j].length =
+ msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
+ orig_tab[j].offset = offset;
+ offset += orig_tab[j].length;
+ /* Subtract 1 because of the terminating NUL. */
+ orig_tab[j].length--;
+ }
+ if (byteswap)
+ for (j = 0; j < nstrings; j++)
+ {
+ BSWAP32 (orig_tab[j].length);
+ BSWAP32 (orig_tab[j].offset);
+ }
+ fwrite (orig_tab, nstrings * sizeof (struct string_desc), 1, output_file);
+
+ /* Table for translated string offsets. */
+ /* Here output_file is at position header.trans_tab_offset. */
+
+ for (j = 0; j < nstrings; j++)
+ {
+ offset = roundup (offset, alignment);
+ trans_tab[j].length = msg_arr[j].str[M_STR].length;
+ trans_tab[j].offset = offset;
+ offset += trans_tab[j].length;
+ /* Subtract 1 because of the terminating NUL. */
+ trans_tab[j].length--;
+ }
+ if (byteswap)
+ for (j = 0; j < nstrings; j++)
+ {
+ BSWAP32 (trans_tab[j].length);
+ BSWAP32 (trans_tab[j].offset);
+ }
+ fwrite (trans_tab, nstrings * sizeof (struct string_desc), 1, output_file);
+
+ /* Skip this part when no hash table is needed. */
+ if (!omit_hash_table)
+ {
+ nls_uint32 *hash_tab;
+ unsigned int j;
+
+ /* Here output_file is at position header.hash_tab_offset. */
+
+ /* Allocate room for the hashing table to be written out. */
+ hash_tab = XNMALLOC (hash_tab_size, nls_uint32);
+ memset (hash_tab, '\0', hash_tab_size * sizeof (nls_uint32));
+
+ /* Insert all value in the hash table, following the algorithm described
+ above. */
+ for (j = 0; j < nstrings; j++)
+ {
+ nls_uint32 hash_val = hash_string (msg_arr[j].str[M_ID].pointer);
+ nls_uint32 idx = hash_val % hash_tab_size;
+
+ if (hash_tab[idx] != 0)
+ {
+ /* We need the second hashing function. */
+ nls_uint32 incr = 1 + (hash_val % (hash_tab_size - 2));
+
+ do
+ if (idx >= hash_tab_size - incr)
+ idx -= hash_tab_size - incr;
+ else
+ idx += incr;
+ while (hash_tab[idx] != 0);
+ }
+
+ hash_tab[idx] = j + 1;
+ }
+
+ /* Write the hash table out. */
+ if (byteswap)
+ for (j = 0; j < hash_tab_size; j++)
+ BSWAP32 (hash_tab[j]);
+ fwrite (hash_tab, hash_tab_size * sizeof (nls_uint32), 1, output_file);
+
+ free (hash_tab);
+ }
+
+ if (minor_revision >= 1)
+ {
+ struct sysdep_segment *sysdep_segments_tab;
+ nls_uint32 *sysdep_tab;
+ size_t stoffset;
+ unsigned int i;
+
+ /* Here output_file is at position header.sysdep_segments_offset. */
+
+ sysdep_segments_tab =
+ XNMALLOC (n_sysdep_segments, struct sysdep_segment);
+ for (i = 0; i < n_sysdep_segments; i++)
+ {
+ offset = roundup (offset, alignment);
+ /* The "+ 1" accounts for the trailing NUL byte. */
+ sysdep_segments_tab[i].length = sysdep_segments[i].length + 1;
+ sysdep_segments_tab[i].offset = offset;
+ offset += sysdep_segments_tab[i].length;
+ }
+
+ if (byteswap)
+ for (i = 0; i < n_sysdep_segments; i++)
+ {
+ BSWAP32 (sysdep_segments_tab[i].length);
+ BSWAP32 (sysdep_segments_tab[i].offset);
+ }
+ fwrite (sysdep_segments_tab,
+ n_sysdep_segments * sizeof (struct sysdep_segment), 1,
+ output_file);
+
+ free (sysdep_segments_tab);
+
+ sysdep_tab = XNMALLOC (n_sysdep_strings, nls_uint32);
+ stoffset = sysdep_tab_offset;
+
+ for (m = 0; m < 2; m++)
+ {
+ /* Here output_file is at position
+ m == M_ID -> header.orig_sysdep_tab_offset,
+ m == M_STR -> header.trans_sysdep_tab_offset. */
+
+ for (j = 0; j < n_sysdep_strings; j++)
+ {
+ sysdep_tab[j] = stoffset;
+ stoffset += sizeof (struct sysdep_string)
+ + sysdep_msg_arr[j].str[m]->segmentcount
+ * sizeof (struct segment_pair);
+ }
+ /* Write the table for original/translated sysdep string offsets. */
+ if (byteswap)
+ for (j = 0; j < n_sysdep_strings; j++)
+ BSWAP32 (sysdep_tab[j]);
+ fwrite (sysdep_tab, n_sysdep_strings * sizeof (nls_uint32), 1,
+ output_file);
+ }
+
+ free (sysdep_tab);
+
+ /* Here output_file is at position sysdep_tab_offset. */
+
+ for (m = 0; m < 2; m++)
+ for (j = 0; j < n_sysdep_strings; j++)
+ {
+ struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
+ struct pre_sysdep_string *pre = msg->str[m];
+ struct sysdep_string *str =
+ (struct sysdep_string *)
+ xmalloca (sizeof (struct sysdep_string)
+ + pre->segmentcount * sizeof (struct segment_pair));
+ unsigned int i;
+
+ offset = roundup (offset, alignment);
+ str->offset = offset;
+ for (i = 0; i <= pre->segmentcount; i++)
+ {
+ str->segments[i].segsize = pre->segments[i].segsize;
+ str->segments[i].sysdepref = pre->segments[i].sysdepref;
+ offset += str->segments[i].segsize;
+ }
+ if (m == M_ID && msg->id_plural_len > 0)
+ {
+ str->segments[pre->segmentcount].segsize += msg->id_plural_len;
+ offset += msg->id_plural_len;
+ }
+ if (byteswap)
+ {
+ BSWAP32 (str->offset);
+ for (i = 0; i <= pre->segmentcount; i++)
+ {
+ BSWAP32 (str->segments[i].segsize);
+ BSWAP32 (str->segments[i].sysdepref);
+ }
+ }
+ fwrite (str,
+ sizeof (struct sysdep_string)
+ + pre->segmentcount * sizeof (struct segment_pair),
+ 1, output_file);
+
+ freea (str);
+ }
+ }
+
+ /* Here output_file is at position end_offset. */
+
+ free (trans_tab);
+ free (orig_tab);
+
+
+ /* Fourth pass: Write the strings. */
+
+ offset = end_offset;
+
+ /* A few zero bytes for padding. */
+ null = (char *) alloca (alignment);
+ memset (null, '\0', alignment);
+
+ /* Now write the original strings. */
+ for (j = 0; j < nstrings; j++)
+ {
+ fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+ offset = roundup (offset, alignment);
+
+ fwrite (msg_arr[j].str[M_ID].pointer, msg_arr[j].str[M_ID].length, 1,
+ output_file);
+ if (msg_arr[j].id_plural_len > 0)
+ fwrite (msg_arr[j].id_plural, msg_arr[j].id_plural_len, 1,
+ output_file);
+ offset += msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
+ }
+
+ /* Now write the translated strings. */
+ for (j = 0; j < nstrings; j++)
+ {
+ fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+ offset = roundup (offset, alignment);
+
+ fwrite (msg_arr[j].str[M_STR].pointer, msg_arr[j].str[M_STR].length, 1,
+ output_file);
+ offset += msg_arr[j].str[M_STR].length;
+ }
+
+ if (minor_revision >= 1)
+ {
+ unsigned int i;
+
+ for (i = 0; i < n_sysdep_segments; i++)
+ {
+ fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+ offset = roundup (offset, alignment);
+
+ fwrite (sysdep_segments[i].pointer, sysdep_segments[i].length, 1,
+ output_file);
+ fwrite (null, 1, 1, output_file);
+ offset += sysdep_segments[i].length + 1;
+ }
+
+ for (m = 0; m < 2; m++)
+ for (j = 0; j < n_sysdep_strings; j++)
+ {
+ struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
+ struct pre_sysdep_string *pre = msg->str[m];
+
+ fwrite (null, roundup (offset, alignment) - offset, 1,
+ output_file);
+ offset = roundup (offset, alignment);
+
+ for (i = 0; i <= pre->segmentcount; i++)
+ {
+ fwrite (pre->segments[i].segptr, pre->segments[i].segsize, 1,
+ output_file);
+ offset += pre->segments[i].segsize;
+ }
+ if (m == M_ID && msg->id_plural_len > 0)
+ {
+ fwrite (msg->id_plural, msg->id_plural_len, 1, output_file);
+ offset += msg->id_plural_len;
+ }
+
+ free (pre);
+ }
+ }
+
+ freea (null);
+ for (j = 0; j < mlp->nitems; j++)
+ free (msgctid_arr[j]);
+ free (sysdep_msg_arr);
+ free (msg_arr);
+ free (msgctid_arr);
+}
+
+
+int
+msgdomain_write_mo (message_list_ty *mlp,
+ const char *domain_name,
+ const char *file_name)
+{
+ FILE *output_file;
+
+ /* If no entry for this domain don't even create the file. */
+ if (mlp->nitems != 0)
+ {
+ if (strcmp (domain_name, "-") == 0)
+ {
+ output_file = stdout;
+ SET_BINARY (fileno (output_file));
+ }
+ else
+ {
+ output_file = fopen (file_name, "wb");
+ if (output_file == NULL)
+ {
+ error (0, errno, _("error while opening \"%s\" for writing"),
+ file_name);
+ return 1;
+ }
+ }
+
+ if (output_file != NULL)
+ {
+ write_table (output_file, mlp);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (output_file))
+ error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ file_name);
+ }
+ }
+
+ return 0;
+}
diff --git a/gettext-tools/src/write-mo.h b/gettext-tools/src/write-mo.h
new file mode 100644
index 0000000..b1ea8ea
--- /dev/null
+++ b/gettext-tools/src/write-mo.h
@@ -0,0 +1,43 @@
+/* Writing binary .mo files.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_MO_H
+#define _WRITE_MO_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "message.h"
+
+/* Alignment of strings in resulting .mo file. */
+extern size_t alignment;
+
+/* True if writing a .mo file in opposite endianness than the host. */
+extern bool byteswap;
+
+/* True if no hash table in .mo is wanted. */
+extern bool no_hash_table;
+
+/* Write a GNU mo file. mlp is a list containing the messages to be output.
+ domain_name is the domain name, file_name is the desired file name.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_mo (message_list_ty *mlp,
+ const char *domain_name,
+ const char *file_name);
+
+#endif /* _WRITE_MO_H */
diff --git a/gettext-tools/src/write-po.c b/gettext-tools/src/write-po.c
new file mode 100644
index 0000000..d49a95f
--- /dev/null
+++ b/gettext-tools/src/write-po.c
@@ -0,0 +1,1620 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2010, 2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "write-po.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_ICONV
+# include <iconv.h>
+#endif
+
+#include "c-ctype.h"
+#include "po-charset.h"
+#include "format.h"
+#include "unilbrk.h"
+#include "msgl-ascii.h"
+#include "write-catalog.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "c-strstr.h"
+#include "ostream.h"
+#ifdef GETTEXTDATADIR
+# include "styled-ostream.h"
+#endif
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "gettext.h"
+
+/* Our regular abbreviation. */
+#define _(str) gettext (str)
+
+#if HAVE_DECL_PUTC_UNLOCKED
+# undef putc
+# define putc putc_unlocked
+#endif
+
+
+/* =================== Putting together a #, flags line. =================== */
+
+
+/* Convert IS_FORMAT in the context of programming language LANG to a flag
+ string for use in #, flags. */
+
+const char *
+make_format_description_string (enum is_format is_format, const char *lang,
+ bool debug)
+{
+ static char result[100];
+
+ switch (is_format)
+ {
+ case possible:
+ if (debug)
+ {
+ sprintf (result, "possible-%s-format", lang);
+ break;
+ }
+ /* FALLTHROUGH */
+ case yes_according_to_context:
+ case yes:
+ sprintf (result, "%s-format", lang);
+ break;
+ case no:
+ sprintf (result, "no-%s-format", lang);
+ break;
+ default:
+ /* The others have already been filtered out by significant_format_p. */
+ abort ();
+ }
+
+ return result;
+}
+
+
+/* Return true if IS_FORMAT is worth mentioning in a #, flags list. */
+
+bool
+significant_format_p (enum is_format is_format)
+{
+ return is_format != undecided && is_format != impossible;
+}
+
+
+/* Return true if one of IS_FORMAT is worth mentioning in a #, flags list. */
+
+static bool
+has_significant_format_p (const enum is_format is_format[NFORMATS])
+{
+ size_t i;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (significant_format_p (is_format[i]))
+ return true;
+ return false;
+}
+
+
+/* Convert a RANGE to a freshly allocated string for use in #, flags. */
+
+char *
+make_range_description_string (struct argument_range range)
+{
+ return xasprintf ("range: %d..%d", range.min, range.max);
+}
+
+
+/* Convert a wrapping flag DO_WRAP to a string for use in #, flags. */
+
+static const char *
+make_c_width_description_string (enum is_wrap do_wrap)
+{
+ const char *result = NULL;
+
+ switch (do_wrap)
+ {
+ case yes:
+ result = "wrap";
+ break;
+ case no:
+ result = "no-wrap";
+ break;
+ default:
+ abort ();
+ }
+
+ return result;
+}
+
+
+/* ========================== Styling primitives. ========================== */
+
+
+/* When compiled in src, enable styling support.
+ When compiled in libgettextpo, don't enable styling support. */
+#ifdef GETTEXTDATADIR
+
+/* Return true if the stream is an instance of styled_ostream_t. */
+static inline bool
+is_stylable (ostream_t stream)
+{
+ return IS_INSTANCE (stream, ostream, styled_ostream);
+}
+
+/* Start a run of text belonging to a given CSS class. */
+static void
+begin_css_class (ostream_t stream, const char *classname)
+{
+ if (is_stylable (stream))
+ styled_ostream_begin_use_class ((styled_ostream_t) stream, classname);
+}
+
+/* End a run of text belonging to a given CSS class. */
+static void
+end_css_class (ostream_t stream, const char *classname)
+{
+ if (is_stylable (stream))
+ styled_ostream_end_use_class ((styled_ostream_t) stream, classname);
+}
+
+#else
+
+#define is_stylable(stream) false
+#define begin_css_class(stream,classname) /* empty */
+#define end_css_class(stream,classname) /* empty */
+
+#endif
+
+/* CSS classes at message level. */
+static const char class_header[] = "header";
+static const char class_translated[] = "translated";
+static const char class_untranslated[] = "untranslated";
+static const char class_fuzzy[] = "fuzzy";
+static const char class_obsolete[] = "obsolete";
+
+/* CSS classes describing the parts of a message. */
+static const char class_comment[] = "comment";
+static const char class_translator_comment[] = "translator-comment";
+static const char class_extracted_comment[] = "extracted-comment";
+static const char class_reference_comment[] = "reference-comment";
+static const char class_reference[] = "reference";
+static const char class_flag_comment[] = "flag-comment";
+static const char class_flag[] = "flag";
+static const char class_fuzzy_flag[] = "fuzzy-flag";
+static const char class_previous_comment[] = "previous-comment";
+static const char class_previous[] = "previous";
+static const char class_msgid[] = "msgid";
+static const char class_msgstr[] = "msgstr";
+static const char class_keyword[] = "keyword";
+static const char class_string[] = "string";
+
+/* CSS classes for the contents of strings. */
+static const char class_text[] = "text";
+static const char class_escape_sequence[] = "escape-sequence";
+static const char class_format_directive[] = "format-directive";
+static const char class_invalid_format_directive[] = "invalid-format-directive";
+#if 0
+static const char class_added[] = "added";
+static const char class_changed[] = "changed";
+static const char class_removed[] = "removed";
+#endif
+
+/* Per-character attributes. */
+enum
+{
+ ATTR_ESCAPE_SEQUENCE = 1 << 0,
+ /* The following two are exclusive. */
+ ATTR_FORMAT_DIRECTIVE = 1 << 1,
+ ATTR_INVALID_FORMAT_DIRECTIVE = 1 << 2
+};
+
+
+/* ================ Output parts of a message, as comments. ================ */
+
+
+/* Output mp->comment as a set of comment lines. */
+
+void
+message_print_comment (const message_ty *mp, ostream_t stream)
+{
+ if (mp->comment != NULL)
+ {
+ size_t j;
+
+ begin_css_class (stream, class_translator_comment);
+
+ for (j = 0; j < mp->comment->nitems; ++j)
+ {
+ const char *s = mp->comment->item[j];
+ do
+ {
+ const char *e;
+ ostream_write_str (stream, "#");
+ if (*s != '\0')
+ ostream_write_str (stream, " ");
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ ostream_write_str (stream, s);
+ s = NULL;
+ }
+ else
+ {
+ ostream_write_mem (stream, s, e - s);
+ s = e + 1;
+ }
+ ostream_write_str (stream, "\n");
+ }
+ while (s != NULL);
+ }
+
+ end_css_class (stream, class_translator_comment);
+ }
+}
+
+
+/* Output mp->comment_dot as a set of comment lines. */
+
+void
+message_print_comment_dot (const message_ty *mp, ostream_t stream)
+{
+ if (mp->comment_dot != NULL)
+ {
+ size_t j;
+
+ begin_css_class (stream, class_extracted_comment);
+
+ for (j = 0; j < mp->comment_dot->nitems; ++j)
+ {
+ const char *s = mp->comment_dot->item[j];
+ ostream_write_str (stream, "#.");
+ if (*s != '\0')
+ ostream_write_str (stream, " ");
+ ostream_write_str (stream, s);
+ ostream_write_str (stream, "\n");
+ }
+
+ end_css_class (stream, class_extracted_comment);
+ }
+}
+
+
+/* Output mp->filepos as a set of comment lines. */
+
+static enum filepos_comment_type filepos_comment_type = filepos_comment_full;
+
+void
+message_print_comment_filepos (const message_ty *mp, ostream_t stream,
+ bool uniforum, size_t page_width)
+{
+ if (filepos_comment_type != filepos_comment_none
+ && mp->filepos_count != 0)
+ {
+ size_t filepos_count;
+ lex_pos_ty *filepos;
+
+ begin_css_class (stream, class_reference_comment);
+
+ if (filepos_comment_type == filepos_comment_file)
+ {
+ size_t i;
+
+ filepos_count = 0;
+ filepos = XNMALLOC (mp->filepos_count, lex_pos_ty);
+
+ for (i = 0; i < mp->filepos_count; ++i)
+ {
+ lex_pos_ty *pp = &mp->filepos[i];
+ size_t j;
+
+ for (j = 0; j < filepos_count; j++)
+ if (strcmp (filepos[j].file_name, pp->file_name) == 0)
+ break;
+
+ if (j == filepos_count)
+ {
+ filepos[filepos_count].file_name = pp->file_name;
+ filepos[filepos_count].line_number = (size_t)-1;
+ filepos_count++;
+ }
+ }
+ }
+ else
+ {
+ filepos = mp->filepos;
+ filepos_count = mp->filepos_count;
+ }
+
+ if (uniforum)
+ {
+ size_t j;
+
+ for (j = 0; j < filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &filepos[j];
+ const char *cp = pp->file_name;
+ char *str;
+
+ while (cp[0] == '.' && cp[1] == '/')
+ cp += 2;
+ ostream_write_str (stream, "# ");
+ begin_css_class (stream, class_reference);
+ /* There are two Sun formats to choose from: SunOS and
+ Solaris. Use the Solaris form here. */
+ str = xasprintf ("File: %s, line: %ld",
+ cp, (long) pp->line_number);
+ ostream_write_str (stream, str);
+ end_css_class (stream, class_reference);
+ ostream_write_str (stream, "\n");
+ free (str);
+ }
+ }
+ else
+ {
+ size_t column;
+ size_t j;
+
+ ostream_write_str (stream, "#:");
+ column = 2;
+ for (j = 0; j < filepos_count; ++j)
+ {
+ lex_pos_ty *pp;
+ char buffer[21];
+ const char *cp;
+ size_t len;
+
+ pp = &filepos[j];
+ cp = pp->file_name;
+ while (cp[0] == '.' && cp[1] == '/')
+ cp += 2;
+ if (filepos_comment_type == filepos_comment_file
+ /* Some xgettext input formats, like RST, lack line
+ numbers. */
+ || pp->line_number == (size_t)(-1))
+ buffer[0] = '\0';
+ else
+ sprintf (buffer, ":%ld", (long) pp->line_number);
+ len = strlen (cp) + strlen (buffer) + 1;
+ if (column > 2 && column + len >= page_width)
+ {
+ ostream_write_str (stream, "\n#:");
+ column = 2;
+ }
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_reference);
+ ostream_write_str (stream, cp);
+ ostream_write_str (stream, buffer);
+ end_css_class (stream, class_reference);
+ column += len;
+ }
+ ostream_write_str (stream, "\n");
+ }
+
+ if (filepos != mp->filepos)
+ free (filepos);
+
+ end_css_class (stream, class_reference_comment);
+ }
+}
+
+
+/* Output mp->is_fuzzy, mp->is_format, mp->range, mp->do_wrap as a comment
+ line. */
+
+void
+message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
+{
+ if ((mp->is_fuzzy && mp->msgstr[0] != '\0')
+ || has_significant_format_p (mp->is_format)
+ || has_range_p (mp->range)
+ || mp->do_wrap == no)
+ {
+ bool first_flag = true;
+ size_t i;
+
+ begin_css_class (stream, class_flag_comment);
+
+ ostream_write_str (stream, "#,");
+
+ /* We don't print the fuzzy flag if the msgstr is empty. This
+ might be introduced by the user but we want to normalize the
+ output. */
+ if (mp->is_fuzzy && mp->msgstr[0] != '\0')
+ {
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_flag);
+ begin_css_class (stream, class_fuzzy_flag);
+ ostream_write_str (stream, "fuzzy");
+ end_css_class (stream, class_fuzzy_flag);
+ end_css_class (stream, class_flag);
+ first_flag = false;
+ }
+
+ for (i = 0; i < NFORMATS; i++)
+ if (significant_format_p (mp->is_format[i]))
+ {
+ if (!first_flag)
+ ostream_write_str (stream, ",");
+
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_flag);
+ ostream_write_str (stream,
+ make_format_description_string (mp->is_format[i],
+ format_language[i],
+ debug));
+ end_css_class (stream, class_flag);
+ first_flag = false;
+ }
+
+ if (has_range_p (mp->range))
+ {
+ char *string;
+
+ if (!first_flag)
+ ostream_write_str (stream, ",");
+
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_flag);
+ string = make_range_description_string (mp->range);
+ ostream_write_str (stream, string);
+ free (string);
+ end_css_class (stream, class_flag);
+ first_flag = false;
+ }
+
+ if (mp->do_wrap == no)
+ {
+ if (!first_flag)
+ ostream_write_str (stream, ",");
+
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_flag);
+ ostream_write_str (stream,
+ make_c_width_description_string (mp->do_wrap));
+ end_css_class (stream, class_flag);
+ first_flag = false;
+ }
+
+ ostream_write_str (stream, "\n");
+
+ end_css_class (stream, class_flag_comment);
+ }
+}
+
+
+/* ========= Some parameters for use by 'msgdomain_list_print_po'. ========= */
+
+
+/* This variable controls the extent to which the page width applies.
+ True means it applies to message strings and file reference lines.
+ False means it applies to file reference lines only. */
+static bool wrap_strings = true;
+
+void
+message_page_width_ignore ()
+{
+ wrap_strings = false;
+}
+
+
+/* These three variables control the output style of the message_print
+ function. Interface functions for them are to be used. */
+static bool indent = false;
+static bool uniforum = false;
+static bool escape = false;
+
+void
+message_print_style_indent ()
+{
+ indent = true;
+}
+
+void
+message_print_style_uniforum ()
+{
+ uniforum = true;
+}
+
+void
+message_print_style_escape (bool flag)
+{
+ escape = flag;
+}
+
+void
+message_print_style_filepos (enum filepos_comment_type type)
+{
+ filepos_comment_type = type;
+}
+
+
+/* --add-location argument handling. Return an error indicator. */
+bool
+handle_filepos_comment_option (const char *option)
+{
+ if (option != NULL)
+ {
+ if (strcmp (option, "never") == 0 || strcmp (option, "no") == 0)
+ message_print_style_filepos (filepos_comment_none);
+ else if (strcmp (option, "full") == 0 || strcmp (option, "yes") == 0)
+ message_print_style_filepos (filepos_comment_full);
+ else if (strcmp (option, "file") == 0)
+ message_print_style_filepos (filepos_comment_file);
+ else
+ {
+ fprintf (stderr, "invalid --add-location argument: %s\n", option);
+ return true;
+ }
+ }
+ else
+ /* --add-location is equivalent to --add-location=full. */
+ message_print_style_filepos (filepos_comment_full);
+ return false;
+}
+
+
+/* =============== msgdomain_list_print_po() and subroutines. =============== */
+
+
+/* A version of memcpy optimized for the case n <= 1. */
+static inline void
+memcpy_small (void *dst, const void *src, size_t n)
+{
+ if (n > 0)
+ {
+ char *q = (char *) dst;
+ const char *p = (const char *) src;
+
+ *q = *p;
+ if (--n > 0)
+ do *++q = *++p; while (--n > 0);
+ }
+}
+
+
+/* A version of memset optimized for the case n <= 1. */
+static inline void
+memset_small (void *dst, char c, size_t n)
+{
+ if (n > 0)
+ {
+ char *p = (char *) dst;
+
+ *p = c;
+ if (--n > 0)
+ do *++p = c; while (--n > 0);
+ }
+}
+
+
+static void
+wrap (const message_ty *mp, ostream_t stream,
+ const char *line_prefix, int extra_indent, const char *css_class,
+ const char *name, const char *value,
+ enum is_wrap do_wrap, size_t page_width,
+ const char *charset)
+{
+ const char *canon_charset;
+ char *fmtdir;
+ char *fmtdirattr;
+ const char *s;
+ bool first_line;
+#if HAVE_ICONV
+ const char *envval;
+ iconv_t conv;
+#endif
+ bool weird_cjk;
+
+ canon_charset = po_charset_canonicalize (charset);
+
+#if HAVE_ICONV
+ /* The old Solaris/openwin msgfmt and GNU msgfmt <= 0.10.35 don't know
+ about multibyte encodings, and require a spurious backslash after
+ every multibyte character whose last byte is 0x5C. Some programs,
+ like vim, distribute PO files in this broken format. It is important
+ for such programs that GNU msgmerge continues to support this old
+ PO file format when the Makefile requests it. */
+ envval = getenv ("OLD_PO_FILE_OUTPUT");
+ if (envval != NULL && *envval != '\0')
+ /* Write a PO file in old format, with extraneous backslashes. */
+ conv = (iconv_t)(-1);
+ else
+ if (canon_charset == NULL)
+ /* Invalid PO file encoding. */
+ conv = (iconv_t)(-1);
+ else
+ /* Avoid glibc-2.1 bug with EUC-KR. */
+# if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
+ && !defined _LIBICONV_VERSION
+ if (strcmp (canon_charset, "EUC-KR") == 0)
+ conv = (iconv_t)(-1);
+ else
+# endif
+ /* Avoid Solaris 2.9 bug with GB2312, EUC-TW, BIG5, BIG5-HKSCS, GBK,
+ GB18030. */
+# if defined __sun && !defined _LIBICONV_VERSION
+ if ( strcmp (canon_charset, "GB2312") == 0
+ || strcmp (canon_charset, "EUC-TW") == 0
+ || strcmp (canon_charset, "BIG5") == 0
+ || strcmp (canon_charset, "BIG5-HKSCS") == 0
+ || strcmp (canon_charset, "GBK") == 0
+ || strcmp (canon_charset, "GB18030") == 0)
+ conv = (iconv_t)(-1);
+ else
+# endif
+ /* Use iconv() to parse multibyte characters. */
+ conv = iconv_open ("UTF-8", canon_charset);
+
+ if (conv != (iconv_t)(-1))
+ weird_cjk = false;
+ else
+#endif
+ if (canon_charset == NULL)
+ weird_cjk = false;
+ else
+ weird_cjk = po_is_charset_weird_cjk (canon_charset);
+
+ if (canon_charset == NULL)
+ canon_charset = po_charset_ascii;
+
+ /* Determine the extent of format string directives. */
+ fmtdir = NULL;
+ fmtdirattr = NULL;
+ if (value[0] != '\0')
+ {
+ bool is_msgstr =
+ (strlen (name) >= 6 && memcmp (name, "msgstr", 6) == 0);
+ /* or equivalent: = (css_class == class_msgstr) */
+ size_t i;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (possible_format_p (mp->is_format[i]))
+ {
+ size_t len = strlen (value);
+ struct formatstring_parser *parser = formatstring_parsers[i];
+ char *invalid_reason = NULL;
+ void *descr;
+ const char *fdp;
+ const char *fd_end;
+ char *fdap;
+
+ fmtdir = XCALLOC (len, char);
+ descr = parser->parse (value, is_msgstr, fmtdir, &invalid_reason);
+ if (descr != NULL)
+ parser->free (descr);
+
+ /* Locate the FMTDIR_* bits and transform the array to an array
+ of attributes. */
+ fmtdirattr = XCALLOC (len, char);
+ fd_end = fmtdir + len;
+ for (fdp = fmtdir, fdap = fmtdirattr; fdp < fd_end; fdp++, fdap++)
+ if (*fdp & FMTDIR_START)
+ {
+ const char *fdq;
+ for (fdq = fdp; fdq < fd_end; fdq++)
+ if (*fdq & (FMTDIR_END | FMTDIR_ERROR))
+ break;
+ if (!(fdq < fd_end))
+ /* The ->parse method has determined the start of a
+ formatstring directive but not stored a bit indicating
+ its end. It is a bug in the ->parse method. */
+ abort ();
+ if (*fdq & FMTDIR_ERROR)
+ memset (fdap, ATTR_INVALID_FORMAT_DIRECTIVE, fdq - fdp + 1);
+ else
+ memset (fdap, ATTR_FORMAT_DIRECTIVE, fdq - fdp + 1);
+ fdap += fdq - fdp;
+ fdp = fdq;
+ }
+ else
+ *fdap = 0;
+
+ break;
+ }
+ }
+
+ /* Loop over the '\n' delimited portions of value. */
+ s = value;
+ first_line = true;
+ do
+ {
+ /* The usual escapes, as defined by the ANSI C Standard. */
+# define is_escape(c) \
+ ((c) == '\a' || (c) == '\b' || (c) == '\f' || (c) == '\n' \
+ || (c) == '\r' || (c) == '\t' || (c) == '\v')
+
+ const char *es;
+ const char *ep;
+ size_t portion_len;
+ char *portion;
+ char *overrides;
+ char *attributes;
+ char *linebreaks;
+ char *pp;
+ char *op;
+ char *ap;
+ int startcol, startcol_after_break, width;
+ size_t i;
+
+ for (es = s; *es != '\0'; )
+ if (*es++ == '\n')
+ break;
+
+ /* Expand escape sequences in each portion. */
+ for (ep = s, portion_len = 0; ep < es; ep++)
+ {
+ char c = *ep;
+ if (is_escape (c))
+ portion_len += 2;
+ else if (escape && !c_isprint ((unsigned char) c))
+ portion_len += 4;
+ else if (c == '\\' || c == '"')
+ portion_len += 2;
+ else
+ {
+#if HAVE_ICONV
+ if (conv != (iconv_t)(-1))
+ {
+ /* Skip over a complete multi-byte character. Don't
+ interpret the second byte of a multi-byte character as
+ ASCII. This is needed for the BIG5, BIG5-HKSCS, GBK,
+ GB18030, SHIFT_JIS, JOHAB encodings. */
+ char scratchbuf[64];
+ const char *inptr = ep;
+ size_t insize;
+ char *outptr = &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+ size_t res;
+
+ res = (size_t)(-1);
+ for (insize = 1; inptr + insize <= es; insize++)
+ {
+ res = iconv (conv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ if (!(res == (size_t)(-1) && errno == EINVAL))
+ break;
+ /* We expect that no input bytes have been consumed
+ so far. */
+ if (inptr != ep)
+ abort ();
+ }
+ if (res == (size_t)(-1))
+ {
+ if (errno == EILSEQ)
+ {
+ po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
+ _("invalid multibyte sequence"));
+ continue;
+ }
+ else if (errno == EINVAL)
+ {
+ /* This could happen if an incomplete
+ multibyte sequence at the end of input
+ bytes. */
+ po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
+ _("incomplete multibyte sequence"));
+ continue;
+ }
+ else
+ abort ();
+ }
+ insize = inptr - ep;
+ portion_len += insize;
+ ep += insize - 1;
+ }
+ else
+#endif
+ {
+ if (weird_cjk
+ /* Special handling of encodings with CJK structure. */
+ && ep + 2 <= es
+ && (unsigned char) ep[0] >= 0x80
+ && (unsigned char) ep[1] >= 0x30)
+ {
+ portion_len += 2;
+ ep += 1;
+ }
+ else
+ portion_len += 1;
+ }
+ }
+ }
+ portion = XNMALLOC (portion_len, char);
+ overrides = XNMALLOC (portion_len, char);
+ attributes = XNMALLOC (portion_len, char);
+ for (ep = s, pp = portion, op = overrides, ap = attributes; ep < es; ep++)
+ {
+ char c = *ep;
+ char attr = (fmtdirattr != NULL ? fmtdirattr[ep - value] : 0);
+ char brk = UC_BREAK_UNDEFINED;
+ /* Don't break inside format directives. */
+ if (attr == ATTR_FORMAT_DIRECTIVE
+ && (fmtdir[ep - value] & FMTDIR_START) == 0)
+ brk = UC_BREAK_PROHIBITED;
+ if (is_escape (c))
+ {
+ switch (c)
+ {
+ case '\a': c = 'a'; break;
+ case '\b': c = 'b'; break;
+ case '\f': c = 'f'; break;
+ case '\n': c = 'n'; break;
+ case '\r': c = 'r'; break;
+ case '\t': c = 't'; break;
+ case '\v': c = 'v'; break;
+ default: abort ();
+ }
+ *pp++ = '\\';
+ *pp++ = c;
+ *op++ = brk;
+ *op++ = UC_BREAK_PROHIBITED;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ /* We warn about any use of escape sequences beside
+ '\n' and '\t'. */
+ if (c != 'n' && c != 't')
+ {
+ char *error_message =
+ xasprintf (_("\
+internationalized messages should not contain the '\\%c' escape sequence"),
+ c);
+ po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, false,
+ error_message);
+ free (error_message);
+ }
+ }
+ else if (escape && !c_isprint ((unsigned char) c))
+ {
+ *pp++ = '\\';
+ *pp++ = '0' + (((unsigned char) c >> 6) & 7);
+ *pp++ = '0' + (((unsigned char) c >> 3) & 7);
+ *pp++ = '0' + ((unsigned char) c & 7);
+ *op++ = brk;
+ *op++ = UC_BREAK_PROHIBITED;
+ *op++ = UC_BREAK_PROHIBITED;
+ *op++ = UC_BREAK_PROHIBITED;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ }
+ else if (c == '\\' || c == '"')
+ {
+ *pp++ = '\\';
+ *pp++ = c;
+ *op++ = brk;
+ *op++ = UC_BREAK_PROHIBITED;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ *ap++ = attr | ATTR_ESCAPE_SEQUENCE;
+ }
+ else
+ {
+#if HAVE_ICONV
+ if (conv != (iconv_t)(-1))
+ {
+ /* Copy a complete multi-byte character. Don't
+ interpret the second byte of a multi-byte character as
+ ASCII. This is needed for the BIG5, BIG5-HKSCS, GBK,
+ GB18030, SHIFT_JIS, JOHAB encodings. */
+ char scratchbuf[64];
+ const char *inptr = ep;
+ size_t insize;
+ char *outptr = &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+ size_t res;
+
+ res = (size_t)(-1);
+ for (insize = 1; inptr + insize <= es; insize++)
+ {
+ res = iconv (conv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ if (!(res == (size_t)(-1) && errno == EINVAL))
+ break;
+ /* We expect that no input bytes have been consumed
+ so far. */
+ if (inptr != ep)
+ abort ();
+ }
+ if (res == (size_t)(-1))
+ {
+ if (errno == EILSEQ)
+ {
+ po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0,
+ false, _("invalid multibyte sequence"));
+ continue;
+ }
+ else
+ abort ();
+ }
+ insize = inptr - ep;
+ memcpy_small (pp, ep, insize);
+ pp += insize;
+ *op = brk;
+ memset_small (op + 1, UC_BREAK_PROHIBITED, insize - 1);
+ op += insize;
+ memset_small (ap, attr, insize);
+ ap += insize;
+ ep += insize - 1;
+ }
+ else
+#endif
+ {
+ if (weird_cjk
+ /* Special handling of encodings with CJK structure. */
+ && ep + 2 <= es
+ && (unsigned char) c >= 0x80
+ && (unsigned char) ep[1] >= 0x30)
+ {
+ *pp++ = c;
+ ep += 1;
+ *pp++ = *ep;
+ *op++ = brk;
+ *op++ = UC_BREAK_PROHIBITED;
+ *ap++ = attr;
+ *ap++ = attr;
+ }
+ else
+ {
+ *pp++ = c;
+ *op++ = brk;
+ *ap++ = attr;
+ }
+ }
+ }
+ }
+
+ /* Don't break immediately before the "\n" at the end. */
+ if (es > s && es[-1] == '\n')
+ overrides[portion_len - 2] = UC_BREAK_PROHIBITED;
+
+ linebreaks = XNMALLOC (portion_len, char);
+
+ /* Subsequent lines after a break are all indented.
+ See INDENT-S. */
+ startcol_after_break = (line_prefix ? strlen (line_prefix) : 0);
+ if (indent)
+ startcol_after_break = (startcol_after_break + extra_indent + 8) & ~7;
+ startcol_after_break++;
+
+ /* The line width. Allow room for the closing quote character. */
+ width = (wrap_strings && do_wrap != no ? page_width : INT_MAX) - 1;
+ /* Adjust for indentation of subsequent lines. */
+ width -= startcol_after_break;
+
+ recompute:
+ /* The line starts with different things depending on whether it
+ is the first line, and if we are using the indented style.
+ See INDENT-F. */
+ startcol = (line_prefix ? strlen (line_prefix) : 0);
+ if (first_line)
+ {
+ startcol += strlen (name);
+ if (indent)
+ startcol = (startcol + extra_indent + 8) & ~7;
+ else
+ startcol++;
+ }
+ else
+ {
+ if (indent)
+ startcol = (startcol + extra_indent + 8) & ~7;
+ }
+ /* Allow room for the opening quote character. */
+ startcol++;
+ /* Adjust for indentation of subsequent lines. */
+ startcol -= startcol_after_break;
+
+ /* Do line breaking on the portion. */
+ ulc_width_linebreaks (portion, portion_len, width, startcol, 0,
+ overrides, canon_charset, linebreaks);
+
+ /* If this is the first line, and we are not using the indented
+ style, and the line would wrap, then use an empty first line
+ and restart. */
+ if (first_line && !indent
+ && portion_len > 0
+ && (*es != '\0'
+ || startcol > width
+ || memchr (linebreaks, UC_BREAK_POSSIBLE, portion_len) != NULL))
+ {
+ if (line_prefix != NULL)
+ ostream_write_str (stream, line_prefix);
+ begin_css_class (stream, css_class);
+ begin_css_class (stream, class_keyword);
+ ostream_write_str (stream, name);
+ end_css_class (stream, class_keyword);
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_string);
+ ostream_write_str (stream, "\"\"");
+ end_css_class (stream, class_string);
+ end_css_class (stream, css_class);
+ ostream_write_str (stream, "\n");
+ first_line = false;
+ /* Recompute startcol and linebreaks. */
+ goto recompute;
+ }
+
+ /* Print the beginning of the line. This will depend on whether
+ this is the first line, and if the indented style is being
+ used. INDENT-F. */
+ {
+ int currcol = 0;
+
+ if (line_prefix != NULL)
+ {
+ ostream_write_str (stream, line_prefix);
+ currcol = strlen (line_prefix);
+ }
+ begin_css_class (stream, css_class);
+ if (first_line)
+ {
+ begin_css_class (stream, class_keyword);
+ ostream_write_str (stream, name);
+ currcol += strlen (name);
+ end_css_class (stream, class_keyword);
+ if (indent)
+ {
+ if (extra_indent > 0)
+ ostream_write_mem (stream, " ", extra_indent);
+ currcol += extra_indent;
+ ostream_write_mem (stream, " ", 8 - (currcol & 7));
+ currcol = (currcol + 8) & ~7;
+ }
+ else
+ {
+ ostream_write_str (stream, " ");
+ currcol++;
+ }
+ first_line = false;
+ }
+ else
+ {
+ if (indent)
+ {
+ if (extra_indent > 0)
+ ostream_write_mem (stream, " ", extra_indent);
+ currcol += extra_indent;
+ ostream_write_mem (stream, " ", 8 - (currcol & 7));
+ currcol = (currcol + 8) & ~7;
+ }
+ }
+ }
+
+ /* Print the portion itself, with linebreaks where necessary. */
+ {
+ char currattr = 0;
+
+ begin_css_class (stream, class_string);
+ ostream_write_str (stream, "\"");
+ begin_css_class (stream, class_text);
+
+ for (i = 0; i < portion_len; i++)
+ {
+ if (linebreaks[i] == UC_BREAK_POSSIBLE)
+ {
+ int currcol;
+
+ /* Change currattr so that it becomes 0. */
+ if (currattr & ATTR_ESCAPE_SEQUENCE)
+ {
+ end_css_class (stream, class_escape_sequence);
+ currattr &= ~ATTR_ESCAPE_SEQUENCE;
+ }
+ if (currattr & ATTR_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_format_directive);
+ currattr &= ~ATTR_FORMAT_DIRECTIVE;
+ }
+ else if (currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_invalid_format_directive);
+ currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+ }
+ if (!(currattr == 0))
+ abort ();
+
+ end_css_class (stream, class_text);
+ ostream_write_str (stream, "\"");
+ end_css_class (stream, class_string);
+ end_css_class (stream, css_class);
+ ostream_write_str (stream, "\n");
+ currcol = 0;
+ /* INDENT-S. */
+ if (line_prefix != NULL)
+ {
+ ostream_write_str (stream, line_prefix);
+ currcol = strlen (line_prefix);
+ }
+ begin_css_class (stream, css_class);
+ if (indent)
+ {
+ ostream_write_mem (stream, " ", 8 - (currcol & 7));
+ currcol = (currcol + 8) & ~7;
+ }
+ begin_css_class (stream, class_string);
+ ostream_write_str (stream, "\"");
+ begin_css_class (stream, class_text);
+ }
+ /* Change currattr so that it matches attributes[i]. */
+ if (attributes[i] != currattr)
+ {
+ /* class_escape_sequence occurs inside class_format_directive
+ and class_invalid_format_directive, so clear it first. */
+ if (currattr & ATTR_ESCAPE_SEQUENCE)
+ {
+ end_css_class (stream, class_escape_sequence);
+ currattr &= ~ATTR_ESCAPE_SEQUENCE;
+ }
+ if (~attributes[i] & currattr & ATTR_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_format_directive);
+ currattr &= ~ATTR_FORMAT_DIRECTIVE;
+ }
+ else if (~attributes[i] & currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_invalid_format_directive);
+ currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+ }
+ if (attributes[i] & ~currattr & ATTR_FORMAT_DIRECTIVE)
+ {
+ begin_css_class (stream, class_format_directive);
+ currattr |= ATTR_FORMAT_DIRECTIVE;
+ }
+ else if (attributes[i] & ~currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+ {
+ begin_css_class (stream, class_invalid_format_directive);
+ currattr |= ATTR_INVALID_FORMAT_DIRECTIVE;
+ }
+ /* class_escape_sequence occurs inside class_format_directive
+ and class_invalid_format_directive, so set it last. */
+ if (attributes[i] & ~currattr & ATTR_ESCAPE_SEQUENCE)
+ {
+ begin_css_class (stream, class_escape_sequence);
+ currattr |= ATTR_ESCAPE_SEQUENCE;
+ }
+ }
+ ostream_write_mem (stream, &portion[i], 1);
+ }
+
+ /* Change currattr so that it becomes 0. */
+ if (currattr & ATTR_ESCAPE_SEQUENCE)
+ {
+ end_css_class (stream, class_escape_sequence);
+ currattr &= ~ATTR_ESCAPE_SEQUENCE;
+ }
+ if (currattr & ATTR_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_format_directive);
+ currattr &= ~ATTR_FORMAT_DIRECTIVE;
+ }
+ else if (currattr & ATTR_INVALID_FORMAT_DIRECTIVE)
+ {
+ end_css_class (stream, class_invalid_format_directive);
+ currattr &= ~ATTR_INVALID_FORMAT_DIRECTIVE;
+ }
+ if (!(currattr == 0))
+ abort ();
+
+ end_css_class (stream, class_text);
+ ostream_write_str (stream, "\"");
+ end_css_class (stream, class_string);
+ end_css_class (stream, css_class);
+ ostream_write_str (stream, "\n");
+ }
+
+ free (linebreaks);
+ free (attributes);
+ free (overrides);
+ free (portion);
+
+ s = es;
+# undef is_escape
+ }
+ while (*s);
+
+ if (fmtdirattr != NULL)
+ free (fmtdirattr);
+ if (fmtdir != NULL)
+ free (fmtdir);
+
+#if HAVE_ICONV
+ if (conv != (iconv_t)(-1))
+ iconv_close (conv);
+#endif
+}
+
+
+static void
+print_blank_line (ostream_t stream)
+{
+ if (uniforum)
+ {
+ begin_css_class (stream, class_comment);
+ ostream_write_str (stream, "#\n");
+ end_css_class (stream, class_comment);
+ }
+ else
+ ostream_write_str (stream, "\n");
+}
+
+
+static void
+message_print (const message_ty *mp, ostream_t stream,
+ const char *charset, size_t page_width, bool blank_line,
+ bool debug)
+{
+ int extra_indent;
+
+ /* Separate messages with a blank line. Uniforum doesn't like blank
+ lines, so use an empty comment (unless there already is one). */
+ if (blank_line && (!uniforum
+ || mp->comment == NULL
+ || mp->comment->nitems == 0
+ || mp->comment->item[0][0] != '\0'))
+ print_blank_line (stream);
+
+ if (is_header (mp))
+ begin_css_class (stream, class_header);
+ else if (mp->msgstr[0] == '\0')
+ begin_css_class (stream, class_untranslated);
+ else if (mp->is_fuzzy)
+ begin_css_class (stream, class_fuzzy);
+ else
+ begin_css_class (stream, class_translated);
+
+ begin_css_class (stream, class_comment);
+
+ /* Print translator comment if available. */
+ message_print_comment (mp, stream);
+
+ /* Print xgettext extracted comments. */
+ message_print_comment_dot (mp, stream);
+
+ /* Print the file position comments. This will help a human who is
+ trying to navigate the sources. There is no problem of getting
+ repeated positions, because duplicates are checked for. */
+ message_print_comment_filepos (mp, stream, uniforum, page_width);
+
+ /* Print flag information in special comment. */
+ message_print_comment_flags (mp, stream, debug);
+
+ /* Print the previous msgid. This helps the translator when the msgid has
+ only slightly changed. */
+ begin_css_class (stream, class_previous_comment);
+ if (mp->prev_msgctxt != NULL)
+ wrap (mp, stream, "#| ", 0, class_previous, "msgctxt", mp->prev_msgctxt,
+ mp->do_wrap, page_width, charset);
+ if (mp->prev_msgid != NULL)
+ wrap (mp, stream, "#| ", 0, class_previous, "msgid", mp->prev_msgid,
+ mp->do_wrap, page_width, charset);
+ if (mp->prev_msgid_plural != NULL)
+ wrap (mp, stream, "#| ", 0, class_previous, "msgid_plural",
+ mp->prev_msgid_plural, mp->do_wrap, page_width, charset);
+ end_css_class (stream, class_previous_comment);
+ extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
+ || mp->prev_msgid_plural != NULL
+ ? 3
+ : 0);
+
+ end_css_class (stream, class_comment);
+
+ /* Print each of the message components. Wrap them nicely so they
+ are as readable as possible. If there is no recorded msgstr for
+ this domain, emit an empty string. */
+ if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt)
+ && po_charset_canonicalize (charset) != po_charset_utf8)
+ {
+ char *warning_message =
+ xasprintf (_("\
+The following msgctxt contains non-ASCII characters.\n\
+This will cause problems to translators who use a character encoding\n\
+different from yours. Consider using a pure ASCII msgctxt instead.\n\
+%s\n"), mp->msgctxt);
+ po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
+ free (warning_message);
+ }
+ if (!is_ascii_string (mp->msgid)
+ && po_charset_canonicalize (charset) != po_charset_utf8)
+ {
+ char *warning_message =
+ xasprintf (_("\
+The following msgid contains non-ASCII characters.\n\
+This will cause problems to translators who use a character encoding\n\
+different from yours. Consider using a pure ASCII msgid instead.\n\
+%s\n"), mp->msgid);
+ po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
+ free (warning_message);
+ }
+ if (mp->msgctxt != NULL)
+ wrap (mp, stream, NULL, extra_indent, class_msgid, "msgctxt", mp->msgctxt,
+ mp->do_wrap, page_width, charset);
+ wrap (mp, stream, NULL, extra_indent, class_msgid, "msgid", mp->msgid,
+ mp->do_wrap, page_width, charset);
+ if (mp->msgid_plural != NULL)
+ wrap (mp, stream, NULL, extra_indent, class_msgid, "msgid_plural",
+ mp->msgid_plural, mp->do_wrap, page_width, charset);
+
+ if (mp->msgid_plural == NULL)
+ wrap (mp, stream, NULL, extra_indent, class_msgstr, "msgstr", mp->msgstr,
+ mp->do_wrap, page_width, charset);
+ else
+ {
+ char prefix_buf[20];
+ unsigned int i;
+ const char *p;
+
+ for (p = mp->msgstr, i = 0;
+ p < mp->msgstr + mp->msgstr_len;
+ p += strlen (p) + 1, i++)
+ {
+ sprintf (prefix_buf, "msgstr[%u]", i);
+ wrap (mp, stream, NULL, extra_indent, class_msgstr, prefix_buf, p,
+ mp->do_wrap, page_width, charset);
+ }
+ }
+
+ if (is_header (mp))
+ end_css_class (stream, class_header);
+ else if (mp->msgstr[0] == '\0')
+ end_css_class (stream, class_untranslated);
+ else if (mp->is_fuzzy)
+ end_css_class (stream, class_fuzzy);
+ else
+ end_css_class (stream, class_translated);
+}
+
+
+static void
+message_print_obsolete (const message_ty *mp, ostream_t stream,
+ const char *charset, size_t page_width, bool blank_line)
+{
+ int extra_indent;
+
+ /* If msgstr is the empty string we print nothing. */
+ if (mp->msgstr[0] == '\0')
+ return;
+
+ /* Separate messages with a blank line. Uniforum doesn't like blank
+ lines, so use an empty comment (unless there already is one). */
+ if (blank_line)
+ print_blank_line (stream);
+
+ begin_css_class (stream, class_obsolete);
+
+ begin_css_class (stream, class_comment);
+
+ /* Print translator comment if available. */
+ message_print_comment (mp, stream);
+
+ /* Print xgettext extracted comments (normally empty). */
+ message_print_comment_dot (mp, stream);
+
+ /* Print the file position comments (normally empty). */
+ message_print_comment_filepos (mp, stream, uniforum, page_width);
+
+ /* Print flag information in special comment. */
+ if (mp->is_fuzzy)
+ {
+ ostream_write_str (stream, "#,");
+
+ if (mp->is_fuzzy)
+ ostream_write_str (stream, " fuzzy");
+
+ ostream_write_str (stream, "\n");
+ }
+
+ /* Print the previous msgid. This helps the translator when the msgid has
+ only slightly changed. */
+ begin_css_class (stream, class_previous_comment);
+ if (mp->prev_msgctxt != NULL)
+ wrap (mp, stream, "#~| ", 0, class_previous, "msgctxt", mp->prev_msgctxt,
+ mp->do_wrap, page_width, charset);
+ if (mp->prev_msgid != NULL)
+ wrap (mp, stream, "#~| ", 0, class_previous, "msgid", mp->prev_msgid,
+ mp->do_wrap, page_width, charset);
+ if (mp->prev_msgid_plural != NULL)
+ wrap (mp, stream, "#~| ", 0, class_previous, "msgid_plural",
+ mp->prev_msgid_plural, mp->do_wrap, page_width, charset);
+ end_css_class (stream, class_previous_comment);
+ extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
+ || mp->prev_msgid_plural != NULL
+ ? 1
+ : 0);
+
+ end_css_class (stream, class_comment);
+
+ /* Print each of the message components. Wrap them nicely so they
+ are as readable as possible. */
+ if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt)
+ && po_charset_canonicalize (charset) != po_charset_utf8)
+ {
+ char *warning_message =
+ xasprintf (_("\
+The following msgctxt contains non-ASCII characters.\n\
+This will cause problems to translators who use a character encoding\n\
+different from yours. Consider using a pure ASCII msgctxt instead.\n\
+%s\n"), mp->msgctxt);
+ po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
+ free (warning_message);
+ }
+ if (!is_ascii_string (mp->msgid)
+ && po_charset_canonicalize (charset) != po_charset_utf8)
+ {
+ char *warning_message =
+ xasprintf (_("\
+The following msgid contains non-ASCII characters.\n\
+This will cause problems to translators who use a character encoding\n\
+different from yours. Consider using a pure ASCII msgid instead.\n\
+%s\n"), mp->msgid);
+ po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
+ free (warning_message);
+ }
+ if (mp->msgctxt != NULL)
+ wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgctxt", mp->msgctxt,
+ mp->do_wrap, page_width, charset);
+ wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgid", mp->msgid,
+ mp->do_wrap, page_width, charset);
+ if (mp->msgid_plural != NULL)
+ wrap (mp, stream, "#~ ", extra_indent, class_msgid, "msgid_plural",
+ mp->msgid_plural, mp->do_wrap, page_width, charset);
+
+ if (mp->msgid_plural == NULL)
+ wrap (mp, stream, "#~ ", extra_indent, class_msgstr, "msgstr", mp->msgstr,
+ mp->do_wrap, page_width, charset);
+ else
+ {
+ char prefix_buf[20];
+ unsigned int i;
+ const char *p;
+
+ for (p = mp->msgstr, i = 0;
+ p < mp->msgstr + mp->msgstr_len;
+ p += strlen (p) + 1, i++)
+ {
+ sprintf (prefix_buf, "msgstr[%u]", i);
+ wrap (mp, stream, "#~ ", extra_indent, class_msgstr, prefix_buf, p,
+ mp->do_wrap, page_width, charset);
+ }
+ }
+
+ end_css_class (stream, class_obsolete);
+}
+
+
+static void
+msgdomain_list_print_po (msgdomain_list_ty *mdlp, ostream_t stream,
+ size_t page_width, bool debug)
+{
+ size_t j, k;
+ bool blank_line;
+
+ /* Write out the messages for each domain. */
+ blank_line = false;
+ for (k = 0; k < mdlp->nitems; k++)
+ {
+ message_list_ty *mlp;
+ const char *header;
+ const char *charset;
+ char *allocated_charset;
+
+ /* If the first domain is the default, don't bother emitting
+ the domain name, because it is the default. */
+ if (!(k == 0
+ && strcmp (mdlp->item[k]->domain, MESSAGE_DOMAIN_DEFAULT) == 0))
+ {
+ if (blank_line)
+ print_blank_line (stream);
+ begin_css_class (stream, class_keyword);
+ ostream_write_str (stream, "domain");
+ end_css_class (stream, class_keyword);
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_string);
+ ostream_write_str (stream, "\"");
+ begin_css_class (stream, class_text);
+ ostream_write_str (stream, mdlp->item[k]->domain);
+ end_css_class (stream, class_text);
+ ostream_write_str (stream, "\"");
+ end_css_class (stream, class_string);
+ ostream_write_str (stream, "\n");
+ blank_line = true;
+ }
+
+ mlp = mdlp->item[k]->messages;
+
+ /* Search the header entry. */
+ header = NULL;
+ for (j = 0; j < mlp->nitems; ++j)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ header = mlp->item[j]->msgstr;
+ break;
+ }
+
+ /* Extract the charset name. */
+ charset = "ASCII";
+ allocated_charset = NULL;
+ if (header != NULL)
+ {
+ const char *charsetstr = c_strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ allocated_charset = (char *) xmalloca (len + 1);
+ memcpy (allocated_charset, charsetstr, len);
+ allocated_charset[len] = '\0';
+ charset = allocated_charset;
+
+ /* Treat the dummy default value as if it were absent. */
+ if (strcmp (charset, "CHARSET") == 0)
+ charset = "ASCII";
+ }
+ }
+
+ /* Write out each of the messages for this domain. */
+ for (j = 0; j < mlp->nitems; ++j)
+ if (!mlp->item[j]->obsolete)
+ {
+ message_print (mlp->item[j], stream, charset, page_width,
+ blank_line, debug);
+ blank_line = true;
+ }
+
+ /* Write out each of the obsolete messages for this domain. */
+ for (j = 0; j < mlp->nitems; ++j)
+ if (mlp->item[j]->obsolete)
+ {
+ message_print_obsolete (mlp->item[j], stream, charset, page_width,
+ blank_line);
+ blank_line = true;
+ }
+
+ if (allocated_charset != NULL)
+ freea (allocated_charset);
+ }
+}
+
+
+/* Describes a PO file in .po syntax. */
+const struct catalog_output_format output_format_po =
+{
+ msgdomain_list_print_po, /* print */
+ false, /* requires_utf8 */
+ true, /* supports_color */
+ true, /* supports_multiple_domains */
+ true, /* supports_contexts */
+ true, /* supports_plurals */
+ true, /* sorts_obsoletes_to_end */
+ false, /* alternative_is_po */
+ false /* alternative_is_java_class */
+};
diff --git a/gettext-tools/src/write-po.h b/gettext-tools/src/write-po.h
new file mode 100644
index 0000000..9a243bf
--- /dev/null
+++ b/gettext-tools/src/write-po.h
@@ -0,0 +1,87 @@
+/* GNU gettext - internationalization aids
+ Copyright (C) 1995-1998, 2000-2003, 2006, 2008 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_PO_H
+#define _WRITE_PO_H
+
+#include "ostream.h"
+#include "message.h"
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum filepos_comment_type
+ {
+ filepos_comment_none,
+ filepos_comment_full,
+ filepos_comment_file
+ };
+
+/* These functions are used to output a #, flags line. */
+extern const char *
+ make_format_description_string (enum is_format is_format,
+ const char *lang, bool debug);
+extern bool
+ significant_format_p (enum is_format is_format);
+
+extern char *
+ make_range_description_string (struct argument_range range);
+
+/* These functions output parts of a message, as comments. */
+extern void
+ message_print_comment (const message_ty *mp, ostream_t stream);
+extern void
+ message_print_comment_dot (const message_ty *mp, ostream_t stream);
+extern void
+ message_print_comment_filepos (const message_ty *mp, ostream_t stream,
+ bool uniforum, size_t page_width);
+extern void
+ message_print_comment_flags (const message_ty *mp, ostream_t stream,
+ bool debug);
+
+/* These functions set some parameters for use by 'output_format_po.print'. */
+extern void
+ message_page_width_ignore (void);
+extern void
+ message_print_style_indent (void);
+extern void
+ message_print_style_uniforum (void);
+extern void
+ message_print_style_escape (bool flag);
+extern void
+ message_print_style_filepos (enum filepos_comment_type type);
+
+/* --add-location argument handling. Return an error indicator. */
+extern bool handle_filepos_comment_option (const char *option);
+
+
+/* Describes a PO file in .po syntax. */
+extern DLL_VARIABLE const struct catalog_output_format output_format_po;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WRITE_PO_H */
diff --git a/gettext-tools/src/write-properties.c b/gettext-tools/src/write-properties.c
new file mode 100644
index 0000000..8b6dcee
--- /dev/null
+++ b/gettext-tools/src/write-properties.c
@@ -0,0 +1,305 @@
+/* Writing Java .properties files.
+ Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-properties.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "message.h"
+#include "msgl-ascii.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "ostream.h"
+#include "write-po.h"
+#include "xalloc.h"
+
+/* The format of the Java .properties files is documented in the JDK
+ documentation for class java.util.Properties. In the case of .properties
+ files for PropertyResourceBundle, for each message, the msgid becomes the
+ key (left-hand side) and the msgstr becomes the value (right-hand side)
+ of a "key=value" line. Messages with plurals are not supported in this
+ format. */
+
+/* Handling of comments: We copy all comments from the PO file to the
+ .properties file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .properties file. */
+
+/* Converts a string to JAVA encoding (with \uxxxx sequences for non-ASCII
+ characters). */
+static const char *
+conv_to_java (const char *string)
+{
+ /* We cannot use iconv to "JAVA" because not all iconv() implementations
+ know about the "JAVA" encoding. */
+ static const char hexdigit[] = "0123456789abcdef";
+ size_t length;
+ char *result;
+
+ if (is_ascii_string (string))
+ return string;
+
+ length = 0;
+ {
+ const char *str = string;
+ const char *str_limit = str + strlen (str);
+
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ length += (uc <= 0x007f ? 1 : uc < 0x10000 ? 6 : 12);
+ }
+ }
+
+ result = XNMALLOC (length + 1, char);
+
+ {
+ char *newstr = result;
+ const char *str = string;
+ const char *str_limit = str + strlen (str);
+
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc <= 0x007f)
+ /* ASCII characters can be output literally.
+ We could treat non-ASCII ISO-8859-1 characters (0x0080..0x00FF)
+ the same way, but there is no point in doing this; Sun's
+ nativetoascii doesn't do it either. */
+ *newstr++ = uc;
+ else if (uc < 0x10000)
+ {
+ /* Single UCS-2 'char' */
+ sprintf (newstr, "\\u%c%c%c%c",
+ hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
+ hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
+ newstr += 6;
+ }
+ else
+ {
+ /* UTF-16 surrogate: two 'char's. */
+ ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
+ ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+ sprintf (newstr, "\\u%c%c%c%c",
+ hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
+ hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
+ newstr += 6;
+ sprintf (newstr, "\\u%c%c%c%c",
+ hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
+ hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
+ newstr += 6;
+ }
+ }
+ *newstr = '\0';
+ }
+
+ return result;
+}
+
+/* Writes a key or value to the stream, without newline. */
+static void
+write_escaped_string (ostream_t stream, const char *str, bool in_key)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ const char *str_limit = str + strlen (str);
+ bool first = true;
+
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ /* Whitespace must be escaped. */
+ if (uc == 0x0020 && (first || in_key))
+ ostream_write_str (stream, "\\ ");
+ else if (uc == 0x0009)
+ ostream_write_str (stream, "\\t");
+ else if (uc == 0x000a)
+ ostream_write_str (stream, "\\n");
+ else if (uc == 0x000d)
+ ostream_write_str (stream, "\\r");
+ else if (uc == 0x000c)
+ ostream_write_str (stream, "\\f");
+ else if (/* Backslash must be escaped. */
+ uc == '\\'
+ /* Possible comment introducers must be escaped. */
+ || uc == '#' || uc == '!'
+ /* Key terminators must be escaped. */
+ || uc == '=' || uc == ':')
+ {
+ char seq[2];
+ seq[0] = '\\';
+ seq[1] = uc;
+ ostream_write_mem (stream, seq, 2);
+ }
+ else if (uc >= 0x0020 && uc <= 0x007e)
+ {
+ /* ASCII characters can be output literally.
+ We could treat non-ASCII ISO-8859-1 characters (0x0080..0x00FF)
+ the same way, but there is no point in doing this; Sun's
+ nativetoascii doesn't do it either. */
+ char seq[1];
+ seq[0] = uc;
+ ostream_write_mem (stream, seq, 1);
+ }
+ else if (uc < 0x10000)
+ {
+ /* Single UCS-2 'char' */
+ char seq[6];
+ seq[0] = '\\';
+ seq[1] = 'u';
+ seq[2] = hexdigit[(uc >> 12) & 0x0f];
+ seq[3] = hexdigit[(uc >> 8) & 0x0f];
+ seq[4] = hexdigit[(uc >> 4) & 0x0f];
+ seq[5] = hexdigit[uc & 0x0f];
+ ostream_write_mem (stream, seq, 6);
+ }
+ else
+ {
+ /* UTF-16 surrogate: two 'char's. */
+ ucs4_t uc1 = 0xd800 + ((uc - 0x10000) >> 10);
+ ucs4_t uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+ char seq[6];
+ seq[0] = '\\';
+ seq[1] = 'u';
+ seq[2] = hexdigit[(uc1 >> 12) & 0x0f];
+ seq[3] = hexdigit[(uc1 >> 8) & 0x0f];
+ seq[4] = hexdigit[(uc1 >> 4) & 0x0f];
+ seq[5] = hexdigit[uc1 & 0x0f];
+ ostream_write_mem (stream, seq, 6);
+ seq[0] = '\\';
+ seq[1] = 'u';
+ seq[2] = hexdigit[(uc2 >> 12) & 0x0f];
+ seq[3] = hexdigit[(uc2 >> 8) & 0x0f];
+ seq[4] = hexdigit[(uc2 >> 4) & 0x0f];
+ seq[5] = hexdigit[uc2 & 0x0f];
+ ostream_write_mem (stream, seq, 6);
+ }
+ first = false;
+ }
+}
+
+/* Writes a message to the stream. */
+static void
+write_message (ostream_t stream, const message_ty *mp,
+ size_t page_width, bool debug)
+{
+ /* Print translator comment if available. */
+ message_print_comment (mp, stream);
+
+ /* Print xgettext extracted comments. */
+ message_print_comment_dot (mp, stream);
+
+ /* Print the file position comments. */
+ message_print_comment_filepos (mp, stream, false, page_width);
+
+ /* Print flag information in special comment. */
+ message_print_comment_flags (mp, stream, debug);
+
+ /* Put a comment mark if the message is the header or untranslated or
+ fuzzy. */
+ if (is_header (mp)
+ || mp->msgstr[0] == '\0'
+ || (mp->is_fuzzy && !is_header (mp)))
+ ostream_write_str (stream, "!");
+
+ /* Now write the untranslated string and the translated string. */
+ write_escaped_string (stream, mp->msgid, true);
+ ostream_write_str (stream, "=");
+ write_escaped_string (stream, mp->msgstr, false);
+
+ ostream_write_str (stream, "\n");
+}
+
+/* Writes an entire message list to the stream. */
+static void
+write_properties (ostream_t stream, message_list_ty *mlp,
+ const char *canon_encoding, size_t page_width, bool debug)
+{
+ bool blank_line;
+ size_t j, i;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->comment != NULL)
+ for (i = 0; i < mp->comment->nitems; ++i)
+ mp->comment->item[i] = conv_to_java (mp->comment->item[i]);
+ if (mp->comment_dot != NULL)
+ for (i = 0; i < mp->comment_dot->nitems; ++i)
+ mp->comment_dot->item[i] = conv_to_java (mp->comment_dot->item[i]);
+ }
+
+ /* Loop through the messages. */
+ blank_line = false;
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ const message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural == NULL && !mp->obsolete)
+ {
+ if (blank_line)
+ ostream_write_str (stream, "\n");
+
+ write_message (stream, mp, page_width, debug);
+
+ blank_line = true;
+ }
+ }
+}
+
+/* Output the contents of a PO file in Java .properties syntax. */
+static void
+msgdomain_list_print_properties (msgdomain_list_ty *mdlp, ostream_t stream,
+ size_t page_width, bool debug)
+{
+ message_list_ty *mlp;
+
+ if (mdlp->nitems == 1)
+ mlp = mdlp->item[0]->messages;
+ else
+ mlp = message_list_alloc (false);
+ write_properties (stream, mlp, mdlp->encoding, page_width, debug);
+}
+
+/* Describes a PO file in Java .properties syntax. */
+const struct catalog_output_format output_format_properties =
+{
+ msgdomain_list_print_properties, /* print */
+ true, /* requires_utf8 */
+ false, /* supports_color */
+ false, /* supports_multiple_domains */
+ false, /* supports_contexts */
+ false, /* supports_plurals */
+ false, /* sorts_obsoletes_to_end */
+ true, /* alternative_is_po */
+ true /* alternative_is_java_class */
+};
diff --git a/gettext-tools/src/write-properties.h b/gettext-tools/src/write-properties.h
new file mode 100644
index 0000000..9f614b5
--- /dev/null
+++ b/gettext-tools/src/write-properties.h
@@ -0,0 +1,26 @@
+/* Writing Java .properties files.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_PROPERTIES_H
+#define _WRITE_PROPERTIES_H
+
+#include "write-catalog.h"
+
+/* Describes a PO file in Java .properties syntax. */
+extern DLL_VARIABLE const struct catalog_output_format output_format_properties;
+
+#endif /* _WRITE_PROPERTIES_H */
diff --git a/gettext-tools/src/write-qt.c b/gettext-tools/src/write-qt.c
new file mode 100644
index 0000000..50b96b7
--- /dev/null
+++ b/gettext-tools/src/write-qt.c
@@ -0,0 +1,754 @@
+/* Writing Qt .qm files.
+ Copyright (C) 2003, 2005-2007, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-qt.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "xerror.h"
+#include "message.h"
+#include "po-charset.h"
+#include "msgl-iconv.h"
+#include "hash-string.h"
+#include "unistr.h"
+#include "xalloc.h"
+#include "obstack.h"
+#include "hash.h"
+#include "binary-io.h"
+#include "fwriteerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+/* Qt .qm files are read by the QTranslator::load() function and written
+ by the Qt QTranslator::save() function.
+
+ The Qt tool 'msg2qm' uses the latter function and can convert PO files
+ to .qm files. But since 'msg2qm' is marked as an "old" tool in Qt 3.0.5's
+ i18n.html documentation and therefore likely to disappear, we provide the
+ same functionality here.
+
+ The format of .qm files, as reverse engineered from the functions
+ QTranslator::save(const QString& filename, SaveMode mode)
+ QTranslator::squeeze(SaveMode mode)
+ QTranslatorMessage::write(QDataStream& stream, bool strip, Prefix prefix)
+ elfHash(const char* name)
+ in qt-3.0.5, is as follows:
+
+ It's a binary data format. Elements are u8 (byte), u16, u32. They are
+ written in big-endian order.
+
+ The file starts with a magic string of 16 bytes:
+ 3C B8 64 18 CA EF 9C 95 CD 21 1C BF 60 A1 BD DD
+
+ Then come three sections. Each of the three sections is optional. Each
+ has this structure:
+ struct {
+ u8 section_type; // 0x42 = hashes, 0x69 = messages, 0x2f = contexts
+ u32 length; // number of bytes of the data
+ u8 data[length];
+ };
+
+ In the first section, the hashes section, the data has the following
+ structure:
+ It's a sorted array of
+ struct {
+ u32 hashcode; // elfHash of the concatenation of msgid and
+ // disambiguating-comment
+ u32 offset; // offset within the data[] of the messages section
+ };
+ It's sorted in ascending order by hashcode as primary sorting criteria
+ and - when the hashcodes are the same - by offset as secondary criteria.
+
+ In the second section, the messages section, the data has the following
+ structure:
+ It's a sequence of records, each representing a message, in no
+ particular order. Each record is a sequence of subsections, each
+ introduced by a particular subsection tag. The possible subsection tags
+ are (and they usually occur in this order):
+ - 03: Translation. Followed by the msgstr in UCS-2 or UTF-16 format:
+ struct {
+ u32 length;
+ u16 chars[length/2];
+ };
+ - 08: Disambiguating-comment. Followed by the NUL-terminated,
+ ISO-8859-1 encoded, disambiguating-comment string:
+ struct {
+ u32 length; // number of bytes including the NUL at the end
+ u8 chars[length];
+ };
+ - 06: SourceText, i.e. msgid. Followed by the NUL-terminated,
+ ISO-8859-1 encoded, msgid:
+ struct {
+ u32 length; // number of bytes including the NUL at the end
+ u8 chars[length];
+ };
+ - 02: SourceText16, i.e. msgid. Encoded as UCS-2, but must actually
+ be ISO-8859-1.
+ struct {
+ u32 length;
+ u16 chars[length/2];
+ };
+ This subsection tag is obsoleted by SourceText.
+ - 07: Context. Followed by the NUL-terminated, ISO-8859-1 encoded,
+ context string (usually a C++ class name or empty):
+ struct {
+ u32 length; // number of bytes including the NUL at the end
+ u8 chars[length];
+ };
+ - 04: Context16. Encoded as UCS-2, but must actually be ISO-8859-1.
+ struct {
+ u32 length;
+ u16 chars[length/2];
+ };
+ This subsection tag is obsoleted by Context.
+ - 05: Hash. Followed by
+ struct {
+ u32 hashcode; // elfHash of the concatenation of msgid and
+ // disambiguating-comment
+ };
+ - 01: End. Designates the end of the record. No further data.
+ Usually the following subsections are written, but some of them are
+ optional:
+ - 03: Translation.
+ - 08: Disambiguating-comment (optional).
+ - 06: SourceText (optional).
+ - 07: Context (optional).
+ - 05: Hash.
+ - 01: End.
+ A subsection can be omitted if the value to be output is the same as
+ for the previous record.
+
+ The third section, the contexts section, contains the set of all occurring
+ context strings. This section is optional; it is used to speed up the
+ search. The data is a hash table with the following structure:
+ struct {
+ u16 table_size;
+ u16 buckets[table_size];
+ u8 pool[...];
+ };
+ pool[...] contains:
+ u16 zero;
+ for i = 0, ..., table_size:
+ if there are context strings with elfHash(context)%table_size == i:
+ for all context strings with elfHash(context)%table_size == i:
+ len := min(length(context),255); // truncated to length 255
+ struct {
+ u8 len;
+ u8 chars[len];
+ };
+ struct {
+ u8 zero[1]; // signals the end of this bucket
+ u8 padding[0 or 1]; // padding for even number of bytes
+ };
+ buckets[i] is 0 for an empty bucket, or the offset in pool[] where
+ the context strings for this bucket start, divided by 2.
+ This context section must not be used
+ - if the empty context is used, or
+ - if a context of length > 255 is used, or
+ - if the context pool's size would be > 2^17.
+
+ The elfHash function is the same as our hash_string function, except that
+ at the end it maps a hash code of 0x00000000 to 0x00000001.
+
+ When we convert from PO file format, all disambiguating-comments and
+ contexts are empty, and therefore the contexts section can be omitted. */
+
+
+/* Write a u8 (a single byte) to the output stream. */
+static inline void
+write_u8 (FILE *output_file, unsigned char value)
+{
+ putc (value, output_file);
+}
+
+/* Write a u16 (two bytes) to the output stream. */
+static inline void
+write_u16 (FILE *output_file, unsigned short value)
+{
+ unsigned char data[2];
+
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+
+ fwrite (data, 2, 1, output_file);
+}
+
+/* Write a u32 (four bytes) to the output stream. */
+static inline void
+write_u32 (FILE *output_file, unsigned int value)
+{
+ unsigned char data[4];
+
+ data[0] = (value >> 24) & 0xff;
+ data[1] = (value >> 16) & 0xff;
+ data[2] = (value >> 8) & 0xff;
+ data[3] = value & 0xff;
+
+ fwrite (data, 4, 1, output_file);
+}
+
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Add a u8 (a single byte) to an obstack. */
+static void
+append_u8 (struct obstack *mempool, unsigned char value)
+{
+ unsigned char data[1];
+
+ data[0] = value;
+
+ obstack_grow (mempool, data, 1);
+}
+
+/* Add a u16 (two bytes) to an obstack. */
+static void
+append_u16 (struct obstack *mempool, unsigned short value)
+{
+ unsigned char data[2];
+
+ data[0] = (value >> 8) & 0xff;
+ data[1] = value & 0xff;
+
+ obstack_grow (mempool, data, 2);
+}
+
+/* Add a u32 (four bytes) to an obstack. */
+static void
+append_u32 (struct obstack *mempool, unsigned int value)
+{
+ unsigned char data[4];
+
+ data[0] = (value >> 24) & 0xff;
+ data[1] = (value >> 16) & 0xff;
+ data[2] = (value >> 8) & 0xff;
+ data[3] = value & 0xff;
+
+ obstack_grow (mempool, data, 4);
+}
+
+/* Add an ISO-8859-1 encoded string to an obstack. */
+static void
+append_base_string (struct obstack *mempool, const char *string)
+{
+ size_t length = strlen (string) + 1;
+ append_u32 (mempool, length);
+ obstack_grow (mempool, string, length);
+}
+
+/* Add an UTF-16 encoded string to an obstack. */
+static void
+append_unicode_string (struct obstack *mempool, const unsigned short *string,
+ size_t length)
+{
+ append_u32 (mempool, length * 2);
+ for (; length > 0; string++, length--)
+ append_u16 (mempool, *string);
+}
+
+/* Retrieve a 4-byte integer from memory. */
+static inline unsigned int
+peek_u32 (const unsigned char *p)
+{
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+/* Convert an UTF-8 string to ISO-8859-1, without error checking. */
+static char *
+conv_to_iso_8859_1 (const char *string)
+{
+ size_t length = strlen (string);
+ const char *str = string;
+ const char *str_limit = string + length;
+ /* Conversion to ISO-8859-1 can only reduce the number of bytes. */
+ char *result = XNMALLOC (length + 1, char);
+ char *q = result;
+
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ /* It has already been verified that the string fits in ISO-8859-1. */
+ if (!(uc < 0x100))
+ abort ();
+ /* Store as ISO-8859-1. */
+ *q++ = (unsigned char) uc;
+ }
+ *q = '\0';
+ assert (q - result <= length);
+
+ return result;
+}
+
+/* Convert an UTF-8 string to UTF-16, returning its size (number of UTF-16
+ codepoints) in *SIZEP. */
+static unsigned short *
+conv_to_utf16 (const char *string, size_t *sizep)
+{
+ size_t length = strlen (string);
+ const char *str = string;
+ const char *str_limit = string + length;
+ /* Conversion to UTF-16 can at most double the number of bytes. */
+ unsigned short *result = XNMALLOC (length, unsigned short);
+ unsigned short *q = result;
+
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc < 0x10000)
+ /* UCS-2 character. */
+ *q++ = (unsigned short) uc;
+ else
+ {
+ /* UTF-16 surrogate. */
+ *q++ = 0xd800 + ((uc - 0x10000) >> 10);
+ *q++ = 0xdc00 + ((uc - 0x10000) & 0x3ff);
+ }
+ }
+ assert (q - result <= 2 * length);
+
+ *sizep = q - result;
+ return result;
+}
+
+/* Return the Qt hash code of a string. */
+static unsigned int
+string_hashcode (const char *str)
+{
+ unsigned int h;
+
+ h = hash_string (str);
+ if (h == 0)
+ h = 1;
+ return h;
+}
+
+/* Compare two entries of the hashes section. */
+static int
+cmp_hashes (const void *va, const void *vb)
+{
+ const unsigned char *a = (const unsigned char *) va;
+ const unsigned char *b = (const unsigned char *) vb;
+ unsigned int a_hashcode = peek_u32 (a);
+ unsigned int b_hashcode = peek_u32 (b);
+
+ if (a_hashcode != b_hashcode)
+ return (a_hashcode >= b_hashcode ? 1 : -1);
+ else
+ {
+ unsigned int a_offset = peek_u32 (a + 4);
+ unsigned int b_offset = peek_u32 (b + 4);
+
+ if (a_offset != b_offset)
+ return (a_offset >= b_offset ? 1 : -1);
+ else
+ return 0;
+ }
+}
+
+
+/* Write a section to the output stream. */
+static void
+write_section (FILE *output_file, unsigned char tag, void *data, size_t size)
+{
+ /* A section can be omitted if it is empty. */
+ if (size > 0)
+ {
+ write_u8 (output_file, tag);
+ write_u32 (output_file, size);
+ fwrite (data, size, 1, output_file);
+ }
+}
+
+
+/* Write an entire .qm file. */
+static void
+write_qm (FILE *output_file, message_list_ty *mlp)
+{
+ static unsigned char magic[16] =
+ {
+ 0x3C, 0xB8, 0x64, 0x18, 0xCA, 0xEF, 0x9C, 0x95,
+ 0xCD, 0x21, 0x1C, 0xBF, 0x60, 0xA1, 0xBD, 0xDD
+ };
+ struct obstack hashes_pool;
+ struct obstack messages_pool;
+ size_t j;
+
+ obstack_init (&hashes_pool);
+ obstack_init (&messages_pool);
+
+ /* Prepare the hashes section and the messages section. */
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ /* No need to emit the header entry, it's not needed at runtime. */
+ if (!is_header (mp))
+ {
+ char *msgctxt_as_iso_8859_1 =
+ conv_to_iso_8859_1 (mp->msgctxt != NULL ? mp->msgctxt : "");
+ char *msgid_as_iso_8859_1 = conv_to_iso_8859_1 (mp->msgid);
+ size_t msgstr_len;
+ unsigned short *msgstr_as_utf16 =
+ conv_to_utf16 (mp->msgstr, &msgstr_len);
+ unsigned int hashcode = string_hashcode (msgid_as_iso_8859_1);
+ unsigned int offset = obstack_object_size (&messages_pool);
+
+ /* Add a record to the hashes section. */
+ append_u32 (&hashes_pool, hashcode);
+ append_u32 (&hashes_pool, offset);
+
+ /* Add a record to the messages section. */
+
+ append_u8 (&messages_pool, 0x03);
+ append_unicode_string (&messages_pool, msgstr_as_utf16, msgstr_len);
+
+ append_u8 (&messages_pool, 0x08);
+ append_base_string (&messages_pool, "");
+
+ append_u8 (&messages_pool, 0x06);
+ append_base_string (&messages_pool, msgid_as_iso_8859_1);
+
+ append_u8 (&messages_pool, 0x07);
+ append_base_string (&messages_pool, msgctxt_as_iso_8859_1);
+
+ append_u8 (&messages_pool, 0x05);
+ append_u32 (&messages_pool, hashcode);
+
+ append_u8 (&messages_pool, 0x01);
+
+ free (msgstr_as_utf16);
+ free (msgid_as_iso_8859_1);
+ free (msgctxt_as_iso_8859_1);
+ }
+ }
+
+ /* Sort the hashes section. */
+ {
+ size_t nstrings = obstack_object_size (&hashes_pool) / 8;
+ if (nstrings > 0)
+ qsort (obstack_base (&hashes_pool), nstrings, 8, cmp_hashes);
+ }
+
+ /* Write the magic number. */
+ fwrite (magic, sizeof (magic), 1, output_file);
+
+ /* Write the hashes section. */
+ write_section (output_file, 0x42, obstack_base (&hashes_pool),
+ obstack_object_size (&hashes_pool));
+
+ /* Write the messages section. */
+ write_section (output_file, 0x69, obstack_base (&messages_pool),
+ obstack_object_size (&messages_pool));
+
+ /* Decide whether to write a contexts section. */
+ {
+ bool can_write_contexts = true;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (!is_header (mp))
+ if (mp->msgctxt == NULL || mp->msgctxt[0] == '\0'
+ || strlen (mp->msgctxt) > 255)
+ {
+ can_write_contexts = false;
+ break;
+ }
+ }
+
+ if (can_write_contexts)
+ {
+ hash_table all_contexts;
+ size_t num_contexts;
+ unsigned long table_size;
+
+ /* Collect the contexts, removing duplicates. */
+ hash_init (&all_contexts, 10);
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (!is_header (mp))
+ hash_insert_entry (&all_contexts,
+ mp->msgctxt, strlen (mp->msgctxt) + 1,
+ NULL);
+ }
+
+ /* Compute the number of different contexts. */
+ num_contexts = all_contexts.size;
+
+ /* Compute a suitable hash table size. */
+ table_size = next_prime (num_contexts * 1.7);
+ if (table_size >= 0x10000)
+ table_size = 65521;
+
+ /* Put the contexts into a hash table of size table_size. */
+ {
+ struct list_cell { const char *context; struct list_cell *next; };
+ struct list_cell *list_memory =
+ XNMALLOC (table_size, struct list_cell);
+ struct list_cell *freelist;
+ struct bucket { struct list_cell *head; struct list_cell **tail; };
+ struct bucket *buckets = XNMALLOC (table_size, struct bucket);
+ size_t i;
+
+ freelist = list_memory;
+
+ for (i = 0; i < table_size; i++)
+ {
+ buckets[i].head = NULL;
+ buckets[i].tail = &buckets[i].head;
+ }
+
+ {
+ void *iter;
+ const void *key;
+ size_t keylen;
+ void *null;
+
+ iter = NULL;
+ while (hash_iterate (&all_contexts, &iter, &key, &keylen, &null)
+ == 0)
+ {
+ const char *context = (const char *)key;
+ i = string_hashcode (context) % table_size;
+ freelist->context = context;
+ freelist->next = NULL;
+ *buckets[i].tail = freelist;
+ buckets[i].tail = &freelist->next;
+ freelist++;
+ }
+ }
+
+ /* Determine the total context pool size. */
+ {
+ size_t pool_size;
+
+ pool_size = 2;
+ for (i = 0; i < table_size; i++)
+ if (buckets[i].head != NULL)
+ {
+ const struct list_cell *p;
+
+ for (p = buckets[i].head; p != NULL; p = p->next)
+ pool_size += 1 + strlen (p->context);
+ pool_size++;
+ if ((pool_size % 2) != 0)
+ pool_size++;
+ }
+ if (pool_size <= 0x20000)
+ {
+ /* Prepare the contexts section. */
+ struct obstack contexts_pool;
+ size_t pool_offset;
+
+ obstack_init (&contexts_pool);
+
+ append_u16 (&contexts_pool, table_size);
+ pool_offset = 2;
+ for (i = 0; i < table_size; i++)
+ if (buckets[i].head != NULL)
+ {
+ const struct list_cell *p;
+
+ append_u16 (&contexts_pool, pool_offset / 2);
+ for (p = buckets[i].head; p != NULL; p = p->next)
+ pool_offset += 1 + strlen (p->context);
+ pool_offset++;
+ if ((pool_offset % 2) != 0)
+ pool_offset++;
+ }
+ else
+ append_u16 (&contexts_pool, 0);
+ if (!(pool_offset == pool_size))
+ abort ();
+
+ append_u16 (&contexts_pool, 0);
+ pool_offset = 2;
+ for (i = 0; i < table_size; i++)
+ if (buckets[i].head != NULL)
+ {
+ const struct list_cell *p;
+
+ for (p = buckets[i].head; p != NULL; p = p->next)
+ {
+ append_u8 (&contexts_pool, strlen (p->context));
+ obstack_grow (&contexts_pool,
+ p->context, strlen (p->context));
+ pool_offset += 1 + strlen (p->context);
+ }
+ append_u8 (&contexts_pool, 0);
+ pool_offset++;
+ if ((pool_offset % 2) != 0)
+ {
+ append_u8 (&contexts_pool, 0);
+ pool_offset++;
+ }
+ }
+ if (!(pool_offset == pool_size))
+ abort ();
+
+ if (!(obstack_object_size (&contexts_pool)
+ == 2 + 2 * table_size + pool_size))
+ abort ();
+
+ /* Write the contexts section. */
+ write_section (output_file, 0x2f, obstack_base (&contexts_pool),
+ obstack_object_size (&contexts_pool));
+
+ obstack_free (&contexts_pool, NULL);
+ }
+ }
+
+ free (buckets);
+ free (list_memory);
+ }
+
+ hash_destroy (&all_contexts);
+ }
+ }
+
+ obstack_free (&messages_pool, NULL);
+ obstack_free (&hashes_pool, NULL);
+}
+
+
+int
+msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
+ const char *domain_name, const char *file_name)
+{
+ FILE *output_file;
+
+ /* If no entry for this domain don't even create the file. */
+ if (mlp->nitems != 0)
+ {
+ /* Determine whether mlp has plural entries. */
+ {
+ bool has_plural;
+ size_t j;
+
+ has_plural = false;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ has_plural = true;
+ if (has_plural)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has plural form translations\n\
+but the Qt message catalog format doesn't support plural handling\n")));
+ return 1;
+ }
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Determine whether mlp has non-ISO-8859-1 msgctxt entries. */
+ {
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ const char *string = mlp->item[j]->msgctxt;
+
+ if (string != NULL)
+ {
+ /* An UTF-8 encoded string fits in ISO-8859-1 if and only if
+ all its bytes are < 0xc4. */
+ for (; *string; string++)
+ if ((unsigned char) *string >= 0xc4)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has msgctxt strings containing characters outside ISO-8859-1\n\
+but the Qt message catalog format supports Unicode only in the translated\n\
+strings, not in the context strings\n")));
+ return 1;
+ }
+ }
+ }
+ }
+
+ /* Determine whether mlp has non-ISO-8859-1 msgid entries. */
+ {
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ const char *string = mlp->item[j]->msgid;
+
+ /* An UTF-8 encoded string fits in ISO-8859-1 if and only if all
+ its bytes are < 0xc4. */
+ for (; *string; string++)
+ if ((unsigned char) *string >= 0xc4)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has msgid strings containing characters outside ISO-8859-1\n\
+but the Qt message catalog format supports Unicode only in the translated\n\
+strings, not in the untranslated strings\n")));
+ return 1;
+ }
+ }
+ }
+
+ if (strcmp (domain_name, "-") == 0)
+ {
+ output_file = stdout;
+ SET_BINARY (fileno (output_file));
+ }
+ else
+ {
+ output_file = fopen (file_name, "wb");
+ if (output_file == NULL)
+ {
+ error (0, errno, _("error while opening \"%s\" for writing"),
+ file_name);
+ return 1;
+ }
+ }
+
+ if (output_file != NULL)
+ {
+ write_qm (output_file, mlp);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (output_file))
+ error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ file_name);
+ }
+ }
+
+ return 0;
+}
diff --git a/gettext-tools/src/write-qt.h b/gettext-tools/src/write-qt.h
new file mode 100644
index 0000000..2b36da2
--- /dev/null
+++ b/gettext-tools/src/write-qt.h
@@ -0,0 +1,30 @@
+/* Writing Qt .qm files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_QT_H
+#define _WRITE_QT_H
+
+#include "message.h"
+
+/* Write a Qt .qm file. mlp is a list containing the messages to be output.
+ domain_name is the domain name, file_name is the desired file name.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_qt (message_list_ty *mlp, const char *canon_encoding,
+ const char *domain_name, const char *file_name);
+
+#endif /* _WRITE_QT_H */
diff --git a/gettext-tools/src/write-resources.c b/gettext-tools/src/write-resources.c
new file mode 100644
index 0000000..6759c1b
--- /dev/null
+++ b/gettext-tools/src/write-resources.c
@@ -0,0 +1,194 @@
+/* Writing C# .resources files.
+ Copyright (C) 2003, 2005, 2007-2009, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-resources.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "error.h"
+#include "xerror.h"
+#include "relocatable.h"
+#include "csharpexec.h"
+#include "spawn-pipe.h"
+#include "wait-process.h"
+#include "message.h"
+#include "msgfmt.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "concat-filename.h"
+#include "fwriteerror.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* A .resources file has such a complex format that it's most easily generated
+ through the C# class ResourceWriter. So we start a C# process to execute
+ the WriteResource program, sending it the msgid/msgstr pairs as
+ NUL-terminated UTF-8 encoded strings. */
+
+struct locals
+{
+ /* IN */
+ message_list_ty *mlp;
+};
+
+static bool
+execute_writing_input (const char *progname,
+ const char *prog_path, char **prog_argv,
+ void *private_data)
+{
+ struct locals *l = (struct locals *) private_data;
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ int exitstatus;
+
+ /* Open a pipe to the C# execution engine. */
+ child = create_pipe_out (progname, prog_path, prog_argv, NULL, false,
+ true, true, fd);
+
+ fp = fdopen (fd[0], "wb");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+ /* Write the message list. */
+ {
+ message_list_ty *mlp = l->mlp;
+ size_t j;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ fwrite (mp->msgid, 1, strlen (mp->msgid) + 1, fp);
+ fwrite (mp->msgstr, 1, strlen (mp->msgstr) + 1, fp);
+ }
+ }
+
+ if (fwriteerror (fp))
+ error (EXIT_FAILURE, 0, _("error while writing to %s subprocess"),
+ progname);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ /* He we can ignore SIGPIPE because WriteResource either writes to a file
+ - then it never gets SIGPIPE - or to standard output, and in the latter
+ case it has no side effects other than writing to standard output. */
+ exitstatus =
+ wait_subprocess (child, progname, true, false, true, true, NULL);
+ if (exitstatus != 0)
+ error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+ progname, exitstatus);
+
+ return false;
+}
+
+int
+msgdomain_write_csharp_resources (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *domain_name,
+ const char *file_name)
+{
+ /* If no entry for this domain don't even create the file. */
+ if (mlp->nitems != 0)
+ {
+ /* Determine whether mlp has entries with context. */
+ {
+ bool has_context;
+ size_t j;
+
+ has_context = false;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgctxt != NULL)
+ has_context = true;
+ if (has_context)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has context dependent translations\n\
+but the C# .resources format doesn't support contexts\n")));
+ return 1;
+ }
+ }
+
+ /* Determine whether mlp has plural entries. */
+ {
+ bool has_plural;
+ size_t j;
+
+ has_plural = false;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ has_plural = true;
+ if (has_plural)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has plural form translations\n\
+but the C# .resources format doesn't support plural handling\n")));
+ return 1;
+ }
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Execute the WriteResource program. */
+ {
+ const char *args[2];
+ const char *gettextexedir;
+ char *assembly_path;
+ struct locals locals;
+
+ /* Prepare arguments. */
+ args[0] = file_name;
+ args[1] = NULL;
+
+ /* Make it possible to override the .exe location. This is
+ necessary for running the testsuite before "make install". */
+ gettextexedir = getenv ("GETTEXTCSHARPEXEDIR");
+ if (gettextexedir == NULL || gettextexedir[0] == '\0')
+ gettextexedir = relocate (LIBDIR "/gettext");
+
+ assembly_path =
+ xconcatenated_filename (gettextexedir, "msgfmt.net", ".exe");
+
+ locals.mlp = mlp;
+
+ if (execute_csharp_program (assembly_path, NULL, 0,
+ args,
+ verbose > 0, false,
+ execute_writing_input, &locals))
+ /* An error message should already have been provided. */
+ exit (EXIT_FAILURE);
+
+ free (assembly_path);
+ }
+ }
+
+ return 0;
+}
diff --git a/gettext-tools/src/write-resources.h b/gettext-tools/src/write-resources.h
new file mode 100644
index 0000000..7512f0f
--- /dev/null
+++ b/gettext-tools/src/write-resources.h
@@ -0,0 +1,31 @@
+/* Writing C# .resources files.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_RESOURCES_H
+#define _WRITE_RESOURCES_H
+
+#include "message.h"
+
+/* Output the contents of a PO file as a binary C# .resources file.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_csharp_resources (message_list_ty *mlp,
+ const char *canon_encoding,
+ const char *domain_name,
+ const char *file_name);
+
+#endif /* _WRITE_RESOURCES_H */
diff --git a/gettext-tools/src/write-stringtable.c b/gettext-tools/src/write-stringtable.c
new file mode 100644
index 0000000..34e171f
--- /dev/null
+++ b/gettext-tools/src/write-stringtable.c
@@ -0,0 +1,335 @@
+/* Writing NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003, 2006-2008 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "write-stringtable.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "msgl-ascii.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "c-strstr.h"
+#include "ostream.h"
+#include "xvasprintf.h"
+#include "write-po.h"
+
+/* The format of NeXTstep/GNUstep .strings files is documented in
+ gnustep-base-1.8.0/Tools/make_strings/Using.txt
+ and in the comments of method propertyListFromStringsFileFormat in
+ gnustep-base-1.8.0/Source/NSString.m
+ In summary, it's a Objective-C like file with pseudo-assignments of the form
+ "key" = "value";
+ where the key is the msgid and the value is the msgstr.
+ */
+
+/* Handling of comments: We copy all comments from the PO file to the
+ .strings file. This is not really needed; it's a service for translators
+ who don't like PO files and prefer to maintain the .strings file. */
+
+/* Since the interpretation of text files in GNUstep depends on the locale's
+ encoding if they don't have a BOM, we choose one of three encodings with
+ a BOM: UCS-2BE, UCS-2LE, UTF-8. Since the first two of these don't cope
+ with all of Unicode and we don't know whether GNUstep will switch to
+ UTF-16 instead of UCS-2, we use UTF-8 with BOM. BOMs are bad because they
+ get in the way when concatenating files, but here we have no choice. */
+
+/* Writes a key or value to the stream, without newline. */
+static void
+write_escaped_string (ostream_t stream, const char *str)
+{
+ const char *str_limit = str + strlen (str);
+
+ ostream_write_str (stream, "\"");
+ while (str < str_limit)
+ {
+ unsigned char c = (unsigned char) *str++;
+
+ if (c == '\t')
+ ostream_write_str (stream, "\\t");
+ else if (c == '\n')
+ ostream_write_str (stream, "\\n");
+ else if (c == '\r')
+ ostream_write_str (stream, "\\r");
+ else if (c == '\f')
+ ostream_write_str (stream, "\\f");
+ else if (c == '\\' || c == '"')
+ {
+ char seq[2];
+ seq[0] = '\\';
+ seq[1] = c;
+ ostream_write_mem (stream, seq, 2);
+ }
+ else
+ {
+ char seq[1];
+ seq[0] = c;
+ ostream_write_mem (stream, seq, 1);
+ }
+ }
+ ostream_write_str (stream, "\"");
+}
+
+/* Writes a message to the stream. */
+static void
+write_message (ostream_t stream, const message_ty *mp,
+ size_t page_width, bool debug)
+{
+ /* Print translator comment if available. */
+ if (mp->comment != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment->nitems; ++j)
+ {
+ const char *s = mp->comment->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (c_strstr (s, "*/") == NULL)
+ {
+ ostream_write_str (stream, "/*");
+ if (*s != '\0' && *s != '\n')
+ ostream_write_str (stream, " ");
+ ostream_write_str (stream, s);
+ ostream_write_str (stream, " */\n");
+ }
+ else
+ do
+ {
+ const char *e;
+ ostream_write_str (stream, "//");
+ if (*s != '\0' && *s != '\n')
+ ostream_write_str (stream, " ");
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ ostream_write_str (stream, s);
+ s = NULL;
+ }
+ else
+ {
+ ostream_write_mem (stream, s, e - s);
+ s = e + 1;
+ }
+ ostream_write_str (stream, "\n");
+ }
+ while (s != NULL);
+ }
+ }
+
+ /* Print xgettext extracted comments. */
+ if (mp->comment_dot != NULL)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->comment_dot->nitems; ++j)
+ {
+ const char *s = mp->comment_dot->item[j];
+
+ /* Test whether it is safe to output the comment in C style, or
+ whether we need C++ style for it. */
+ if (c_strstr (s, "*/") == NULL)
+ {
+ ostream_write_str (stream, "/* Comment: ");
+ ostream_write_str (stream, s);
+ ostream_write_str (stream, " */\n");
+ }
+ else
+ {
+ bool first = true;
+ do
+ {
+ const char *e;
+ ostream_write_str (stream, "//");
+ if (first || (*s != '\0' && *s != '\n'))
+ ostream_write_str (stream, " ");
+ if (first)
+ ostream_write_str (stream, "Comment: ");
+ e = strchr (s, '\n');
+ if (e == NULL)
+ {
+ ostream_write_str (stream, s);
+ s = NULL;
+ }
+ else
+ {
+ ostream_write_mem (stream, s, e - s);
+ s = e + 1;
+ }
+ ostream_write_str (stream, "\n");
+ first = false;
+ }
+ while (s != NULL);
+ }
+ }
+ }
+
+ /* Print the file position comments. */
+ if (mp->filepos_count != 0)
+ {
+ size_t j;
+
+ for (j = 0; j < mp->filepos_count; ++j)
+ {
+ lex_pos_ty *pp = &mp->filepos[j];
+ const char *cp = pp->file_name;
+ char *str;
+
+ while (cp[0] == '.' && cp[1] == '/')
+ cp += 2;
+ str = xasprintf ("/* File: %s:%ld */\n", cp, (long) pp->line_number);
+ ostream_write_str (stream, str);
+ free (str);
+ }
+ }
+
+ /* Print flag information in special comment. */
+ if (mp->is_fuzzy || mp->msgstr[0] == '\0')
+ ostream_write_str (stream, "/* Flag: untranslated */\n");
+ if (mp->obsolete)
+ ostream_write_str (stream, "/* Flag: unmatched */\n");
+ {
+ size_t i;
+ for (i = 0; i < NFORMATS; i++)
+ if (significant_format_p (mp->is_format[i]))
+ {
+ ostream_write_str (stream, "/* Flag: ");
+ ostream_write_str (stream,
+ make_format_description_string (mp->is_format[i],
+ format_language[i],
+ debug));
+ ostream_write_str (stream, " */\n");
+ }
+ }
+ if (has_range_p (mp->range))
+ {
+ char *string;
+
+ ostream_write_str (stream, "/* Flag: ");
+ string = make_range_description_string (mp->range);
+ ostream_write_str (stream, string);
+ free (string);
+ ostream_write_str (stream, " */\n");
+ }
+
+ /* Now write the untranslated string and the translated string. */
+ write_escaped_string (stream, mp->msgid);
+ ostream_write_str (stream, " = ");
+ if (mp->msgstr[0] != '\0')
+ {
+ if (mp->is_fuzzy)
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (stream, mp->msgid);
+
+ /* Output the msgstr as a comment, so that at runtime
+ propertyListFromStringsFileFormat ignores it. */
+ if (c_strstr (mp->msgstr, "*/") == NULL)
+ {
+ ostream_write_str (stream, " /* = ");
+ write_escaped_string (stream, mp->msgstr);
+ ostream_write_str (stream, " */");
+ }
+ else
+ {
+ ostream_write_str (stream, "; // = ");
+ write_escaped_string (stream, mp->msgstr);
+ }
+ }
+ else
+ write_escaped_string (stream, mp->msgstr);
+ }
+ else
+ {
+ /* Output the msgid as value, so that at runtime the untranslated
+ string is returned. */
+ write_escaped_string (stream, mp->msgid);
+ }
+ ostream_write_str (stream, ";");
+
+ ostream_write_str (stream, "\n");
+}
+
+/* Writes an entire message list to the stream. */
+static void
+write_stringtable (ostream_t stream, message_list_ty *mlp,
+ const char *canon_encoding, size_t page_width, bool debug)
+{
+ bool blank_line;
+ size_t j;
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Output the BOM. */
+ if (!is_ascii_message_list (mlp))
+ ostream_write_str (stream, "\xef\xbb\xbf");
+
+ /* Loop through the messages. */
+ blank_line = false;
+ for (j = 0; j < mlp->nitems; ++j)
+ {
+ const message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural == NULL)
+ {
+ if (blank_line)
+ ostream_write_str (stream, "\n");
+
+ write_message (stream, mp, page_width, debug);
+
+ blank_line = true;
+ }
+ }
+}
+
+/* Output the contents of a PO file in .strings syntax. */
+static void
+msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, ostream_t stream,
+ size_t page_width, bool debug)
+{
+ message_list_ty *mlp;
+
+ if (mdlp->nitems == 1)
+ mlp = mdlp->item[0]->messages;
+ else
+ mlp = message_list_alloc (false);
+ write_stringtable (stream, mlp, mdlp->encoding, page_width, debug);
+}
+
+/* Describes a PO file in .strings syntax. */
+const struct catalog_output_format output_format_stringtable =
+{
+ msgdomain_list_print_stringtable, /* print */
+ true, /* requires_utf8 */
+ false, /* supports_color */
+ false, /* supports_multiple_domains */
+ false, /* supports_contexts */
+ false, /* supports_plurals */
+ false, /* sorts_obsoletes_to_end */
+ false, /* alternative_is_po */
+ false /* alternative_is_java_class */
+};
diff --git a/gettext-tools/src/write-stringtable.h b/gettext-tools/src/write-stringtable.h
new file mode 100644
index 0000000..3130467
--- /dev/null
+++ b/gettext-tools/src/write-stringtable.h
@@ -0,0 +1,26 @@
+/* Writing NeXTstep/GNUstep .strings files.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_STRINGTABLE_H
+#define _WRITE_STRINGTABLE_H
+
+#include "write-catalog.h"
+
+/* Describes a PO file in .strings syntax. */
+extern DLL_VARIABLE const struct catalog_output_format output_format_stringtable;
+
+#endif /* _WRITE_STRINGTABLE_H */
diff --git a/gettext-tools/src/write-tcl.c b/gettext-tools/src/write-tcl.c
new file mode 100644
index 0000000..db582c6
--- /dev/null
+++ b/gettext-tools/src/write-tcl.c
@@ -0,0 +1,229 @@
+/* Writing tcl/msgcat .msg files.
+ Copyright (C) 2002-2003, 2005, 2007-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+/* Specification. */
+#include "write-tcl.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "xerror.h"
+#include "message.h"
+#include "msgl-iconv.h"
+#include "po-charset.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "concat-filename.h"
+#include "fwriteerror.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Write a string in Tcl Unicode notation to the given stream.
+ Tcl 8 uses Unicode for its internal string representation.
+ In tcl-8.3.3, the .msg files are read in using the locale dependent
+ encoding. The only way to specify strings in an encoding independent
+ form is the \unnnn notation. Newer tcl versions have this fixed:
+ they read the .msg files in UTF-8 encoding. */
+static void
+write_tcl_string (FILE *stream, const char *str)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ const char *str_limit = str + strlen (str);
+
+ fprintf (stream, "\"");
+ while (str < str_limit)
+ {
+ ucs4_t uc;
+ unsigned int count;
+ count = u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
+ if (uc < 0x10000)
+ {
+ /* Single UCS-2 'char'. */
+ if (uc == 0x000a)
+ fprintf (stream, "\\n");
+ else if (uc == 0x000d)
+ fprintf (stream, "\\r");
+ else if (uc == 0x0022)
+ fprintf (stream, "\\\"");
+ else if (uc == 0x0024)
+ fprintf (stream, "\\$");
+ else if (uc == 0x005b)
+ fprintf (stream, "\\[");
+ else if (uc == 0x005c)
+ fprintf (stream, "\\\\");
+ else if (uc == 0x005d)
+ fprintf (stream, "\\]");
+ /* No need to escape '{' and '}' because we don't have opening
+ braces outside the strings. */
+#if 0
+ else if (uc == 0x007b)
+ fprintf (stream, "\\{");
+ else if (uc == 0x007d)
+ fprintf (stream, "\\}");
+#endif
+ else if (uc >= 0x0020 && uc < 0x007f)
+ fprintf (stream, "%c", (int) uc);
+ else
+ fprintf (stream, "\\u%c%c%c%c",
+ hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
+ hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
+ }
+ else
+ /* The \unnnn notation doesn't support characters >= 0x10000.
+ We output them as UTF-8 byte sequences and hope that either
+ the Tcl version reading them will be new enough or that the
+ user is using an UTF-8 locale. */
+ fwrite (str, 1, count, stream);
+ str += count;
+ }
+ fprintf (stream, "\"");
+}
+
+
+static void
+write_msg (FILE *output_file, message_list_ty *mlp, const char *locale_name)
+{
+ size_t j;
+
+ /* We don't care about esthetic formattic of the output (like respecting
+ a maximum line width, or including the translator comments) because
+ the \unnnn notation is unesthetic anyway. Translators shall edit
+ the PO file. */
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (is_header (mp))
+ /* Tcl's msgcat unit ignores this, but msgunfmt needs it. */
+ fprintf (output_file, "set ::msgcat::header ");
+ else
+ {
+ fprintf (output_file, "::msgcat::mcset %s ", locale_name);
+ write_tcl_string (output_file, mp->msgid);
+ fprintf (output_file, " ");
+ }
+ write_tcl_string (output_file, mp->msgstr);
+ fprintf (output_file, "\n");
+ }
+}
+
+int
+msgdomain_write_tcl (message_list_ty *mlp, const char *canon_encoding,
+ const char *locale_name,
+ const char *directory)
+{
+ /* If no entry for this domain don't even create the file. */
+ if (mlp->nitems == 0)
+ return 0;
+
+ /* Determine whether mlp has entries with context. */
+ {
+ bool has_context;
+ size_t j;
+
+ has_context = false;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgctxt != NULL)
+ has_context = true;
+ if (has_context)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has context dependent translations\n\
+but the Tcl message catalog format doesn't support contexts\n")));
+ return 1;
+ }
+ }
+
+ /* Determine whether mlp has plural entries. */
+ {
+ bool has_plural;
+ size_t j;
+
+ has_plural = false;
+ for (j = 0; j < mlp->nitems; j++)
+ if (mlp->item[j]->msgid_plural != NULL)
+ has_plural = true;
+ if (has_plural)
+ {
+ multiline_error (xstrdup (""),
+ xstrdup (_("\
+message catalog has plural form translations\n\
+but the Tcl message catalog format doesn't support plural handling\n")));
+ return 1;
+ }
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
+
+ /* Now create the file. */
+ {
+ size_t len;
+ char *frobbed_locale_name;
+ char *p;
+ char *file_name;
+ FILE *output_file;
+
+ /* Convert the locale name to lowercase and remove any encoding. */
+ len = strlen (locale_name);
+ frobbed_locale_name = (char *) xmalloca (len + 1);
+ memcpy (frobbed_locale_name, locale_name, len + 1);
+ for (p = frobbed_locale_name; *p != '\0'; p++)
+ if (*p >= 'A' && *p <= 'Z')
+ *p = *p - 'A' + 'a';
+ else if (*p == '.')
+ {
+ *p = '\0';
+ break;
+ }
+
+ file_name = xconcatenated_filename (directory, frobbed_locale_name, ".msg");
+
+ output_file = fopen (file_name, "w");
+ if (output_file == NULL)
+ {
+ error (0, errno, _("error while opening \"%s\" for writing"),
+ file_name);
+ freea (frobbed_locale_name);
+ return 1;
+ }
+
+ write_msg (output_file, mlp, frobbed_locale_name);
+
+ /* Make sure nothing went wrong. */
+ if (fwriteerror (output_file))
+ error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"),
+ file_name);
+
+ freea (frobbed_locale_name);
+ }
+
+ return 0;
+}
diff --git a/gettext-tools/src/write-tcl.h b/gettext-tools/src/write-tcl.h
new file mode 100644
index 0000000..e6a3dce
--- /dev/null
+++ b/gettext-tools/src/write-tcl.h
@@ -0,0 +1,32 @@
+/* Writing tcl/msgcat .msg files.
+ Copyright (C) 2002-2003 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _WRITE_TCL_H
+#define _WRITE_TCL_H
+
+#include "message.h"
+
+/* Write a Tcl msg file. mlp is a list containing the messages to be output.
+ locale_name is the locale name (with underscore separators), directory is
+ the base directory.
+ Return 0 if ok, nonzero on error. */
+extern int
+ msgdomain_write_tcl (message_list_ty *mlp, const char *canon_encoding,
+ const char *locale_name,
+ const char *directory);
+
+#endif /* _WRITE_TCL_H */
diff --git a/gettext-tools/src/x-awk.c b/gettext-tools/src/x-awk.c
new file mode 100644
index 0000000..6a6a9dc
--- /dev/null
+++ b/gettext-tools/src/x-awk.c
@@ -0,0 +1,887 @@
+/* xgettext awk backend.
+ Copyright (C) 2002-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-awk.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+
+/* The awk syntax is defined in the gawk manual page and documentation.
+ See also gawk/awkgram.y. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_awk_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_awk_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_awk_keyword ("dcgettext");
+ x_awk_keyword ("dcngettext:1,2");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_awk ()
+{
+ xgettext_record_flag ("dcgettext:1:pass-awk-format");
+ xgettext_record_flag ("dcngettext:1:pass-awk-format");
+ xgettext_record_flag ("dcngettext:2:pass-awk-format");
+ xgettext_record_flag ("printf:1:awk-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* 1. line_number handling. */
+
+static int
+phase1_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ ungetc (c, fp);
+ }
+}
+
+
+/* 2. Replace each comment that is not inside a string literal or regular
+ expression with a newline character. We need to remember the comment
+ for later, because it may be attached to a keyword string. */
+
+static int
+phase2_getc ()
+{
+ static char *buffer;
+ static size_t bufmax;
+ size_t buflen;
+ int lineno;
+ int c;
+
+ c = phase1_getc ();
+ if (c == '#')
+ {
+ buflen = 0;
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ {
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ last_comment_line = lineno;
+ }
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ phase1_ungetc (c);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_string, /* "abc" */
+ token_type_i18nstring, /* _"abc" */
+ token_type_symbol, /* symbol, number */
+ token_type_semicolon, /* ; */
+ token_type_other /* regexp, misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_{symbol,string,i18nstring} */
+ int line_number;
+};
+
+
+/* 7. Replace escape sequences within character strings with their
+ single character equivalents. */
+
+#define P7_QUOTES (1000 + '"')
+
+static int
+phase7_getc ()
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 1, because phase 2 elides comments. */
+ c = phase1_getc ();
+
+ if (c == EOF || c == '\n')
+ break;
+ if (c == '"')
+ return P7_QUOTES;
+ if (c != '\\')
+ return c;
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c != '\n')
+ switch (c)
+ {
+ case 'a':
+ return '\a';
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = phase1_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = phase1_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ phase1_ungetc (c);
+ }
+ }
+ else
+ phase1_ungetc (c);
+ }
+ return (unsigned char) n;
+ }
+ case 'x':
+ {
+ int n = 0;
+
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ else if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ else
+ {
+ phase1_ungetc (c);
+ break;
+ }
+ }
+ return (unsigned char) n;
+ }
+ default:
+ return c;
+ }
+ }
+
+ phase1_ungetc (c);
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated string"), logical_file_name,
+ line_number);
+ error_with_progname = true;
+ return P7_QUOTES;
+}
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ switch (tp->type)
+ {
+ case token_type_string:
+ case token_type_i18nstring:
+ case token_type_symbol:
+ free (tp->string);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Combine characters into tokens. Discard whitespace. */
+
+/* There is an ambiguity about '/': It can start a division operator ('/' or
+ '/=') or it can start a regular expression. The distinction is important
+ because inside regular expressions, '#' and '"' lose its special meanings.
+ If you look at the awk grammar, you see that the operator is only allowed
+ right after a 'variable' or 'simp_exp' nonterminal, and these nonterminals
+ can only end in the NAME, LENGTH, YSTRING, YNUMBER, ')', ']' terminals.
+ So we prefer the division operator interpretation only right after
+ symbol, string, number, ')', ']', with whitespace but no newline allowed
+ in between. */
+static bool prefer_division_over_regexp;
+
+static void
+x_awk_lex (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase2_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* Newline is not allowed inside expressions. It usually
+ introduces a fresh statement.
+ FIXME: Newlines after any of ',' '{' '?' ':' '||' '&&' 'do' 'else'
+ does *not* introduce a fresh statement. */
+ prefer_division_over_regexp = false;
+ /* FALLTHROUGH */
+ case '\t':
+ case ' ':
+ /* Ignore whitespace and comments. */
+ continue;
+
+ case '\\':
+ /* Backslash ought to be immediately followed by a newline. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case '.':
+ {
+ int c2 = phase2_getc ();
+ phase2_ungetc (c2);
+ if (!(c2 >= '0' && c2 <= '9'))
+ {
+
+ tp->type = token_type_other;
+ prefer_division_over_regexp = false;
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Symbol, or part of a number. */
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase2_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ default:
+ if (bufpos == 1 && buffer[0] == '_' && c == '"')
+ {
+ tp->type = token_type_i18nstring;
+ goto case_string;
+ }
+ phase2_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ /* Most identifiers can be variable names; after them we must
+ interpret '/' as division operator. But for awk's builtin
+ keywords we have three cases:
+ (a) Must interpret '/' as division operator. "length".
+ (b) Must interpret '/' as start of a regular expression.
+ "do", "exit", "print", "printf", "return".
+ (c) '/' after this keyword in invalid anyway. All others.
+ I used the following script for the distinction.
+ for k in $awk_keywords; do
+ echo; echo $k; awk "function foo () { $k / 10 }" < /dev/null
+ done
+ */
+ if (strcmp (buffer, "do") == 0
+ || strcmp (buffer, "exit") == 0
+ || strcmp (buffer, "print") == 0
+ || strcmp (buffer, "printf") == 0
+ || strcmp (buffer, "return") == 0)
+ prefer_division_over_regexp = false;
+ else
+ prefer_division_over_regexp = true;
+ return;
+
+ case '"':
+ tp->type = token_type_string;
+ case_string:
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase7_getc ();
+ if (c == EOF || c == P7_QUOTES)
+ break;
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ prefer_division_over_regexp = true;
+ return;
+
+ case '(':
+ tp->type = token_type_lparen;
+ prefer_division_over_regexp = false;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ prefer_division_over_regexp = true;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ prefer_division_over_regexp = false;
+ return;
+
+ case ';':
+ tp->type = token_type_semicolon;
+ prefer_division_over_regexp = false;
+ return;
+
+ case ']':
+ tp->type = token_type_other;
+ prefer_division_over_regexp = true;
+ return;
+
+ case '/':
+ if (!prefer_division_over_regexp)
+ {
+ /* Regular expression.
+ Counting brackets is non-trivial. [[] is balanced, and so is
+ [\]]. Also, /[/]/ is balanced and ends at the third slash.
+ Do not count [ or ] if either one is preceded by a \.
+ A '[' should be counted if
+ a) it is the first one so far (brackets == 0), or
+ b) it is the '[' in '[:'.
+ A ']' should be counted if not preceded by a \.
+ According to POSIX, []] is how you put a ] into a set.
+ Try to handle that too.
+ */
+ int brackets = 0;
+ bool pos0 = true; /* true at start of regexp */
+ bool pos1_open = false; /* true after [ at start of regexp */
+ bool pos2_open_not = false; /* true after [^ at start of regexp */
+
+ for (;;)
+ {
+ c = phase1_getc ();
+
+ if (c == EOF || c == '\n')
+ {
+ phase1_ungetc (c);
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated regular expression"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ break;
+ }
+ else if (c == '[')
+ {
+ if (brackets == 0)
+ brackets++;
+ else
+ {
+ c = phase1_getc ();
+ if (c == ':')
+ brackets++;
+ phase1_ungetc (c);
+ }
+ if (pos0)
+ {
+ pos0 = false;
+ pos1_open = true;
+ continue;
+ }
+ }
+ else if (c == ']')
+ {
+ if (!(pos1_open || pos2_open_not))
+ brackets--;
+ }
+ else if (c == '^')
+ {
+ if (pos1_open)
+ {
+ pos1_open = false;
+ pos2_open_not = true;
+ continue;
+ }
+ }
+ else if (c == '\\')
+ {
+ c = phase1_getc ();
+ /* Backslash-newline is valid and ignored. */
+ }
+ else if (c == '/')
+ {
+ if (brackets <= 0)
+ break;
+ }
+
+ pos0 = false;
+ pos1_open = false;
+ pos2_open_not = false;
+ }
+
+ tp->type = token_type_other;
+ prefer_division_over_regexp = false;
+ return;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = token_type_other;
+ prefer_division_over_regexp = false;
+ return;
+ }
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis.
+ Extracted messages are added to MLP.
+ Return true upon eof, false upon closing parenthesis. */
+static bool
+extract_parenthesized (message_list_ty *mlp,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Whether to implicitly assume the next tokens are arguments even without
+ a '('. */
+ bool next_is_argument = false;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_awk_lex (&token);
+
+ if (next_is_argument && token.type != token_type_lparen)
+ {
+ /* An argument list starts, even though there is no '('. */
+ context_iter = next_context_iter;
+ outer_context = inner_context;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ }
+
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_is_argument =
+ (strcmp (token.string, "print") == 0
+ || strcmp (token.string, "printf") == 0);
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_parenthesized (mlp, inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ arglist_parser_done (argparser, arg);
+ return false;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_is_argument = false;
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context, &pos,
+ NULL, savable_comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ savable_comment);
+ }
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_i18nstring:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ remember_a_message (mlp, NULL, token.string, inner_context, &pos,
+ NULL, savable_comment);
+ }
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_semicolon:
+ /* An argument list ends, and a new statement begins. */
+ /* FIXME: Should handle newline that acts as statement separator
+ in the same way. */
+ /* FIXME: Instead of resetting outer_context here, it may be better
+ to recurse in the next_is_argument handling above, waiting for
+ the next semicolon or other statement terminator. */
+ outer_context = null_context;
+ context_iter = null_context_list_iterator;
+ next_is_argument = false;
+ next_context_iter = passthrough_context_list_iterator;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ arglist_parser_done (argparser, arg);
+ return true;
+
+ case token_type_other:
+ next_is_argument = false;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_awk (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ prefer_division_over_regexp = false;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-awk.h b/gettext-tools/src/x-awk.h
new file mode 100644
index 0000000..642c0e2
--- /dev/null
+++ b/gettext-tools/src/x-awk.h
@@ -0,0 +1,51 @@
+/* xgettext awk backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_AWK \
+ { "awk", "awk" }, \
+
+#define SCANNERS_AWK \
+ { "awk", extract_awk, \
+ &flag_table_awk, &formatstring_awk, NULL, NULL }, \
+
+/* Scan an awk file and add its translatable strings to mdlp. */
+extern void extract_awk (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_awk_keyword (const char *keyword);
+extern void x_awk_extract_all (void);
+
+extern void init_flag_table_awk (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-c.c b/gettext-tools/src/x-c.c
new file mode 100644
index 0000000..001cfd9
--- /dev/null
+++ b/gettext-tools/src/x-c.c
@@ -0,0 +1,2174 @@
+/* xgettext C/C++/ObjectiveC backend.
+ Copyright (C) 1995-1998, 2000-2009, 2012 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-c.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The ANSI C standard defines several phases of translation:
+
+ 1. Terminate line by \n, regardless of the external representation
+ of a text line. Stdio does this for us.
+
+ 2. Convert trigraphs to their single character equivalents.
+
+ 3. Concatenate each line ending in backslash (\) with the following
+ line.
+
+ 4. Replace each comment with a space character.
+
+ 5. Parse each resulting logical line as preprocessing tokens a
+ white space.
+
+ 6. Recognize and carry out directives (it also expands macros on
+ non-directive lines, which we do not do here).
+
+ 7. Replaces escape sequences within character strings with their
+ single character equivalents (we do this in step 5, because we
+ don't have to worry about the #include argument).
+
+ 8. Concatenates adjacent string literals to form single string
+ literals (because we don't expand macros, there are a few things
+ we will miss).
+
+ 9. Converts the remaining preprocessing tokens to C tokens and
+ discards any white space from the translation unit.
+
+ This lexer implements the above, and presents the scanner (in
+ xgettext.c) with a stream of C tokens. The comments are
+ accumulated in a buffer, and given to xgettext when asked for. */
+
+
+/* ========================= Lexer customization. ========================= */
+
+static bool trigraphs = false;
+
+void
+x_c_trigraphs ()
+{
+ trigraphs = true;
+}
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table c_keywords;
+static hash_table objc_keywords;
+static bool default_keywords = true;
+
+
+void
+x_c_extract_all ()
+{
+ extract_all = true;
+}
+
+
+static void
+add_keyword (const char *name, hash_table *keywords)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords->table == NULL)
+ hash_init (keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (keywords, name, end - name, &shape);
+ }
+}
+
+void
+x_c_keyword (const char *name)
+{
+ add_keyword (name, &c_keywords);
+}
+
+void
+x_objc_keyword (const char *name)
+{
+ add_keyword (name, &objc_keywords);
+}
+
+/* Finish initializing the keywords hash tables.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_c_keyword ("gettext");
+ x_c_keyword ("dgettext:2");
+ x_c_keyword ("dcgettext:2");
+ x_c_keyword ("ngettext:1,2");
+ x_c_keyword ("dngettext:2,3");
+ x_c_keyword ("dcngettext:2,3");
+ x_c_keyword ("gettext_noop");
+ x_c_keyword ("pgettext:1c,2");
+ x_c_keyword ("dpgettext:2c,3");
+ x_c_keyword ("dcpgettext:2c,3");
+ x_c_keyword ("npgettext:1c,2,3");
+ x_c_keyword ("dnpgettext:2c,3,4");
+ x_c_keyword ("dcnpgettext:2c,3,4");
+
+ x_objc_keyword ("gettext");
+ x_objc_keyword ("dgettext:2");
+ x_objc_keyword ("dcgettext:2");
+ x_objc_keyword ("ngettext:1,2");
+ x_objc_keyword ("dngettext:2,3");
+ x_objc_keyword ("dcngettext:2,3");
+ x_objc_keyword ("gettext_noop");
+ x_objc_keyword ("pgettext:1c,2");
+ x_objc_keyword ("dpgettext:2c,3");
+ x_objc_keyword ("dcpgettext:2c,3");
+ x_objc_keyword ("npgettext:1c,2,3");
+ x_objc_keyword ("dnpgettext:2c,3,4");
+ x_objc_keyword ("dcnpgettext:2c,3,4");
+ x_objc_keyword ("NSLocalizedString"); /* similar to gettext */
+ x_objc_keyword ("_"); /* similar to gettext */
+ x_objc_keyword ("NSLocalizedStaticString"); /* similar to gettext_noop */
+ x_objc_keyword ("__"); /* similar to gettext_noop */
+
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_c ()
+{
+ xgettext_record_flag ("gettext:1:pass-c-format");
+ xgettext_record_flag ("dgettext:2:pass-c-format");
+ xgettext_record_flag ("dcgettext:2:pass-c-format");
+ xgettext_record_flag ("ngettext:1:pass-c-format");
+ xgettext_record_flag ("ngettext:2:pass-c-format");
+ xgettext_record_flag ("dngettext:2:pass-c-format");
+ xgettext_record_flag ("dngettext:3:pass-c-format");
+ xgettext_record_flag ("dcngettext:2:pass-c-format");
+ xgettext_record_flag ("dcngettext:3:pass-c-format");
+ xgettext_record_flag ("gettext_noop:1:pass-c-format");
+ xgettext_record_flag ("pgettext:2:pass-c-format");
+ xgettext_record_flag ("dpgettext:3:pass-c-format");
+ xgettext_record_flag ("dcpgettext:3:pass-c-format");
+ xgettext_record_flag ("npgettext:2:pass-c-format");
+ xgettext_record_flag ("npgettext:3:pass-c-format");
+ xgettext_record_flag ("dnpgettext:3:pass-c-format");
+ xgettext_record_flag ("dnpgettext:4:pass-c-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-c-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-c-format");
+
+ /* <stdio.h> */
+ xgettext_record_flag ("fprintf:2:c-format");
+ xgettext_record_flag ("vfprintf:2:c-format");
+ xgettext_record_flag ("printf:1:c-format");
+ xgettext_record_flag ("vprintf:1:c-format");
+ xgettext_record_flag ("sprintf:2:c-format");
+ xgettext_record_flag ("vsprintf:2:c-format");
+ xgettext_record_flag ("snprintf:3:c-format");
+ xgettext_record_flag ("vsnprintf:3:c-format");
+#if 0 /* These functions are not standard. */
+ /* <stdio.h> */
+ xgettext_record_flag ("asprintf:2:c-format");
+ xgettext_record_flag ("vasprintf:2:c-format");
+ xgettext_record_flag ("dprintf:2:c-format");
+ xgettext_record_flag ("vdprintf:2:c-format");
+ xgettext_record_flag ("obstack_printf:2:c-format");
+ xgettext_record_flag ("obstack_vprintf:2:c-format");
+ /* <error.h> */
+ xgettext_record_flag ("error:3:c-format");
+ xgettext_record_flag ("error_at_line:5:c-format");
+ /* <argp.h> */
+ xgettext_record_flag ("argp_error:2:c-format");
+ xgettext_record_flag ("argp_failure:2:c-format");
+#endif
+
+ xgettext_record_flag ("gettext:1:pass-qt-format");
+ xgettext_record_flag ("dgettext:2:pass-qt-format");
+ xgettext_record_flag ("dcgettext:2:pass-qt-format");
+ xgettext_record_flag ("ngettext:1:pass-qt-format");
+ xgettext_record_flag ("ngettext:2:pass-qt-format");
+ xgettext_record_flag ("dngettext:2:pass-qt-format");
+ xgettext_record_flag ("dngettext:3:pass-qt-format");
+ xgettext_record_flag ("dcngettext:2:pass-qt-format");
+ xgettext_record_flag ("dcngettext:3:pass-qt-format");
+ xgettext_record_flag ("gettext_noop:1:pass-qt-format");
+ xgettext_record_flag ("pgettext:2:pass-qt-format");
+ xgettext_record_flag ("dpgettext:3:pass-qt-format");
+ xgettext_record_flag ("dcpgettext:3:pass-qt-format");
+ xgettext_record_flag ("npgettext:2:pass-qt-format");
+ xgettext_record_flag ("npgettext:3:pass-qt-format");
+ xgettext_record_flag ("dnpgettext:3:pass-qt-format");
+ xgettext_record_flag ("dnpgettext:4:pass-qt-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-qt-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-qt-format");
+
+ xgettext_record_flag ("gettext:1:pass-kde-format");
+ xgettext_record_flag ("dgettext:2:pass-kde-format");
+ xgettext_record_flag ("dcgettext:2:pass-kde-format");
+ xgettext_record_flag ("ngettext:1:pass-kde-format");
+ xgettext_record_flag ("ngettext:2:pass-kde-format");
+ xgettext_record_flag ("dngettext:2:pass-kde-format");
+ xgettext_record_flag ("dngettext:3:pass-kde-format");
+ xgettext_record_flag ("dcngettext:2:pass-kde-format");
+ xgettext_record_flag ("dcngettext:3:pass-kde-format");
+ xgettext_record_flag ("gettext_noop:1:pass-kde-format");
+ xgettext_record_flag ("pgettext:2:pass-kde-format");
+ xgettext_record_flag ("dpgettext:3:pass-kde-format");
+ xgettext_record_flag ("dcpgettext:3:pass-kde-format");
+ xgettext_record_flag ("npgettext:2:pass-kde-format");
+ xgettext_record_flag ("npgettext:3:pass-kde-format");
+ xgettext_record_flag ("dnpgettext:3:pass-kde-format");
+ xgettext_record_flag ("dnpgettext:4:pass-kde-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-kde-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-kde-format");
+
+ xgettext_record_flag ("gettext:1:pass-boost-format");
+ xgettext_record_flag ("dgettext:2:pass-boost-format");
+ xgettext_record_flag ("dcgettext:2:pass-boost-format");
+ xgettext_record_flag ("ngettext:1:pass-boost-format");
+ xgettext_record_flag ("ngettext:2:pass-boost-format");
+ xgettext_record_flag ("dngettext:2:pass-boost-format");
+ xgettext_record_flag ("dngettext:3:pass-boost-format");
+ xgettext_record_flag ("dcngettext:2:pass-boost-format");
+ xgettext_record_flag ("dcngettext:3:pass-boost-format");
+ xgettext_record_flag ("gettext_noop:1:pass-boost-format");
+ xgettext_record_flag ("pgettext:2:pass-boost-format");
+ xgettext_record_flag ("dpgettext:3:pass-boost-format");
+ xgettext_record_flag ("dcpgettext:3:pass-boost-format");
+ xgettext_record_flag ("npgettext:2:pass-boost-format");
+ xgettext_record_flag ("npgettext:3:pass-boost-format");
+ xgettext_record_flag ("dnpgettext:3:pass-boost-format");
+ xgettext_record_flag ("dnpgettext:4:pass-boost-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-boost-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-boost-format");
+
+ /* <boost/format.hpp> */
+ xgettext_record_flag ("format:1:boost-format");
+}
+
+void
+init_flag_table_objc ()
+{
+ /* Since the settings done in init_flag_table_c() also have an effect for
+ the ObjectiveC parser, we don't have to repeat them here. */
+ xgettext_record_flag ("gettext:1:pass-objc-format");
+ xgettext_record_flag ("dgettext:2:pass-objc-format");
+ xgettext_record_flag ("dcgettext:2:pass-objc-format");
+ xgettext_record_flag ("ngettext:1:pass-objc-format");
+ xgettext_record_flag ("ngettext:2:pass-objc-format");
+ xgettext_record_flag ("dngettext:2:pass-objc-format");
+ xgettext_record_flag ("dngettext:3:pass-objc-format");
+ xgettext_record_flag ("dcngettext:2:pass-objc-format");
+ xgettext_record_flag ("dcngettext:3:pass-objc-format");
+ xgettext_record_flag ("gettext_noop:1:pass-objc-format");
+ xgettext_record_flag ("pgettext:2:pass-objc-format");
+ xgettext_record_flag ("dpgettext:3:pass-objc-format");
+ xgettext_record_flag ("dcpgettext:3:pass-objc-format");
+ xgettext_record_flag ("npgettext:2:pass-objc-format");
+ xgettext_record_flag ("npgettext:3:pass-objc-format");
+ xgettext_record_flag ("dnpgettext:3:pass-objc-format");
+ xgettext_record_flag ("dnpgettext:4:pass-objc-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-objc-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-objc-format");
+ xgettext_record_flag ("NSLocalizedString:1:pass-c-format");
+ xgettext_record_flag ("NSLocalizedString:1:pass-objc-format");
+ xgettext_record_flag ("_:1:pass-c-format");
+ xgettext_record_flag ("_:1:pass-objc-format");
+ xgettext_record_flag ("stringWithFormat::1:objc-format");
+ xgettext_record_flag ("initWithFormat::1:objc-format");
+ xgettext_record_flag ("stringByAppendingFormat::1:objc-format");
+ xgettext_record_flag ("localizedStringWithFormat::1:objc-format");
+ xgettext_record_flag ("appendFormat::1:objc-format");
+}
+
+void
+init_flag_table_gcc_internal ()
+{
+ xgettext_record_flag ("gettext:1:pass-gcc-internal-format");
+ xgettext_record_flag ("dgettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("dcgettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("ngettext:1:pass-gcc-internal-format");
+ xgettext_record_flag ("ngettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("dngettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("dngettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("dcngettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("dcngettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("gettext_noop:1:pass-gcc-internal-format");
+ xgettext_record_flag ("pgettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("dpgettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("dcpgettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("npgettext:2:pass-gcc-internal-format");
+ xgettext_record_flag ("npgettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("dnpgettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("dnpgettext:4:pass-gcc-internal-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-gcc-internal-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-gcc-internal-format");
+#if 0 /* This should better be done inside GCC. */
+ /* grepping for ATTRIBUTE_PRINTF in gcc-3.3/gcc/?*.h */
+ /* c-format.c */
+ xgettext_record_flag ("status_warning:2:gcc-internal-format");
+ /* c-tree.h */
+ xgettext_record_flag ("pedwarn_c99:1:pass-gcc-internal-format");
+ /* collect2.h */
+ //xgettext_record_flag ("error:1:c-format"); // 3 different versions
+ xgettext_record_flag ("notice:1:c-format");
+ //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions
+ xgettext_record_flag ("fatal_perror:1:c-format");
+ /* cpplib.h */
+ xgettext_record_flag ("cpp_error:3:c-format");
+ xgettext_record_flag ("cpp_error_with_line:5:c-format");
+ /* diagnostic.h */
+ xgettext_record_flag ("diagnostic_set_info:2:pass-gcc-internal-format");
+ xgettext_record_flag ("output_printf:2:gcc-internal-format");
+ xgettext_record_flag ("output_verbatim:2:pass-gcc-internal-format");
+ xgettext_record_flag ("verbatim:1:gcc-internal-format");
+ xgettext_record_flag ("inform:1:pass-gcc-internal-format");
+ /* gcc.h */
+ //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions
+ //xgettext_record_flag ("error:1:c-format"); // 3 different versions
+ /* genattrtab.h */
+ xgettext_record_flag ("attr_printf:2:pass-c-format");
+ /* gengtype.h */
+ xgettext_record_flag ("error_at_line:2:pass-c-format");
+ xgettext_record_flag ("xvasprintf:2:pass-c-format");
+ xgettext_record_flag ("xasprintf:1:pass-c-format");
+ xgettext_record_flag ("oprintf:2:pass-c-format");
+ /* gensupport.h */
+ xgettext_record_flag ("message_with_line:2:pass-c-format");
+ /* output.h */
+ xgettext_record_flag ("output_operand_lossage:1:c-format");
+ /* ra.h */
+ xgettext_record_flag ("ra_debug_msg:2:pass-c-format");
+ /* toplev.h */
+ xgettext_record_flag ("fnotice:2:c-format");
+ xgettext_record_flag ("fatal_io_error:2:gcc-internal-format");
+ xgettext_record_flag ("error_for_asm:2:pass-gcc-internal-format");
+ xgettext_record_flag ("warning_for_asm:2:pass-gcc-internal-format");
+ xgettext_record_flag ("error_with_file_and_line:3:pass-gcc-internal-format");
+ xgettext_record_flag ("error_with_decl:2:pass-gcc-internal-format");
+ xgettext_record_flag ("pedwarn:1:gcc-internal-format");
+ xgettext_record_flag ("pedwarn_with_file_and_line:3:gcc-internal-format");
+ xgettext_record_flag ("pedwarn_with_decl:2:gcc-internal-format");
+ xgettext_record_flag ("sorry:1:gcc-internal-format");
+ xgettext_record_flag ("error:1:pass-gcc-internal-format");
+ xgettext_record_flag ("fatal_error:1:pass-gcc-internal-format");
+ xgettext_record_flag ("internal_error:1:pass-gcc-internal-format");
+ xgettext_record_flag ("warning:1:pass-gcc-internal-format");
+ xgettext_record_flag ("warning_with_file_and_line:3:pass-gcc-internal-format");
+ xgettext_record_flag ("warning_with_decl:2:pass-gcc-internal-format");
+ /* f/com.h */
+ xgettext_record_flag ("ffecom_get_invented_identifier:1:pass-c-format");
+ /* f/sts.h */
+ xgettext_record_flag ("ffests_printf:2:pass-c-format");
+ /* java/java-tree.h */
+ xgettext_record_flag ("parse_error_context:2:pass-c-format");
+#endif
+
+ xgettext_record_flag ("gettext:1:pass-gfc-internal-format");
+ xgettext_record_flag ("dgettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("dcgettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("ngettext:1:pass-gfc-internal-format");
+ xgettext_record_flag ("ngettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("dngettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("dngettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("dcngettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("dcngettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("gettext_noop:1:pass-gfc-internal-format");
+ xgettext_record_flag ("pgettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("dpgettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("dcpgettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("npgettext:2:pass-gfc-internal-format");
+ xgettext_record_flag ("npgettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("dnpgettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("dnpgettext:4:pass-gfc-internal-format");
+ xgettext_record_flag ("dcnpgettext:3:pass-gfc-internal-format");
+ xgettext_record_flag ("dcnpgettext:4:pass-gfc-internal-format");
+#if 0 /* This should better be done inside GCC. */
+ /* fortran/error.c */
+ xgettext_record_flag ("gfc_error:1:gfc-internal-format");
+ xgettext_record_flag ("gfc_error_now:1:gfc-internal-format");
+ xgettext_record_flag ("gfc_fatal_error:1:gfc-internal-format");
+ xgettext_record_flag ("gfc_internal_error:1:gfc-internal-format");
+ xgettext_record_flag ("gfc_notify_std:2:gfc-internal-format");
+ xgettext_record_flag ("gfc_warning:1:gfc-internal-format");
+ xgettext_record_flag ("gfc_warning_now:1:gfc-internal-format");
+#endif
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 0. Terminate line by \n, regardless whether the external representation of
+ a line terminator is LF (Unix), CR (Mac) or CR/LF (DOS/Windows).
+ It is debatable whether supporting CR/LF line terminators in C sources
+ on Unix is ISO C or POSIX compliant, but since GCC 3.3 now supports it
+ unconditionally, it must be OK.
+ The so-called "text mode" in stdio on DOS/Windows translates CR/LF to \n
+ automatically, but here we also need this conversion on Unix. As a side
+ effect, on DOS/Windows we also parse CR/CR/LF into a single \n, but this
+ is not a problem. */
+
+
+static int
+phase0_getc ()
+{
+ int c;
+
+ c = getc (fp);
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\r')
+ {
+ int c1 = getc (fp);
+
+ if (c1 != EOF && c1 != '\n')
+ ungetc (c1, fp);
+
+ /* Seen line terminator CR or CR/LF. */
+ return '\n';
+ }
+
+ return c;
+}
+
+
+/* Supports only one pushback character, and not '\n'. */
+static inline void
+phase0_ungetc (int c)
+{
+ if (c != EOF)
+ ungetc (c, fp);
+}
+
+
+/* 1. line_number handling. Combine backslash-newline to nothing. */
+
+static unsigned char phase1_pushback[2];
+static int phase1_pushback_length;
+
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ {
+ c = phase1_pushback[--phase1_pushback_length];
+ if (c == '\n')
+ ++line_number;
+ return c;
+ }
+ for (;;)
+ {
+ c = phase0_getc ();
+ switch (c)
+ {
+ case '\n':
+ ++line_number;
+ return '\n';
+
+ case '\\':
+ c = phase0_getc ();
+ if (c != '\n')
+ {
+ phase0_ungetc (c);
+ return '\\';
+ }
+ ++line_number;
+ break;
+
+ default:
+ return c;
+ }
+ }
+}
+
+
+/* Supports 2 characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ switch (c)
+ {
+ case EOF:
+ break;
+
+ case '\n':
+ --line_number;
+ /* FALLTHROUGH */
+
+ default:
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ break;
+ }
+}
+
+
+/* 2. Convert trigraphs to their single character equivalents. Most
+ sane human beings vomit copiously at the mention of trigraphs, which
+ is why they are an option. */
+
+static unsigned char phase2_pushback[1];
+static int phase2_pushback_length;
+
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+ if (!trigraphs)
+ return phase1_getc ();
+
+ c = phase1_getc ();
+ if (c != '?')
+ return c;
+ c = phase1_getc ();
+ if (c != '?')
+ {
+ phase1_ungetc (c);
+ return '?';
+ }
+ c = phase1_getc ();
+ switch (c)
+ {
+ case '(':
+ return '[';
+ case '/':
+ return '\\';
+ case ')':
+ return ']';
+ case '\'':
+ return '^';
+ case '<':
+ return '{';
+ case '!':
+ return '|';
+ case '>':
+ return '}';
+ case '-':
+ return '~';
+ case '#':
+ return '=';
+ }
+ phase1_ungetc (c);
+ phase1_ungetc ('?');
+ return '?';
+}
+
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* 3. Concatenate each line ending in backslash (\) with the following
+ line. Basically, all you need to do is elide "\\\n" sequences from
+ the input. */
+
+static unsigned char phase3_pushback[2];
+static int phase3_pushback_length;
+
+
+static int
+phase3_getc ()
+{
+ if (phase3_pushback_length)
+ return phase3_pushback[--phase3_pushback_length];
+ for (;;)
+ {
+ int c = phase2_getc ();
+ if (c != '\\')
+ return c;
+ c = phase2_getc ();
+ if (c != '\n')
+ {
+ phase2_ungetc (c);
+ return '\\';
+ }
+ }
+}
+
+
+/* Supports 2 characters of pushback. */
+static void
+phase3_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = c;
+ }
+}
+
+
+/* Accumulating comments. */
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+static int newline_count;
+
+
+/* 4. Replace each comment that is not inside a character constant or
+ string literal with a space character. We need to remember the
+ comment for later, because it may be attached to a keyword string.
+ We also optionally understand C++ comments. */
+
+static int
+phase4_getc ()
+{
+ int c;
+ bool last_was_star;
+
+ c = phase3_getc ();
+ if (c != '/')
+ return c;
+ c = phase3_getc ();
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ return '/';
+
+ case '*':
+ /* C comment. */
+ comment_start ();
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = newline_count;
+ return ' ';
+
+ case '/':
+ /* C++ or ISO C 99 comment. */
+ comment_start ();
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end (0);
+ last_comment_line = newline_count;
+ return '\n';
+ }
+}
+
+
+/* Supports only one pushback character. */
+static void
+phase4_ungetc (int c)
+{
+ phase3_ungetc (c);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* True if ObjectiveC extensions are recognized. */
+static bool objc_extensions;
+
+enum token_type_ty
+{
+ token_type_character_constant, /* 'x' */
+ token_type_eof,
+ token_type_eoln,
+ token_type_hash, /* # */
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_colon, /* : */
+ token_type_name, /* abc */
+ token_type_number, /* 2.7 */
+ token_type_string_literal, /* "abc" */
+ token_type_symbol, /* < > = etc. */
+ token_type_objc_special, /* @ */
+ token_type_white_space
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_name, token_type_string_literal */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal,
+ token_type_objc_special */
+ long number;
+ int line_number;
+};
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_name || tp->type == token_type_string_literal)
+ free (tp->string);
+ if (tp->type == token_type_string_literal
+ || tp->type == token_type_objc_special)
+ drop_reference (tp->comment);
+}
+
+
+static char *
+literalstring_parse (const char *string, lex_pos_ty *pos,
+ enum literalstring_escape_type type)
+{
+ struct mixed_string_buffer *bp;
+ const char *p;
+
+ /* Start accumulating the string. */
+ bp = mixed_string_buffer_alloc (lc_string,
+ logical_file_name,
+ line_number);
+
+ for (p = string; *p != '\0'; p++)
+ {
+ int c;
+
+ if (*p != '\\')
+ {
+ mixed_string_buffer_append_char (bp, *p);
+ continue;
+ }
+
+ if (!(type & LET_ANSI_C) && !(type & LET_UNICODE))
+ {
+ mixed_string_buffer_append_char (bp, '\\');
+ continue;
+ }
+
+ c = *++p;
+
+ if (type & LET_ANSI_C)
+ switch (c)
+ {
+ case '"':
+ case '\'':
+ case '?':
+ case '\\':
+ mixed_string_buffer_append_char (bp, c);
+ continue;
+
+ case 'a':
+ mixed_string_buffer_append_char (bp, '\a');
+ continue;
+ case 'b':
+ mixed_string_buffer_append_char (bp, '\b');
+ continue;
+
+ /* The \e escape is preculiar to gcc, and assumes an ASCII
+ character set (or superset). We don't provide support for it
+ here. */
+
+ case 'f':
+ mixed_string_buffer_append_char (bp, '\f');
+ continue;
+ case 'n':
+ mixed_string_buffer_append_char (bp, '\n');
+ continue;
+ case 'r':
+ mixed_string_buffer_append_char (bp, '\r');
+ continue;
+ case 't':
+ mixed_string_buffer_append_char (bp, '\t');
+ continue;
+ case 'v':
+ mixed_string_buffer_append_char (bp, '\v');
+ continue;
+
+ case 'x':
+ c = *++p;
+ switch (c)
+ {
+ default:
+ mixed_string_buffer_append_char (bp, '\\');
+ mixed_string_buffer_append_char (bp, 'x');
+ mixed_string_buffer_append_char (bp, c);
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ {
+ int n;
+
+ for (n = 0; ; ++p)
+ {
+ switch (*p)
+ {
+ default:
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = n * 16 + *p - '0';
+ continue;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ n = n * 16 + 10 + *p - 'A';
+ continue;
+
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ n = n * 16 + 10 + *p - 'a';
+ continue;
+ }
+ break;
+ }
+
+ mixed_string_buffer_append_char (bp, n);
+ --p;
+ }
+ break;
+ }
+ continue;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ int n, j;
+
+ for (n = 0, j = 0; j < 3; ++j)
+ {
+ n = n * 8 + c - '0';
+ c = *++p;
+ switch (c)
+ {
+ default:
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ continue;
+ }
+ break;
+ }
+
+ mixed_string_buffer_append_char (bp, n);
+ --p;
+ }
+ continue;
+ }
+
+ if (type & LET_UNICODE)
+ switch (c)
+ {
+ case 'U': case 'u':
+ {
+ unsigned char buf[8];
+ int length = c == 'u' ? 4 : 8;
+ int n, j;
+
+ for (n = 0, j = 0; j < length; j++)
+ {
+ int c1 = *++p;
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ break;
+
+ buf[j] = c1;
+ }
+
+ if (j == length)
+ {
+ if (n < 0x110000)
+ mixed_string_buffer_append_unicode (bp, n);
+ else
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ pos->file_name, pos->line_number,
+ _("\
+warning: invalid Unicode character"));
+ error_with_progname = true;
+ }
+ }
+ else
+ {
+ int i;
+
+ mixed_string_buffer_append_char (bp, '\\');
+ mixed_string_buffer_append_char (bp, c);
+
+ for (i = 0; i < j; i++)
+ mixed_string_buffer_append_char (bp, buf[i]);
+
+ --p;
+ }
+ }
+ continue;
+ }
+
+ mixed_string_buffer_append_char (bp, c);
+ }
+
+ return mixed_string_buffer_done (bp);
+}
+
+struct literalstring_parser literalstring_c =
+ {
+ literalstring_parse
+ };
+
+
+/* 5. Parse each resulting logical line as preprocessing tokens and
+ white space. Preprocessing tokens and C tokens don't always match. */
+
+static token_ty phase5_pushback[1];
+static int phase5_pushback_length;
+
+
+static void
+phase5_get (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+ int last_was_backslash;
+ bool raw_expected = false;
+
+ if (phase5_pushback_length)
+ {
+ *tp = phase5_pushback[--phase5_pushback_length];
+ return;
+ }
+ tp->string = NULL;
+ tp->number = 0;
+ tp->line_number = line_number;
+ c = phase4_getc ();
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ tp->type = token_type_eoln;
+ return;
+
+ case ' ':
+ case '\f':
+ case '\t':
+ for (;;)
+ {
+ c = phase4_getc ();
+ switch (c)
+ {
+ case ' ':
+ case '\f':
+ case '\t':
+ continue;
+
+ default:
+ phase4_ungetc (c);
+ break;
+ }
+ break;
+ }
+ tp->type = token_type_white_space;
+ return;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase4_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+
+ default:
+ /* Recognize C++ string literals prefixed by R, u8, u8R,
+ u, uR, U, UR, L, or LR. It is defined in ISO/IEC
+ 9899:2011 2.14.5. Since gettext's argument is a byte
+ sequence, we are only interested in u8, R, and u8R. */
+ if (c == '"')
+ {
+ bool is_prefix = false;
+
+ switch (buffer[0])
+ {
+ case 'R':
+ if (bufpos == 1)
+ is_prefix = true;
+ break;
+ case 'u':
+ if (bufpos == 1)
+ is_prefix = true;
+ else
+ switch (buffer[1])
+ {
+ case 'R':
+ if (bufpos == 2)
+ is_prefix = true;
+ break;
+ case '8':
+ if (bufpos == 2
+ || (bufpos == 3 && buffer[2] == 'R'))
+ is_prefix = true;
+ break;
+ }
+ break;
+ case 'U':
+ case 'L':
+ if (bufpos == 1
+ || (bufpos == 2 && buffer[1] == 'R'))
+ is_prefix = true;
+ break;
+ }
+
+ if (is_prefix)
+ {
+ raw_expected = buffer[bufpos - 1] == 'R';
+ bufpos = 0;
+ goto string;
+ }
+ }
+ phase4_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_name;
+ return;
+
+ case '.':
+ c = phase4_getc ();
+ phase4_ungetc (c);
+ switch (c)
+ {
+ default:
+ tp->type = token_type_symbol;
+ return;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c = '.';
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* The preprocessing number token is more "generous" than the C
+ number tokens. This is mostly due to token pasting (another
+ thing we can ignore here). */
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase4_getc ();
+ switch (c)
+ {
+ case 'e':
+ case 'E':
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase4_getc ();
+ if (c != '+' && c != '-')
+ {
+ phase4_ungetc (c);
+ break;
+ }
+ continue;
+
+ case 'A': case 'B': case 'C': case 'D': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.':
+ continue;
+
+ default:
+ phase4_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ tp->type = token_type_number;
+ tp->number = atol (buffer);
+ return;
+
+ case '\'':
+ /* We could worry about the 'L' before wide character constants,
+ but ignoring it has no effect unless one of the keywords is
+ "L". Just pretend it won't happen. Also, we don't need to
+ remember the character constant. */
+ last_was_backslash = false;
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (last_was_backslash)
+ {
+ last_was_backslash = false;
+ continue;
+ }
+ switch (c)
+ {
+ case '\\':
+ last_was_backslash = true;
+ /* FALLTHROUGH */
+ default:
+ continue;
+ case '\n':
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated character constant"),
+ logical_file_name, line_number - 1);
+ error_with_progname = true;
+ phase3_ungetc ('\n');
+ break;
+ case EOF: case '\'':
+ break;
+ }
+ break;
+ }
+ tp->type = token_type_character_constant;
+ return;
+
+ case '"':
+ {
+ string:
+ /* We could worry about the 'L' before wide string constants,
+ but since gettext's argument is not a wide character string,
+ let the compiler complain about the argument not matching the
+ prototype. Just pretend it won't happen. */
+ last_was_backslash = false;
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (last_was_backslash)
+ {
+ last_was_backslash = false;
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ continue;
+ }
+ switch (c)
+ {
+ case '\\':
+ last_was_backslash = true;
+ /* FALLTHROUGH */
+ default:
+ if (c == '\n' && !raw_expected)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: unterminated string literal"),
+ logical_file_name, line_number - 1);
+ error_with_progname = true;
+ phase3_ungetc ('\n');
+ break;
+ }
+ else
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ continue;
+ }
+
+ case EOF: case '"':
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+
+ if (raw_expected)
+ {
+ char *delimiter_left_end;
+ char *delimiter_right_start;
+
+ if (!(delimiter_left_end = strchr (buffer, '('))
+ || !(delimiter_right_start = strrchr (buffer, ')'))
+ || strncmp (buffer, delimiter_right_start + 1,
+ (delimiter_left_end - buffer)) != 0)
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated string literal"),
+ logical_file_name, line_number - 1);
+ error_with_progname = true;
+ }
+ else
+ {
+ *delimiter_right_start = '\0';
+ tp->type = token_type_string_literal;
+ tp->string = xstrdup (delimiter_left_end + 1);
+ tp->comment = add_reference (savable_comment);
+ return;
+ }
+ }
+ tp->type = token_type_string_literal;
+ tp->string = xstrdup (buffer);
+ tp->comment = add_reference (savable_comment);
+ return;
+ }
+
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '#':
+ tp->type = token_type_hash;
+ return;
+
+ case ':':
+ tp->type = token_type_colon;
+ return;
+
+ case '@':
+ if (objc_extensions)
+ {
+ tp->type = token_type_objc_special;
+ tp->comment = add_reference (savable_comment);
+ return;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = token_type_symbol;
+ return;
+ }
+}
+
+
+/* Supports only one pushback token. */
+static void
+phase5_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = *tp;
+ }
+}
+
+
+/* X. Recognize a leading # symbol. Leave leading hash as a hash, but
+ turn hash in the middle of a line into a plain symbol token. This
+ makes the phase 6 easier. */
+
+static void
+phaseX_get (token_ty *tp)
+{
+ static bool middle; /* false at the beginning of a line, true otherwise. */
+
+ phase5_get (tp);
+
+ if (tp->type == token_type_eoln || tp->type == token_type_eof)
+ middle = false;
+ else
+ {
+ if (middle)
+ {
+ /* Turn hash in the middle of a line into a plain symbol token. */
+ if (tp->type == token_type_hash)
+ tp->type = token_type_symbol;
+ }
+ else
+ {
+ /* When we see leading whitespace followed by a hash sign,
+ discard the leading white space token. The hash is all
+ phase 6 is interested in. */
+ if (tp->type == token_type_white_space)
+ {
+ token_ty next;
+
+ phase5_get (&next);
+ if (next.type == token_type_hash)
+ *tp = next;
+ else
+ phase5_unget (&next);
+ }
+ middle = true;
+ }
+ }
+}
+
+
+/* 6. Recognize and carry out directives (it also expands macros on
+ non-directive lines, which we do not do here). The only directive
+ we care about are the #line and #define directive. We throw all the
+ others away. */
+
+static token_ty phase6_pushback[2];
+static int phase6_pushback_length;
+
+
+static void
+phase6_get (token_ty *tp)
+{
+ static token_ty *buf;
+ static int bufmax;
+ int bufpos;
+ int j;
+
+ if (phase6_pushback_length)
+ {
+ *tp = phase6_pushback[--phase6_pushback_length];
+ return;
+ }
+ for (;;)
+ {
+ /* Get the next token. If it is not a '#' at the beginning of a
+ line (ignoring whitespace), return immediately. */
+ phaseX_get (tp);
+ if (tp->type != token_type_hash)
+ return;
+
+ /* Accumulate the rest of the directive in a buffer, until the
+ "define" keyword is seen or until end of line. */
+ bufpos = 0;
+ for (;;)
+ {
+ phaseX_get (tp);
+ if (tp->type == token_type_eoln || tp->type == token_type_eof)
+ break;
+
+ /* Before the "define" keyword and inside other directives
+ white space is irrelevant. So just throw it away. */
+ if (tp->type != token_type_white_space)
+ {
+ /* If it is a #define directive, return immediately,
+ thus treating the body of the #define directive like
+ normal input. */
+ if (bufpos == 0
+ && tp->type == token_type_name
+ && strcmp (tp->string, "define") == 0)
+ return;
+
+ /* Accumulate. */
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buf = xrealloc (buf, bufmax * sizeof (buf[0]));
+ }
+ buf[bufpos++] = *tp;
+ }
+ }
+
+ /* If it is a #line directive, with no macros to expand, act on
+ it. Ignore all other directives. */
+ if (bufpos >= 3 && buf[0].type == token_type_name
+ && strcmp (buf[0].string, "line") == 0
+ && buf[1].type == token_type_number
+ && buf[2].type == token_type_string_literal)
+ {
+ logical_file_name = xstrdup (buf[2].string);
+ line_number = buf[1].number;
+ }
+ if (bufpos >= 2 && buf[0].type == token_type_number
+ && buf[1].type == token_type_string_literal)
+ {
+ logical_file_name = xstrdup (buf[1].string);
+ line_number = buf[0].number;
+ }
+
+ /* Release the storage held by the directive. */
+ for (j = 0; j < bufpos; ++j)
+ free_token (&buf[j]);
+
+ /* We must reset the selected comments. */
+ savable_comment_reset ();
+ }
+}
+
+
+/* Supports 2 tokens of pushback. */
+static void
+phase6_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase6_pushback_length == SIZEOF (phase6_pushback))
+ abort ();
+ phase6_pushback[phase6_pushback_length++] = *tp;
+ }
+}
+
+
+/* 8a. Convert ISO C 99 section 7.8.1 format string directives to string
+ literal placeholders. */
+
+/* Test for an ISO C 99 section 7.8.1 format string directive. */
+static bool
+is_inttypes_macro (const char *name)
+{
+ /* Syntax:
+ P R I { d | i | o | u | x | X }
+ { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */
+ if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I')
+ {
+ name += 3;
+ if (name[0] == 'd' || name[0] == 'i' || name[0] == 'o' || name[0] == 'u'
+ || name[0] == 'x' || name[0] == 'X')
+ {
+ name += 1;
+ if (name[0] == 'M' && name[1] == 'A' && name[2] == 'X'
+ && name[3] == '\0')
+ return true;
+ if (name[0] == 'P' && name[1] == 'T' && name[2] == 'R'
+ && name[3] == '\0')
+ return true;
+ if (name[0] == 'L' && name[1] == 'E' && name[2] == 'A'
+ && name[3] == 'S' && name[4] == 'T')
+ name += 5;
+ else if (name[0] == 'F' && name[1] == 'A' && name[2] == 'S'
+ && name[3] == 'T')
+ name += 4;
+ if (name[0] == '8' && name[1] == '\0')
+ return true;
+ if (name[0] == '1' && name[1] == '6' && name[2] == '\0')
+ return true;
+ if (name[0] == '3' && name[1] == '2' && name[2] == '\0')
+ return true;
+ if (name[0] == '6' && name[1] == '4' && name[2] == '\0')
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+phase8a_get (token_ty *tp)
+{
+ phase6_get (tp);
+ if (tp->type == token_type_name && is_inttypes_macro (tp->string))
+ {
+ /* Turn PRIdXXX into "<PRIdXXX>". */
+ char *new_string = xasprintf ("<%s>", tp->string);
+ free (tp->string);
+ tp->string = new_string;
+ tp->comment = add_reference (savable_comment);
+ tp->type = token_type_string_literal;
+ }
+}
+
+/* Supports 2 tokens of pushback. */
+static inline void
+phase8a_unget (token_ty *tp)
+{
+ phase6_unget (tp);
+}
+
+
+/* 8b. Drop whitespace. */
+static void
+phase8b_get (token_ty *tp)
+{
+ for (;;)
+ {
+ phase8a_get (tp);
+
+ if (tp->type == token_type_white_space)
+ continue;
+ if (tp->type == token_type_eoln)
+ {
+ /* We have to track the last occurrence of a string. One
+ mode of xgettext allows to group an extracted message
+ with a comment for documentation. The rule which states
+ which comment is assumed to be grouped with the message
+ says it should immediately precede it. Our
+ interpretation: between the last line of the comment and
+ the line in which the keyword is found must be no line
+ with non-white space tokens. */
+ ++newline_count;
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ continue;
+ }
+ break;
+ }
+}
+
+/* Supports 2 tokens of pushback. */
+static inline void
+phase8b_unget (token_ty *tp)
+{
+ phase8a_unget (tp);
+}
+
+
+/* 8c. In ObjectiveC mode, drop '@' before a literal string. We need to
+ do this before performing concatenation of adjacent string literals. */
+static void
+phase8c_get (token_ty *tp)
+{
+ token_ty tmp;
+
+ phase8b_get (tp);
+ if (tp->type != token_type_objc_special)
+ return;
+ phase8b_get (&tmp);
+ if (tmp.type != token_type_string_literal)
+ {
+ phase8b_unget (&tmp);
+ return;
+ }
+ /* Drop the '@' token and return immediately the following string. */
+ drop_reference (tmp.comment);
+ tmp.comment = tp->comment;
+ *tp = tmp;
+}
+
+/* Supports only one pushback token. */
+static inline void
+phase8c_unget (token_ty *tp)
+{
+ phase8b_unget (tp);
+}
+
+
+/* 8. Concatenate adjacent string literals to form single string
+ literals (because we don't expand macros, there are a few things we
+ will miss). */
+
+static void
+phase8_get (token_ty *tp)
+{
+ phase8c_get (tp);
+ if (tp->type != token_type_string_literal)
+ return;
+ for (;;)
+ {
+ token_ty tmp;
+ size_t len;
+
+ phase8c_get (&tmp);
+ if (tmp.type != token_type_string_literal)
+ {
+ phase8c_unget (&tmp);
+ return;
+ }
+ len = strlen (tp->string);
+ tp->string = xrealloc (tp->string, len + strlen (tmp.string) + 1);
+ strcpy (tp->string + len, tmp.string);
+ free_token (&tmp);
+ }
+}
+
+
+/* ===================== Reading of high-level tokens. ==================== */
+
+
+enum xgettext_token_type_ty
+{
+ xgettext_token_type_eof,
+ xgettext_token_type_keyword,
+ xgettext_token_type_symbol,
+ xgettext_token_type_lparen,
+ xgettext_token_type_rparen,
+ xgettext_token_type_comma,
+ xgettext_token_type_colon,
+ xgettext_token_type_string_literal,
+ xgettext_token_type_other
+};
+typedef enum xgettext_token_type_ty xgettext_token_type_ty;
+
+typedef struct xgettext_token_ty xgettext_token_ty;
+struct xgettext_token_ty
+{
+ xgettext_token_type_ty type;
+
+ /* This field is used only for xgettext_token_type_keyword. */
+ const struct callshapes *shapes;
+
+ /* This field is used only for xgettext_token_type_string_literal,
+ xgettext_token_type_keyword, xgettext_token_type_symbol. */
+ char *string;
+
+ /* This field is used only for xgettext_token_type_string_literal. */
+ refcounted_string_list_ty *comment;
+
+ /* These fields are only for
+ xgettext_token_type_keyword,
+ xgettext_token_type_string_literal. */
+ lex_pos_ty pos;
+};
+
+
+/* 9. Convert the remaining preprocessing tokens to C tokens and
+ discards any white space from the translation unit. */
+
+static void
+x_c_lex (xgettext_token_ty *tp)
+{
+ for (;;)
+ {
+ token_ty token;
+ void *keyword_value;
+
+ phase8_get (&token);
+ switch (token.type)
+ {
+ case token_type_eof:
+ tp->type = xgettext_token_type_eof;
+ return;
+
+ case token_type_name:
+ last_non_comment_line = newline_count;
+
+ if (hash_find_entry (objc_extensions ? &objc_keywords : &c_keywords,
+ token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ tp->type = xgettext_token_type_keyword;
+ tp->shapes = (const struct callshapes *) keyword_value;
+ tp->pos.file_name = logical_file_name;
+ tp->pos.line_number = token.line_number;
+ }
+ else
+ tp->type = xgettext_token_type_symbol;
+ tp->string = token.string;
+ return;
+
+ case token_type_lparen:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_lparen;
+ return;
+
+ case token_type_rparen:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_rparen;
+ return;
+
+ case token_type_comma:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_comma;
+ return;
+
+ case token_type_colon:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_colon;
+ return;
+
+ case token_type_string_literal:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_string_literal;
+ tp->string = token.string;
+ tp->comment = token.comment;
+ tp->pos.file_name = logical_file_name;
+ tp->pos.line_number = token.line_number;
+ return;
+
+ case token_type_objc_special:
+ drop_reference (token.comment);
+ /* FALLTHROUGH */
+
+ default:
+ last_non_comment_line = newline_count;
+
+ tp->type = xgettext_token_type_other;
+ return;
+ }
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis.
+ Extracted messages are added to MLP.
+ Return true upon eof, false upon closing parenthesis. */
+static bool
+extract_parenthesized (message_list_ty *mlp,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Context iterator that will be used if the next token is a ':'.
+ (Objective C selector syntax.) */
+ flag_context_list_iterator_ty selectorcall_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ xgettext_token_ty token;
+
+ x_c_lex (&token);
+ switch (token.type)
+ {
+ case xgettext_token_type_keyword:
+ next_shapes = token.shapes;
+ state = 1;
+ goto keyword_or_symbol;
+
+ case xgettext_token_type_symbol:
+ state = 0;
+ keyword_or_symbol:
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ if (objc_extensions)
+ {
+ size_t token_string_len = strlen (token.string);
+ token.string = xrealloc (token.string, token_string_len + 2);
+ token.string[token_string_len] = ':';
+ token.string[token_string_len + 1] = '\0';
+ selectorcall_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, token_string_len + 1));
+ }
+ free (token.string);
+ continue;
+
+ case xgettext_token_type_lparen:
+ if (extract_parenthesized (mlp, inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ selectorcall_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case xgettext_token_type_rparen:
+ arglist_parser_done (argparser, arg);
+ return false;
+
+ case xgettext_token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ selectorcall_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case xgettext_token_type_colon:
+ if (objc_extensions)
+ {
+ context_iter = selectorcall_context_iter;
+ inner_context =
+ inherited_context (inner_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ selectorcall_context_iter = passthrough_context_list_iterator;
+ }
+ else
+ {
+ next_context_iter = null_context_list_iterator;
+ selectorcall_context_iter = null_context_list_iterator;
+ }
+ state = 0;
+ continue;
+
+ case xgettext_token_type_string_literal:
+ if (extract_all)
+ {
+ char *string;
+ refcounted_string_list_ty *comment;
+ const char *encoding;
+
+ string = literalstring_parse (token.string, &token.pos,
+ LET_ANSI_C | LET_UNICODE);
+ free (token.string);
+ token.string = string;
+
+ if (token.comment != NULL)
+ {
+ comment = savable_comment_convert_encoding (token.comment,
+ &token.pos);
+ drop_reference (token.comment);
+ token.comment = comment;
+ }
+
+ /* token.string and token.comment are already converted
+ to UTF-8. Prevent further conversion in
+ remember_a_message. */
+ encoding = xgettext_current_source_encoding;
+ xgettext_current_source_encoding = po_charset_utf8;
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &token.pos, NULL, token.comment);
+ xgettext_current_source_encoding = encoding;
+ }
+ else
+ arglist_parser_remember_literal (argparser, arg, token.string,
+ inner_context,
+ token.pos.file_name,
+ token.pos.line_number,
+ token.comment,
+ LET_ANSI_C | LET_UNICODE);
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ selectorcall_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case xgettext_token_type_other:
+ next_context_iter = null_context_list_iterator;
+ selectorcall_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case xgettext_token_type_eof:
+ arglist_parser_done (argparser, arg);
+ return true;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+static void
+extract_whole_file (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ newline_count = 0;
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
+
+
+void
+extract_c (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ objc_extensions = false;
+ extract_whole_file (f, real_filename, logical_filename, flag_table, mdlp);
+}
+
+void
+extract_objc (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ objc_extensions = true;
+ extract_whole_file (f, real_filename, logical_filename, flag_table, mdlp);
+}
diff --git a/gettext-tools/src/x-c.h b/gettext-tools/src/x-c.h
new file mode 100644
index 0000000..64e4953
--- /dev/null
+++ b/gettext-tools/src/x-c.h
@@ -0,0 +1,92 @@
+/* xgettext C/C++/ObjectiveC backend.
+ Copyright (C) 2001-2003, 2006, 2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_C \
+ { "c", "C" }, \
+ { "h", "C" }, \
+ { "C", "C++" }, \
+ { "c++", "C++" }, \
+ { "cc", "C++" }, \
+ { "cxx", "C++" }, \
+ { "cpp", "C++" }, \
+ { "hh", "C++" }, \
+ { "hxx", "C++" }, \
+ { "hpp", "C++" }, \
+ { "m", "ObjectiveC" }, \
+
+#define SCANNERS_C \
+ { "C", extract_c, \
+ &flag_table_c, \
+ &formatstring_c, NULL, \
+ &literalstring_c }, \
+ { "C++", extract_c, \
+ &flag_table_c, \
+ &formatstring_c, NULL, \
+ &literalstring_c }, \
+ { "ObjectiveC", extract_objc, \
+ &flag_table_objc, \
+ &formatstring_c, &formatstring_objc, \
+ &literalstring_c }, \
+ { "GCC-source", extract_c, \
+ &flag_table_gcc_internal, \
+ &formatstring_gcc_internal, &formatstring_gfc_internal, \
+ &literalstring_c }, \
+
+/* Scan a C/C++ file and add its translatable strings to mdlp. */
+extern void extract_c (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+/* Scan an ObjectiveC file and add its translatable strings to mdlp. */
+extern void extract_objc (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_c_extract_all (void);
+
+extern void x_c_keyword (const char *name);
+extern void x_objc_keyword (const char *name);
+
+extern void x_c_trigraphs (void);
+
+extern void init_flag_table_c (void);
+extern void init_flag_table_objc (void);
+extern void init_flag_table_gcc_internal (void);
+
+
+extern struct literalstring_parser literalstring_c;
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-csharp.c b/gettext-tools/src/x-csharp.c
new file mode 100644
index 0000000..5091c3b
--- /dev/null
+++ b/gettext-tools/src/x-csharp.c
@@ -0,0 +1,2147 @@
+/* xgettext C# backend.
+ Copyright (C) 2003, 2005-2009, 2011 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-csharp.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "c-ctype.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The C# syntax is defined in ECMA-334, second edition. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_csharp_extract_all ()
+{
+ extract_all = true;
+}
+
+
+/* Processes a --keyword option.
+ Non-ASCII function names can be used if given in UTF-8 encoding. */
+void
+x_csharp_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C#
+ identifier sequence with dots.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_csharp_keyword ("GetString"); /* Resource{Manager,Set}.GetString */
+ x_csharp_keyword ("GetPluralString:1,2"); /* GettextResource{Manager,Set}.GetPluralString */
+ x_csharp_keyword ("GetParticularString:1c,2"); /* Resource{Manager,Set}.GetParticularString */
+ x_csharp_keyword ("GetParticularPluralString:1c,2,3"); /* Resource{Manager,Set}.GetParticularPluralString */
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_csharp ()
+{
+ xgettext_record_flag ("GetString:1:pass-csharp-format");
+ xgettext_record_flag ("GetPluralString:1:pass-csharp-format");
+ xgettext_record_flag ("GetPluralString:2:pass-csharp-format");
+ xgettext_record_flag ("GetParticularString:2:pass-csharp-format");
+ xgettext_record_flag ("GetParticularPluralString:2:pass-csharp-format");
+ xgettext_record_flag ("GetParticularPluralString:3:pass-csharp-format");
+ xgettext_record_flag ("String.Format:1:csharp-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Phase 1: line_number handling. */
+
+/* Maximum used, roughly a safer MB_LEN_MAX. */
+#define MAX_PHASE1_PUSHBACK 16
+static unsigned char phase1_pushback[MAX_PHASE1_PUSHBACK];
+static int phase1_pushback_length;
+
+/* Read the next single byte from the input file. */
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ {
+ c = phase1_pushback[--phase1_pushback_length];
+ if (c == '\n')
+ ++line_number;
+ return c;
+ }
+
+ c = getc (fp);
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\n')
+ ++line_number;
+ return c;
+}
+
+/* Supports MAX_PHASE1_PUSHBACK characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* Phase 2: Conversion to Unicode.
+ This is done early because ECMA-334 section 9.1. says that the source is
+ "an ordered sequence of Unicode characters", and because the recognition
+ of the line terminators (ECMA-334 section 9.3.1) is hardly possible without
+ prior conversion to Unicode. */
+
+/* End-of-file indicator for functions returning an UCS-4 character. */
+#define UEOF -1
+
+/* Newline Unicode character. */
+#define UNL 0x000a
+
+static lexical_context_ty lexical_context;
+
+static int phase2_pushback[1];
+static int phase2_pushback_length;
+
+/* Read the next Unicode UCS-4 character from the input file. */
+static int
+phase2_getc ()
+{
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (xgettext_current_source_encoding == po_charset_ascii)
+ {
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ if (!c_isascii (c))
+ {
+ multiline_error (xstrdup (""),
+ xasprintf ("%s\n%s\n",
+ non_ascii_error_message (lexical_context,
+ real_file_name,
+ line_number),
+ _("\
+Please specify the source encoding through --from-code.")));
+ exit (EXIT_FAILURE);
+ }
+ return c;
+ }
+ else if (xgettext_current_source_encoding != po_charset_utf8)
+ {
+#if HAVE_ICONV
+ /* Use iconv on an increasing number of bytes. Read only as many bytes
+ through phase1_getc as needed. This is needed to give reasonable
+ interactive behaviour when fp is connected to an interactive tty. */
+ unsigned char buf[MAX_PHASE1_PUSHBACK];
+ size_t bufcount;
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = (unsigned char) c;
+ bufcount = 1;
+
+ for (;;)
+ {
+ unsigned char scratchbuf[6];
+ const char *inptr = (const char *) &buf[0];
+ size_t insize = bufcount;
+ char *outptr = (char *) &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+
+ size_t res = iconv (xgettext_current_source_iconv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ /* We expect that a character has been produced if and only if
+ some input bytes have been consumed. */
+ if ((insize < bufcount) != (outsize < sizeof (scratchbuf)))
+ abort ();
+ if (outsize == sizeof (scratchbuf))
+ {
+ /* No character has been produced. Must be an error. */
+ if (res != (size_t)(-1))
+ abort ();
+
+ if (errno == EILSEQ)
+ {
+ /* An invalid multibyte sequence was encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the correct source encoding through --from-code.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ else if (errno == EINVAL)
+ {
+ /* An incomplete multibyte character. */
+ int c;
+
+ if (bufcount == MAX_PHASE1_PUSHBACK)
+ {
+ /* An overlong incomplete multibyte sequence was
+ encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Long incomplete multibyte sequence.\n\
+Please specify the correct source encoding through --from-code.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+
+ /* Read one more byte and retry iconv. */
+ c = phase1_getc ();
+ if (c == EOF)
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of file.\n\
+Please specify the correct source encoding through --from-code.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ if (c == '\n')
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of line.\n\
+Please specify the correct source encoding through --from-code.\n"),
+ real_file_name, line_number - 1));
+ exit (EXIT_FAILURE);
+ }
+ buf[bufcount++] = (unsigned char) c;
+ }
+ else
+ error (EXIT_FAILURE, errno, _("%s:%d: iconv failure"),
+ real_file_name, line_number);
+ }
+ else
+ {
+ size_t outbytes = sizeof (scratchbuf) - outsize;
+ size_t bytes = bufcount - insize;
+ ucs4_t uc;
+
+ /* We expect that one character has been produced. */
+ if (bytes == 0)
+ abort ();
+ if (outbytes == 0)
+ abort ();
+ /* Push back the unused bytes. */
+ while (insize > 0)
+ phase1_ungetc (buf[--insize]);
+ /* Convert the character from UTF-8 to UCS-4. */
+ if (u8_mbtoucr (&uc, scratchbuf, outbytes) < (int) outbytes)
+ {
+ /* scratchbuf contains an out-of-range Unicode character
+ (> 0x10ffff). */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the source encoding through --from-code.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ return uc;
+ }
+ }
+#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. */
+ abort ();
+#endif
+ }
+ else
+ {
+ /* Read an UTF-8 encoded character. */
+ unsigned char buf[6];
+ unsigned int count;
+ int c;
+ ucs4_t uc;
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = c;
+ count = 1;
+
+ if (buf[0] >= 0xc0)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[1] = c;
+ count = 2;
+ }
+
+ if (buf[0] >= 0xe0
+ && ((buf[1] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[2] = c;
+ count = 3;
+ }
+
+ if (buf[0] >= 0xf0
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[3] = c;
+ count = 4;
+ }
+
+ if (buf[0] >= 0xf8
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[4] = c;
+ count = 5;
+ }
+
+ if (buf[0] >= 0xfc
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40)
+ && ((buf[4] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[5] = c;
+ count = 6;
+ }
+
+ u8_mbtouc (&uc, buf, count);
+ return uc;
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != UEOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* Phase 3: Convert all line terminators to LF.
+ See ECMA-334 section 9.3.1. */
+
+/* Line number defined in terms of phase3. */
+static int logical_line_number;
+
+static int phase3_pushback[9];
+static int phase3_pushback_length;
+
+/* Read the next Unicode UCS-4 character from the input file, mapping
+ all line terminators to U+000A, and dropping U+001A at the end of file. */
+static int
+phase3_getc ()
+{
+ int c;
+
+ if (phase3_pushback_length)
+ {
+ c = phase3_pushback[--phase3_pushback_length];
+ if (c == UNL)
+ ++logical_line_number;
+ return c;
+ }
+
+ c = phase2_getc ();
+
+ if (c == 0x000d)
+ {
+ int c1 = phase2_getc ();
+
+ if (c1 != UEOF && c1 != 0x000a)
+ phase2_ungetc (c1);
+
+ /* Seen line terminator CR or CR/LF. */
+ ++logical_line_number;
+ return UNL;
+ }
+
+ if (c == 0x0085 || c == 0x2028 || c == 0x2029)
+ {
+ /* Seen Unicode word processor newline. */
+ ++logical_line_number;
+ return UNL;
+ }
+
+ if (c == 0x001a)
+ {
+ int c1 = phase2_getc ();
+
+ if (c1 == UEOF)
+ /* Seen U+001A right before the end of file. */
+ return UEOF;
+
+ phase2_ungetc (c1);
+ }
+
+ if (c == UNL)
+ ++logical_line_number;
+ return c;
+}
+
+/* Supports 9 characters of pushback. */
+static void
+phase3_ungetc (int c)
+{
+ if (c != UEOF)
+ {
+ if (c == UNL)
+ --logical_line_number;
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = c;
+ }
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* A string buffer type that allows appending Unicode characters.
+ Returns the entire string in UTF-8 encoding. */
+
+struct 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;
+};
+
+/* Initialize a 'struct string_buffer' to empty. */
+static inline void
+init_string_buffer (struct string_buffer *bp)
+{
+ bp->utf8_buffer = NULL;
+ bp->utf8_buflen = 0;
+ bp->utf8_allocated = 0;
+}
+
+/* Auxiliary function: Ensure count more bytes are available in bp->utf8. */
+static inline void
+string_buffer_append_unicode_grow (struct 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
+string_buffer_append_unicode (struct string_buffer *bp, unsigned int 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 ();
+
+ string_buffer_append_unicode_grow (bp, count);
+ memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
+ bp->utf8_buflen += count;
+}
+
+/* Return the string buffer's contents. */
+static char *
+string_buffer_result (struct string_buffer *bp)
+{
+ /* NUL-terminate it. */
+ string_buffer_append_unicode_grow (bp, 1);
+ bp->utf8_buffer[bp->utf8_buflen] = '\0';
+ /* Return it. */
+ return bp->utf8_buffer;
+}
+
+/* Free the memory pointed to by a 'struct string_buffer'. */
+static inline void
+free_string_buffer (struct string_buffer *bp)
+{
+ free (bp->utf8_buffer);
+}
+
+
+/* ======================== Accumulating comments. ======================== */
+
+
+/* Accumulating a single comment line. */
+
+static struct string_buffer comment_buffer;
+
+static inline void
+comment_start ()
+{
+ lexical_context = lc_comment;
+ comment_buffer.utf8_buflen = 0;
+}
+
+static inline bool
+comment_at_start ()
+{
+ return (comment_buffer.utf8_buflen == 0);
+}
+
+static inline void
+comment_add (int c)
+{
+ string_buffer_append_unicode (&comment_buffer, c);
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ char *buffer = string_buffer_result (&comment_buffer);
+ size_t buflen = strlen (buffer);
+
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ lexical_context = lc_outside;
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* Phase 4: Replace each comment that is not inside a character constant or
+ string literal with a space or newline character.
+ See ECMA-334 section 9.3.2. */
+
+static int
+phase4_getc ()
+{
+ int c0;
+ int c;
+ bool last_was_star;
+
+ c0 = phase3_getc ();
+ if (c0 != '/')
+ return c0;
+ c = phase3_getc ();
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ return c0;
+
+ case '*':
+ /* C style comment. */
+ comment_start ();
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case UNL:
+ comment_line_end (1);
+ comment_start ();
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = logical_line_number;
+ return ' ';
+
+ case '/':
+ /* C++ style comment. */
+ last_comment_line = logical_line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == UNL || c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ phase3_ungetc (c); /* push back the newline, to decrement logical_line_number */
+ comment_line_end (0);
+ phase3_getc (); /* read the newline again */
+ return UNL;
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase4_ungetc (int c)
+{
+ phase3_ungetc (c);
+}
+
+
+/* ======================= Character classification. ====================== */
+
+
+/* Return true if a given character is white space.
+ See ECMA-334 section 9.3.3. */
+static bool
+is_whitespace (int c)
+{
+ /* Unicode character class Zs, as of Unicode 4.0. */
+ /* grep '^[^;]*;[^;]*;Zs;' UnicodeData-4.0.0.txt */
+ switch (c >> 8)
+ {
+ case 0x00:
+ return (c == 0x0020 || c == 0x00a0);
+ case 0x16:
+ return (c == 0x1680);
+ case 0x18:
+ return (c == 0x180e);
+ case 0x20:
+ return ((c >= 0x2000 && c <= 0x200b) || c == 0x202f || c == 0x205f);
+ case 0x30:
+ return (c == 0x3000);
+ default:
+ return false;
+ }
+}
+
+
+/* C# allows identifiers containing many Unicode characters. We recognize
+ them; to use an identifier with Unicode characters in a --keyword option,
+ it must be specified in UTF-8. */
+
+static inline int
+bitmap_lookup (const void *table, unsigned int uc)
+{
+ unsigned int index1 = uc >> 16;
+ if (index1 < ((const int *) table)[0])
+ {
+ int lookup1 = ((const int *) table)[1 + index1];
+ if (lookup1 >= 0)
+ {
+ unsigned int index2 = (uc >> 9) & 0x7f;
+ int lookup2 = ((const int *) table)[lookup1 + index2];
+ if (lookup2 >= 0)
+ {
+ unsigned int index3 = (uc >> 5) & 0xf;
+ unsigned int lookup3 = ((const int *) table)[lookup2 + index3];
+
+ return (lookup3 >> (uc & 0x1f)) & 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Unicode character classes Lu, Ll, Lt, Lm, Lo, Nl, as of Unicode 4.0,
+ plus the underscore. */
+static const
+struct
+ {
+ int header[1];
+ int level1[3];
+ int level2[3 << 7];
+ /*unsigned*/ int level3[34 << 4];
+ }
+table_identifier_start =
+{
+ { 3 },
+ { 4, 132, 260 },
+ {
+ 388, 404, 420, 436, 452, 468, 484, 500,
+ 516, 532, 548, 564, 580, -1, 596, 612,
+ 628, -1, -1, -1, -1, -1, -1, -1,
+ 644, -1, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 676, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 692,
+ 660, 660, 708, -1, -1, -1, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 724, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 740, 756, 772, 788,
+ 804, 820, 836, -1, 852, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 868, 884, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 660, 660, 660, 660, 660,
+ 660, 660, 660, 900, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 660, 916, -1, -1
+ },
+ {
+ 0x00000000, 0x00000000, 0x87FFFFFE, 0x07FFFFFE,
+ 0x00000000, 0x04200400, 0xFF7FFFFF, 0xFF7FFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x007FFFFF, 0xFFFF0000, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x0003FFC3, 0x0000401F,
+ 0x00000000, 0x00000000, 0x00000000, 0x04000000,
+ 0xFFFFD740, 0xFFFFFFFB, 0xFFFF7FFF, 0x0FBFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFC03, 0xFFFFFFFF, 0xFFFF7FFF, 0x033FFFFF,
+ 0x0000FFFF, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+ 0x000000FF, 0x00000000, 0xFFFF0000, 0x000707FF,
+ 0x00000000, 0x07FFFFFE, 0x000007FF, 0xFFFEC000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x002FFFFF, 0x9C00C060,
+ 0xFFFD0000, 0x0000FFFF, 0x0000E000, 0x00000000,
+ 0xFFFFFFFF, 0x0002003F, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFF0, 0x23FFFFFF, 0xFF010000, 0x00000003,
+ 0xFFF99FE0, 0x23C5FDFF, 0xB0000000, 0x00030003,
+ 0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+ 0xFFFBBFE0, 0x23EDFDFF, 0x00010000, 0x00000003,
+ 0xFFF99FE0, 0x23EDFDFF, 0xB0000000, 0x00020003,
+ 0xD63DC7E8, 0x03BFC718, 0x00000000, 0x00000000,
+ 0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+ 0xFFFDDFE0, 0x23EFFDFF, 0x40000000, 0x00000003,
+ 0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+ 0xFC7FFFE0, 0x2FFBFFFF, 0x0000007F, 0x00000000,
+ 0xFFFFFFFE, 0x000DFFFF, 0x0000007F, 0x00000000,
+ 0xFEF02596, 0x200DECAE, 0x3000005F, 0x00000000,
+ 0x00000001, 0x00000000, 0xFFFFFEFF, 0x000007FF,
+ 0x00000F00, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0x000006FB, 0x003F0000, 0x00000000,
+ 0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x01FFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x83FFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFF07, 0xFFFFFFFF, 0x03FFFFFF,
+ 0xFFFFFF7F, 0xFFFFFFFF, 0x3D7F3D7F, 0xFFFFFFFF,
+ 0xFFFF3D7F, 0x7F3D7FFF, 0xFF7F7F3D, 0xFFFF7FFF,
+ 0x7F3D7FFF, 0xFFFFFFFF, 0x07FFFF7F, 0x00000000,
+ 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x001FFFFF,
+ 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x007F9FFF,
+ 0x07FFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x0001C7FF,
+ 0x0003DFFF, 0x0003FFFF, 0x0003FFFF, 0x0001DFFF,
+ 0xFFFFFFFF, 0x000FFFFF, 0x10800000, 0x00000000,
+ 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00FFFFFF,
+ 0xFFFFFFFF, 0x000001FF, 0x00000000, 0x00000000,
+ 0x1FFFFFFF, 0x00000000, 0xFFFF0000, 0x001F3FFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+ 0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+ 0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x80020000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x3E2FFC84, 0xE3FBBD50, 0x000003E0, 0xFFFFFFFF,
+ 0x0000000F, 0x00000000, 0x00000000, 0x00000000,
+ 0x000000E0, 0x1F3E03FE, 0xFFFFFFFE, 0xFFFFFFFF,
+ 0xE07FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xF7FFFFFF,
+ 0xFFFFFFE0, 0xFFFE1FFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00007FFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x003FFFFF, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00001FFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFF3FFF, 0xFFFFFFFF, 0x000007FF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xA0F8007F, 0x5F7FFDFF, 0xFFFFFFDB, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0003FFFF, 0xFFF80000, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x3FFFFFFF, 0xFFFF0000, 0xFFFFFFFF,
+ 0xFFFCFFFF, 0xFFFFFFFF, 0x000000FF, 0x0FFF0000,
+ 0x00000000, 0x00000000, 0x00000000, 0xFFDF0000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FFFFFFF,
+ 0x00000000, 0x07FFFFFE, 0x07FFFFFE, 0xFFFFFFC0,
+ 0xFFFFFFFF, 0x7FFFFFFF, 0x1CFCFCFC, 0x00000000,
+ 0xFFFFEFFF, 0xB7FFFF7F, 0x3FFF3FFF, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x07FFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x7FFFFFFF, 0xFFFF0000, 0x000007FF, 0x00000000,
+ 0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFD3F, 0x91BFFFFF, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFDFFFFF, 0xFFFFFFFF,
+ 0xDFFFFFFF, 0xEBFFDE64, 0xFFFFFFEF, 0xFFFFFFFF,
+ 0xDFDFE7BF, 0x7BFFFFFF, 0xFFFDFC5F, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFF0F, 0xF7FFFFFD, 0xF7FFFFFF,
+ 0xFFDFFFFF, 0xFFDFFFFF, 0xFFFF7FFF, 0xFFFF7FFF,
+ 0xFFFFFDFF, 0xFFFFFDFF, 0x000003F7, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x007FFFFF, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+ }
+};
+
+/* Unicode character classes Lu, Ll, Lt, Lm, Lo, Nl, Nd, Pc, Mn, Mc, Cf,
+ as of Unicode 4.0. */
+static const
+struct
+ {
+ int header[1];
+ int level1[15];
+ int level2[4 << 7];
+ /*unsigned*/ int level3[36 << 4];
+ }
+table_identifier_part =
+{
+ { 15 },
+ {
+ 16, 144, 272, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 400
+ },
+ {
+ 528, 544, 560, 576, 592, 608, 624, 640,
+ 656, 672, 688, 704, 720, -1, 736, 752,
+ 768, -1, -1, -1, -1, -1, -1, -1,
+ 784, -1, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 816, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 832,
+ 800, 800, 848, -1, -1, -1, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 864, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 880, 896, 912, 928,
+ 944, 960, 976, -1, 992, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 1008, -1, 1024, 1040, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 1056, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 800, 1072, -1, -1,
+ 1088, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+ },
+ {
+ 0x00000000, 0x03FF0000, 0x87FFFFFE, 0x07FFFFFE,
+ 0x00000000, 0x04202400, 0xFF7FFFFF, 0xFF7FFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x007FFFFF, 0xFFFF0000, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x0003FFC3, 0x0000401F,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xE0FFFFFF, 0x0400FFFF,
+ 0xFFFFD740, 0xFFFFFFFB, 0xFFFF7FFF, 0x0FBFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFC7B, 0xFFFFFFFF, 0xFFFF7FFF, 0x033FFFFF,
+ 0x0000FFFF, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+ 0xFFFE00FF, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+ 0x003F000F, 0x07FFFFFE, 0x01FFFFFF, 0xFFFFC3FF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xBFEFFFFF, 0x9FFFFDFF,
+ 0xFFFF8000, 0xFFFFFFFF, 0x0000E7FF, 0x00000000,
+ 0xFFFFFFFF, 0x0003FFFF, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFE, 0xF3FFFFFF, 0xFF1F3FFF, 0x0000FFCF,
+ 0xFFF99FEE, 0xF3C5FDFF, 0xB080399F, 0x0003FFCF,
+ 0xFFF987EE, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+ 0xFFFBBFEE, 0xF3EDFDFF, 0x00013BBF, 0x0000FFCF,
+ 0xFFF99FEE, 0xF3EDFDFF, 0xB0C0398F, 0x0002FFC3,
+ 0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+ 0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+ 0xFFFDDFEC, 0xF3EFFDFF, 0x40603DDF, 0x0000FFC3,
+ 0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+ 0xFC7FFFEC, 0x2FFBFFFF, 0xFF5F847F, 0x000C0000,
+ 0xFFFFFFFE, 0x07FFFFFF, 0x03FF7FFF, 0x00000000,
+ 0xFEF02596, 0x3BFFECAE, 0x33FF3F5F, 0x00000000,
+ 0x03000001, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE07FF,
+ 0xFEFF0FDF, 0x1FFFFFFF, 0x00000040, 0x00000000,
+ 0xFFFFFFFF, 0x03C7F6FB, 0x03FF03FF, 0x00000000,
+ 0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x01FFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x83FFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFF07, 0xFFFFFFFF, 0x03FFFFFF,
+ 0xFFFFFF7F, 0xFFFFFFFF, 0x3D7F3D7F, 0xFFFFFFFF,
+ 0xFFFF3D7F, 0x7F3D7FFF, 0xFF7F7F3D, 0xFFFF7FFF,
+ 0x7F3D7FFF, 0xFFFFFFFF, 0x07FFFF7F, 0x0003FE00,
+ 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x001FFFFF,
+ 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x007F9FFF,
+ 0x07FFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x0001C7FF,
+ 0x001FDFFF, 0x001FFFFF, 0x000FFFFF, 0x000DDFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x308FFFFF, 0x000003FF,
+ 0x03FF3800, 0xFFFFFFFF, 0xFFFFFFFF, 0x00FFFFFF,
+ 0xFFFFFFFF, 0x000003FF, 0x00000000, 0x00000000,
+ 0x1FFFFFFF, 0x0FFF0FFF, 0xFFFFFFC0, 0x001F3FFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000FFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+ 0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+ 0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+ 0x0000F000, 0x80007C00, 0x00100001, 0x8002FC0F,
+ 0x00000000, 0x00000000, 0x1FFF0000, 0x000007E2,
+ 0x3E2FFC84, 0xE3FBBD50, 0x000003E0, 0xFFFFFFFF,
+ 0x0000000F, 0x00000000, 0x00000000, 0x00000000,
+ 0x000000E0, 0x1F3EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+ 0xE67FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFE0, 0xFFFE1FFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00007FFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x003FFFFF, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00001FFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFF3FFF, 0xFFFFFFFF, 0x000007FF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xE0F8007F, 0x5F7FFDFF, 0xFFFFFFDB, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x0003FFFF, 0xFFF80000, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0x3FFFFFFF, 0xFFFF0000, 0xFFFFFFFF,
+ 0xFFFCFFFF, 0xFFFFFFFF, 0x000000FF, 0x0FFF0000,
+ 0x0000FFFF, 0x0018000F, 0x0000E000, 0xFFDF0000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9FFFFFFF,
+ 0x03FF0000, 0x87FFFFFE, 0x07FFFFFE, 0xFFFFFFE0,
+ 0xFFFFFFFF, 0x7FFFFFFF, 0x1CFCFCFC, 0x0E000000,
+ 0xFFFFEFFF, 0xB7FFFF7F, 0x3FFF3FFF, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x07FFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x7FFFFFFF, 0xFFFF0000, 0x000007FF, 0x00000000,
+ 0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x3FFFFFFF, 0x000003FF, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFD3F, 0x91BFFFFF, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0xFFFFE3E0,
+ 0x00000FE7, 0x00003C00, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFDFFFFF, 0xFFFFFFFF,
+ 0xDFFFFFFF, 0xEBFFDE64, 0xFFFFFFEF, 0xFFFFFFFF,
+ 0xDFDFE7BF, 0x7BFFFFFF, 0xFFFDFC5F, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFF0F, 0xF7FFFFFD, 0xF7FFFFFF,
+ 0xFFDFFFFF, 0xFFDFFFFF, 0xFFFF7FFF, 0xFFFF7FFF,
+ 0xFFFFFDFF, 0xFFFFFDFF, 0xFFFFC3F7, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x007FFFFF, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x3FFFFFFF, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000002, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF
+ }
+};
+
+/* Return true if a given character can occur as first character of an
+ identifier. See ECMA-334 section 9.4.2. */
+static bool
+is_identifier_start (int c)
+{
+ return bitmap_lookup (&table_identifier_start, c);
+ /* In ASCII only this would be:
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_');
+ */
+}
+
+/* Return true if a given character can occur as character of an identifier.
+ See ECMA-334 section 9.4.2. */
+static bool
+is_identifier_part (int c)
+{
+ return bitmap_lookup (&table_identifier_part, c);
+ /* In ASCII only this would be:
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9') || c == '_');
+ */
+}
+
+static bool
+is_any_character (int c)
+{
+ return true;
+}
+
+
+/* ======================= Preprocessor directives. ======================= */
+
+
+/* Phase 5: Remove preprocessor lines. See ECMA-334 section 9.5.
+ As a side effect, this also removes initial whitespace on every line;
+ this whitespace doesn't matter. */
+
+static int phase5_pushback[10];
+static int phase5_pushback_length;
+
+static int
+phase5_getc ()
+{
+ int c;
+
+ if (phase5_pushback_length)
+ return phase5_pushback[--phase5_pushback_length];
+
+ c = phase4_getc ();
+ if (c != UNL)
+ return c;
+
+ do
+ c = phase3_getc ();
+ while (c != UEOF && is_whitespace (c));
+
+ if (c == '#')
+ {
+ /* Ignore the entire line containing the preprocessor directive
+ (including the // comment if it contains one). */
+ do
+ c = phase3_getc ();
+ while (c != UEOF && c != UNL);
+ return c;
+ }
+ else
+ {
+ phase3_ungetc (c);
+ return UNL;
+ }
+}
+
+#ifdef unused
+static void
+phase5_ungetc (int c)
+{
+ if (c != UEOF)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = c;
+ }
+}
+#endif
+
+
+/* ========================== Reading of tokens. ========================== */
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_lbrace, /* { */
+ token_type_rbrace, /* } */
+ token_type_comma, /* , */
+ token_type_dot, /* . */
+ token_type_string_literal, /* "abc", @"abc" */
+ token_type_number, /* 1.23 */
+ token_type_symbol, /* identifier, keyword, null */
+ token_type_plus, /* + */
+ token_type_other /* character literal, misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ int line_number;
+ int logical_line_number;
+};
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string_literal)
+ drop_reference (tp->comment);
+}
+
+
+/* Read a Unicode escape sequence outside string/character literals.
+ Reject Unicode escapes that don't fulfill the given predicate.
+ See ECMA-334 section 9.4.2. */
+static int
+do_getc_unicode_escaped (bool (*predicate) (int))
+{
+ int c;
+
+ /* Use phase 3, because phase 4 elides comments. */
+ c = phase3_getc ();
+ if (c == UEOF)
+ return '\\';
+ if (c == 'u' || c == 'U')
+ {
+ unsigned char buf[8];
+ int expect;
+ unsigned int n;
+ int i;
+
+ expect = (c == 'U' ? 8 : 4);
+ n = 0;
+ for (i = 0; i < expect; i++)
+ {
+ int c1 = phase3_getc ();
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ {
+ phase3_ungetc (c1);
+ while (--i >= 0)
+ phase3_ungetc (buf[i]);
+ phase3_ungetc (c);
+ return '\\';
+ }
+
+ buf[i] = c1;
+ }
+
+ if (n >= 0x110000)
+ {
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: invalid Unicode character"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ }
+ else if (predicate (n))
+ return n;
+
+ while (--i >= 0)
+ phase3_ungetc (buf[i]);
+ }
+ phase3_ungetc (c);
+ return '\\';
+}
+
+
+/* Read an escape sequence inside a string literal or character literal.
+ See ECMA-334 sections 9.4.4.4., 9.4.4.5. */
+static int
+do_getc_escaped ()
+{
+ int c;
+ int n;
+ int i;
+
+ /* Use phase 3, because phase 4 elides comments. */
+ c = phase3_getc ();
+ if (c == UEOF)
+ return '\\';
+ switch (c)
+ {
+ case 'a':
+ return 0x0007;
+ case 'b':
+ return 0x0008;
+ case 't':
+ return 0x0009;
+ case 'n':
+ return 0x000a;
+ case 'v':
+ return 0x000b;
+ case 'f':
+ return 0x000c;
+ case 'r':
+ return 0x000d;
+ case '"':
+ return '"';
+ case '\'':
+ return '\'';
+ case '\\':
+ return '\\';
+ case '0':
+ return 0x0000;
+ case 'x':
+ c = phase3_getc ();
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ phase3_ungetc ('x');
+ return '\\';
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ break;
+ }
+ n = 0;
+ for (i = 0;; i++)
+ {
+ switch (c)
+ {
+ default:
+ phase3_ungetc (c);
+ return n;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = n * 16 + c - '0';
+ break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ n = n * 16 + 10 + c - 'A';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ n = n * 16 + 10 + c - 'a';
+ break;
+ }
+ if (i == 3)
+ break;
+ c = phase3_getc ();
+ }
+ return n;
+ case 'u': case 'U':
+ phase3_ungetc (c);
+ return do_getc_unicode_escaped (is_any_character);
+ default:
+ /* Invalid escape sequence. */
+ phase3_ungetc (c);
+ return '\\';
+ }
+}
+
+/* Read a regular string literal or character literal.
+ See ECMA-334 sections 9.4.4.4., 9.4.4.5. */
+static void
+accumulate_escaped (struct mixed_string_buffer *literal, int delimiter)
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 3, because phase 4 elides comments. */
+ c = phase3_getc ();
+ if (c == UEOF || c == delimiter)
+ break;
+ if (c == UNL)
+ {
+ phase3_ungetc (c);
+ error_with_progname = false;
+ if (delimiter == '\'')
+ error (0, 0, _("%s:%d: warning: unterminated character constant"),
+ logical_file_name, line_number);
+ else
+ error (0, 0, _("%s:%d: warning: unterminated string constant"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ break;
+ }
+ if (c == '\\')
+ c = do_getc_escaped ();
+ if (literal)
+ mixed_string_buffer_append_unicode (literal, c);
+ }
+}
+
+
+/* Combine characters into tokens. Discard whitespace. */
+
+/* Maximum used guaranteed to be < 4. */
+static token_ty phase6_pushback[4];
+static int phase6_pushback_length;
+
+static void
+phase6_get (token_ty *tp)
+{
+ int c;
+
+ if (phase6_pushback_length)
+ {
+ *tp = phase6_pushback[--phase6_pushback_length];
+ return;
+ }
+ tp->string = NULL;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ tp->logical_line_number = logical_line_number;
+ c = phase5_getc ();
+
+ if (c == UEOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+
+ switch (c)
+ {
+ case UNL:
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\t':
+ case '\f':
+ /* Ignore whitespace and comments. */
+ continue;
+ }
+
+ last_non_comment_line = tp->logical_line_number;
+
+ switch (c)
+ {
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case '{':
+ tp->type = token_type_lbrace;
+ return;
+
+ case '}':
+ tp->type = token_type_rbrace;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '.':
+ c = phase4_getc ();
+ if (!(c >= '0' && c <= '9'))
+ {
+ phase4_ungetc (c);
+ tp->type = token_type_dot;
+ return;
+ }
+ /* FALLTHROUGH */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ /* Don't need to verify the complicated syntax of integers and
+ floating-point numbers. We assume a valid C# input.
+ The simplified syntax that we recognize as number is: any
+ sequence of alphanumeric characters, additionally '+' and '-'
+ immediately after 'e' or 'E' except in hexadecimal numbers. */
+ bool hexadecimal = false;
+
+ for (;;)
+ {
+ c = phase4_getc ();
+ if (c >= '0' && c <= '9')
+ continue;
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' &&c <= 'z'))
+ {
+ if (c == 'X' || c == 'x')
+ hexadecimal = true;
+ if ((c == 'E' || c == 'e') && !hexadecimal)
+ {
+ c = phase4_getc ();
+ if (!(c == '+' || c == '-'))
+ phase4_ungetc (c);
+ }
+ continue;
+ }
+ if (c == '.')
+ continue;
+ break;
+ }
+ phase4_ungetc (c);
+ tp->type = token_type_number;
+ return;
+ }
+
+ case '"':
+ /* Regular string literal. */
+ {
+ struct mixed_string_buffer *literal;
+
+ lexical_context = lc_string;
+ literal = mixed_string_buffer_alloc (lexical_context,
+ logical_file_name,
+ logical_line_number);
+ accumulate_escaped (literal, '"');
+ tp->string = mixed_string_buffer_done (literal);
+ tp->comment = add_reference (savable_comment);
+ lexical_context = lc_outside;
+ tp->type = token_type_string_literal;
+ return;
+ }
+
+ case '\'':
+ /* Character literal. */
+ {
+ accumulate_escaped (NULL, '\'');
+ tp->type = token_type_other;
+ return;
+ }
+
+ case '+':
+ c = phase4_getc ();
+ if (c == '+')
+ /* Operator ++ */
+ tp->type = token_type_other;
+ else if (c == '=')
+ /* Operator += */
+ tp->type = token_type_other;
+ else
+ {
+ /* Operator + */
+ phase4_ungetc (c);
+ tp->type = token_type_plus;
+ }
+ return;
+
+ case '@':
+ c = phase4_getc ();
+ if (c == '"')
+ {
+ /* Verbatim string literal. */
+ struct string_buffer literal;
+
+ lexical_context = lc_string;
+ init_string_buffer (&literal);
+ for (;;)
+ {
+ /* Use phase 2, because phase 4 elides comments and phase 3
+ mixes up the newline characters. */
+ c = phase2_getc ();
+ if (c == UEOF)
+ break;
+ if (c == '"')
+ {
+ c = phase2_getc ();
+ if (c != '"')
+ {
+ phase2_ungetc (c);
+ break;
+ }
+ }
+ /* No special treatment of newline and backslash here. */
+ string_buffer_append_unicode (&literal, c);
+ }
+ tp->string = xstrdup (string_buffer_result (&literal));
+ free_string_buffer (&literal);
+ tp->comment = add_reference (savable_comment);
+ lexical_context = lc_outside;
+ tp->type = token_type_string_literal;
+ return;
+ }
+ /* FALLTHROUGH, so that @identifier is recognized. */
+
+ default:
+ if (c == '\\')
+ c = do_getc_unicode_escaped (is_identifier_start);
+ if (is_identifier_start (c))
+ {
+ static struct string_buffer buffer;
+ buffer.utf8_buflen = 0;
+ for (;;)
+ {
+ string_buffer_append_unicode (&buffer, c);
+ c = phase4_getc ();
+ if (c == '\\')
+ c = do_getc_unicode_escaped (is_identifier_part);
+ if (!is_identifier_part (c))
+ break;
+ }
+ phase4_ungetc (c);
+ tp->string = xstrdup (string_buffer_result (&buffer));
+ tp->type = token_type_symbol;
+ return;
+ }
+ else
+ {
+ /* Misc. operator. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+ }
+}
+
+/* Supports 3 tokens of pushback. */
+static void
+phase6_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase6_pushback_length == SIZEOF (phase6_pushback))
+ abort ();
+ phase6_pushback[phase6_pushback_length++] = *tp;
+ }
+}
+
+
+/* Compile-time optimization of string literal concatenation.
+ Combine "string1" + ... + "stringN" to the concatenated string if
+ - the token after this expression is not '.' (because then the last
+ string could be part of a method call expression). */
+
+static token_ty phase7_pushback[2];
+static int phase7_pushback_length;
+
+static void
+phase7_get (token_ty *tp)
+{
+ if (phase7_pushback_length)
+ {
+ *tp = phase7_pushback[--phase7_pushback_length];
+ return;
+ }
+
+ phase6_get (tp);
+ if (tp->type == token_type_string_literal)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase6_get (&token2);
+ if (token2.type == token_type_plus)
+ {
+ token_ty token3;
+
+ phase6_get (&token3);
+ if (token3.type == token_type_string_literal)
+ {
+ token_ty token_after;
+
+ phase6_get (&token_after);
+ if (token_after.type != token_type_dot)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ phase6_unget (&token_after);
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase6_unget (&token_after);
+ }
+ phase6_unget (&token3);
+ }
+ phase6_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+}
+
+/* Supports 2 tokens of pushback. */
+static void
+phase7_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase7_pushback_length == SIZEOF (phase7_pushback))
+ abort ();
+ phase7_pushback[phase7_pushback_length++] = *tp;
+ }
+}
+
+
+static void
+x_csharp_lex (token_ty *tp)
+{
+ phase7_get (tp);
+}
+
+/* Supports 2 tokens of pushback. */
+static void
+x_csharp_unlex (token_ty *tp)
+{
+ phase7_unget (tp);
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis or brace,
+ depending on TERMINATOR.
+ Extracted messages are added to MLP.
+ Return true upon eof, false upon closing parenthesis or brace. */
+static bool
+extract_parenthesized (message_list_ty *mlp, token_type_ty terminator,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_csharp_lex (&token);
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ /* Combine symbol1 . ... . symbolN to a single strings, so that
+ we can recognize static function calls like
+ GettextResource.gettext. The information present for
+ symbolI.....symbolN has precedence over the information for
+ symbolJ.....symbolN with J > I. */
+ char *sum = token.string;
+ size_t sum_len = strlen (sum);
+ const char *dottedname;
+ flag_context_list_ty *context_list;
+
+ for (;;)
+ {
+ token_ty token2;
+
+ x_csharp_lex (&token2);
+ if (token2.type == token_type_dot)
+ {
+ token_ty token3;
+
+ x_csharp_lex (&token3);
+ if (token3.type == token_type_symbol)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum =
+ (char *) xrealloc (sum, sum_len + 1 + addend_len + 1);
+ sum[sum_len] = '.';
+ memcpy (sum + sum_len + 1, addend, addend_len + 1);
+ sum_len += 1 + addend_len;
+
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ x_csharp_unlex (&token3);
+ }
+ x_csharp_unlex (&token2);
+ break;
+ }
+
+ for (dottedname = sum;;)
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, dottedname, strlen (dottedname),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ break;
+ }
+
+ dottedname = strchr (dottedname, '.');
+ if (dottedname == NULL)
+ {
+ state = 0;
+ break;
+ }
+ dottedname++;
+ }
+
+ for (dottedname = sum;;)
+ {
+ context_list =
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ dottedname, strlen (dottedname));
+ if (context_list != NULL)
+ break;
+
+ dottedname = strchr (dottedname, '.');
+ if (dottedname == NULL)
+ break;
+ dottedname++;
+ }
+ next_context_iter = flag_context_list_iterator (context_list);
+
+ free (sum);
+ continue;
+ }
+
+ case token_type_lparen:
+ if (extract_parenthesized (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ if (terminator == token_type_rparen)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return false;
+ }
+ if (terminator == token_type_rbrace)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: ')' found where '}' was expected"),
+ logical_file_name, token.line_number);
+ error_with_progname = true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbrace:
+ if (extract_parenthesized (mlp, token_type_rbrace,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rbrace:
+ if (terminator == token_type_rbrace)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return false;
+ }
+ if (terminator == token_type_rparen)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: '}' found where ')' was expected"),
+ logical_file_name, token.line_number);
+ error_with_progname = true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string_literal:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ token.comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+
+ case token_type_dot:
+ case token_type_number:
+ case token_type_plus:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_csharp (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ lexical_context = lc_outside;
+
+ logical_line_number = 1;
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_parenthesized (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-csharp.h b/gettext-tools/src/x-csharp.h
new file mode 100644
index 0000000..de37789
--- /dev/null
+++ b/gettext-tools/src/x-csharp.h
@@ -0,0 +1,50 @@
+/* xgettext C# backend.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_CSHARP \
+ { "cs", "C#" }, \
+
+#define SCANNERS_CSHARP \
+ { "C#", extract_csharp, \
+ &flag_table_csharp, &formatstring_csharp, NULL, NULL }, \
+
+extern void extract_csharp (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_csharp_keyword (const char *keyword);
+extern void x_csharp_extract_all (void);
+
+extern void init_flag_table_csharp (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-desktop.c b/gettext-tools/src/x-desktop.c
new file mode 100644
index 0000000..320266e
--- /dev/null
+++ b/gettext-tools/src/x-desktop.c
@@ -0,0 +1,193 @@
+/* xgettext Desktop Entry backend.
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ This file was written by Daiki Ueno <ueno@gnu.org>, 2014.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-desktop.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "hash.h"
+#include "gettext.h"
+#include "read-desktop.h"
+#include "po-charset.h"
+#include "c-ctype.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* ====================== Keyword set customization. ====================== */
+
+/* The syntax of a Desktop Entry file is defined at
+ http://standards.freedesktop.org/desktop-entry-spec/latest/index.html
+
+ Basically, values with 'localestring' type can be translated.
+
+ The type of a value is determined by looking at the key associated
+ with it. The list of available keys are listed on:
+ http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html */
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+static void
+add_keyword (const char *name, hash_table *keywords, bool is_list)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ if (keywords->table == NULL)
+ hash_init (keywords, 100);
+
+ desktop_add_keyword (keywords, name, is_list);
+ }
+}
+
+void
+x_desktop_keyword (const char *name)
+{
+ add_keyword (name, &keywords, false);
+}
+
+static void
+init_keywords (void)
+{
+ if (default_keywords)
+ {
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ desktop_add_default_keywords (&keywords);
+ default_keywords = false;
+ }
+}
+
+typedef struct extract_desktop_reader_ty extract_desktop_reader_ty;
+struct extract_desktop_reader_ty
+{
+ DESKTOP_READER_TY
+
+ message_list_ty *mlp;
+};
+
+static void
+extract_desktop_handle_group (struct desktop_reader_ty *reader,
+ const char *group)
+{
+ savable_comment_reset ();
+}
+
+static void
+extract_desktop_handle_pair (struct desktop_reader_ty *reader,
+ lex_pos_ty *key_pos,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ extract_desktop_reader_ty *extract_reader =
+ (extract_desktop_reader_ty *) reader;
+ void *keyword_value;
+
+ if (!locale /* Skip already translated entry. */
+ && hash_find_entry (&keywords, key, strlen (key), &keyword_value) == 0)
+ {
+ bool is_list = (bool) keyword_value;
+
+ remember_a_message (extract_reader->mlp, NULL,
+ desktop_unescape_string (value, is_list),
+ null_context, key_pos,
+ NULL, savable_comment);
+ }
+ savable_comment_reset ();
+}
+
+static void
+extract_desktop_handle_comment (struct desktop_reader_ty *reader,
+ const char *buffer)
+{
+ size_t buflen = strlen (buffer);
+ size_t bufpos = 0;
+
+ while (bufpos < buflen
+ && c_isspace (buffer[bufpos]))
+ ++bufpos;
+ while (buflen >= bufpos
+ && c_isspace (buffer[buflen - 1]))
+ --buflen;
+ if (bufpos < buflen)
+ {
+ char *comment = xstrdup (buffer);
+ comment[buflen] = 0;
+ savable_comment_add (&comment[bufpos]);
+ free (comment);
+ }
+}
+
+static void
+extract_desktop_handle_blank (struct desktop_reader_ty *reader,
+ const char *s)
+{
+ savable_comment_reset ();
+}
+
+desktop_reader_class_ty extract_methods =
+ {
+ sizeof (extract_desktop_reader_ty),
+ NULL,
+ NULL,
+ extract_desktop_handle_group,
+ extract_desktop_handle_pair,
+ extract_desktop_handle_comment,
+ extract_desktop_handle_blank
+ };
+
+void
+extract_desktop (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ desktop_reader_ty *reader = desktop_reader_alloc (&extract_methods);
+ extract_desktop_reader_ty *extract_reader =
+ (extract_desktop_reader_ty *) reader;
+
+ init_keywords ();
+ xgettext_current_source_encoding = po_charset_utf8;
+
+ extract_reader->mlp = mdlp->item[0]->messages;
+
+ desktop_parse (reader, f, real_filename, logical_filename);
+ desktop_reader_free (reader);
+
+ reader = NULL;
+}
diff --git a/gettext-tools/src/x-desktop.h b/gettext-tools/src/x-desktop.h
new file mode 100644
index 0000000..d24f174
--- /dev/null
+++ b/gettext-tools/src/x-desktop.h
@@ -0,0 +1,47 @@
+/* xgettext Desktop Entry backend.
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Daiki Ueno <ueno@gnu.org>, 2014.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_DESKTOP \
+ { "desktop", "Desktop" }, \
+
+#define SCANNERS_DESKTOP \
+ { "Desktop", extract_desktop, NULL, NULL, NULL, NULL }, \
+
+/* Scan a Desktop Entry file and add its translatable strings to mdlp. */
+extern void extract_desktop (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_desktop_keyword (const char *keyword);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-elisp.c b/gettext-tools/src/x-elisp.c
new file mode 100644
index 0000000..50c3d55
--- /dev/null
+++ b/gettext-tools/src/x-elisp.c
@@ -0,0 +1,1255 @@
+/* xgettext Emacs Lisp backend.
+ Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2001-2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-elisp.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "c-ctype.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+
+/* Summary of Emacs Lisp syntax:
+ - ';' starts a comment until end of line.
+ - '#@nn' starts a comment of nn bytes.
+ - Integers are constituted of an optional prefix (#b, #B for binary,
+ #o, #O for octal, #x, #X for hexadecimal, #nnr, #nnR for any radix),
+ an optional sign (+ or -), the digits, and an optional trailing dot.
+ - Characters are written as '?' followed by the character, possibly
+ with an escape sequence, for examples '?a', '?\n', '?\177'.
+ - Strings are delimited by double quotes. Backslash introduces an escape
+ sequence. The following are understood: '\n', '\r', '\f', '\t', '\a',
+ '\\', '\^C', '\012' (octal), '\x12' (hexadecimal).
+ - Symbols: can contain meta-characters if preceded by backslash.
+ - Uninterned symbols: written as #:SYMBOL.
+ - () delimit lists.
+ - [] delimit vectors.
+ The reader is implemented in emacs-21.1/src/lread.c. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_elisp_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_elisp_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Lisp
+ symbol. */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_elisp_keyword ("_");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_elisp ()
+{
+ xgettext_record_flag ("_:1:pass-elisp-format");
+ xgettext_record_flag ("format:1:elisp-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* A token consists of a sequence of characters. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ char *chars; /* the token's constituents */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (char *) xrealloc (tp->chars, tp->allocated * sizeof (char));
+ }
+}
+
+/* Test whether a token has integer syntax. */
+static inline bool
+is_integer (const char *p)
+{
+ /* NB: Yes, '+.' and '-.' both designate the integer 0. */
+ const char *p_start = p;
+
+ if (*p == '+' || *p == '-')
+ p++;
+ if (*p == '\0')
+ return false;
+ while (*p >= '0' && *p <= '9')
+ p++;
+ if (p > p_start && *p == '.')
+ p++;
+ return (*p == '\0');
+}
+
+/* Test whether a token has float syntax. */
+static inline bool
+is_float (const char *p)
+{
+ enum { LEAD_INT = 1, DOT_CHAR = 2, TRAIL_INT = 4, E_CHAR = 8, EXP_INT = 16 };
+ int state;
+
+ state = 0;
+ if (*p == '+' || *p == '-')
+ p++;
+ if (*p >= '0' && *p <= '9')
+ {
+ state |= LEAD_INT;
+ do
+ p++;
+ while (*p >= '0' && *p <= '9');
+ }
+ if (*p == '.')
+ {
+ state |= DOT_CHAR;
+ p++;
+ }
+ if (*p >= '0' && *p <= '9')
+ {
+ state |= TRAIL_INT;
+ do
+ p++;
+ while (*p >= '0' && *p <= '9');
+ }
+ if (*p == 'e' || *p == 'E')
+ {
+ state |= E_CHAR;
+ p++;
+ if (*p == '+' || *p == '-')
+ p++;
+ if (*p >= '0' && *p <= '9')
+ {
+ state |= EXP_INT;
+ do
+ p++;
+ while (*p >= '0' && *p <= '9');
+ }
+ else if (p[-1] == '+'
+ && ((p[0] == 'I' && p[1] == 'N' && p[2] == 'F')
+ || (p[0] == 'N' && p[1] == 'a' && p[2] == 'N')))
+ {
+ state |= EXP_INT;
+ p += 3;
+ }
+ }
+ return (*p == '\0')
+ && (state == (LEAD_INT | DOT_CHAR | TRAIL_INT)
+ || state == (DOT_CHAR | TRAIL_INT)
+ || state == (LEAD_INT | E_CHAR | EXP_INT)
+ || state == (LEAD_INT | DOT_CHAR | TRAIL_INT | E_CHAR | EXP_INT)
+ || state == (DOT_CHAR | TRAIL_INT | E_CHAR | EXP_INT));
+}
+
+/* Read the next token. 'first' is the first character, which has already
+ been read. Returns true for a symbol, false for a number. */
+static bool
+read_token (struct token *tp, int first)
+{
+ int c;
+ bool quoted = false;
+
+ init_token (tp);
+
+ c = first;
+
+ for (;; c = do_getc ())
+ {
+ if (c == EOF)
+ break;
+ if (c <= ' ') /* FIXME: Assumes ASCII compatible encoding */
+ break;
+ if (c == '\"' || c == '\'' || c == ';' || c == '(' || c == ')'
+ || c == '[' || c == ']' || c == '#')
+ break;
+ if (c == '\\')
+ {
+ quoted = true;
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid, but be tolerant. */
+ break;
+ }
+ grow_token (tp);
+ tp->chars[tp->charcount++] = c;
+ }
+ if (c != EOF)
+ do_ungetc (c);
+
+ if (quoted)
+ return true; /* symbol */
+
+ /* Add a NUL byte at the end, for is_integer and is_float. */
+ grow_token (tp);
+ tp->chars[tp->charcount] = '\0';
+
+ if (is_integer (tp->chars) || is_float (tp->chars))
+ return false; /* number */
+ else
+ return true; /* symbol */
+}
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ============== Reading of objects. See CLHS 2 "Syntax". ============== */
+
+
+/* We are only interested in symbols (e.g. GETTEXT or NGETTEXT) and strings.
+ Other objects need not to be represented precisely. */
+enum object_type
+{
+ t_symbol, /* symbol */
+ t_string, /* string */
+ t_other, /* other kind of real object */
+ t_dot, /* '.' pseudo object */
+ t_listclose, /* ')' pseudo object */
+ t_vectorclose,/* ']' pseudo object */
+ t_eof /* EOF marker */
+};
+
+struct object
+{
+ enum object_type type;
+ struct token *token; /* for t_symbol and t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct object'. */
+static inline void
+free_object (struct object *op)
+{
+ if (op->type == t_symbol || op->type == t_string)
+ {
+ free_token (op->token);
+ free (op->token);
+ }
+}
+
+/* Convert a t_symbol/t_string token to a char*. */
+static char *
+string_of_object (const struct object *op)
+{
+ char *str;
+ int n;
+
+ if (!(op->type == t_symbol || op->type == t_string))
+ abort ();
+ n = op->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, op->token->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+/* Returns the character represented by an escape sequence. */
+#define IGNORABLE_ESCAPE (EOF - 1)
+static int
+do_getc_escaped (int c, bool in_string)
+{
+ switch (c)
+ {
+ case 'a':
+ return '\a';
+ case 'b':
+ return '\b';
+ case 'd':
+ return 0x7F;
+ case 'e':
+ return 0x1B;
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+
+ case '\n':
+ return IGNORABLE_ESCAPE;
+
+ case ' ':
+ return (in_string ? IGNORABLE_ESCAPE : ' ');
+
+ case 'M': /* meta */
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c != '-')
+ /* Invalid input. But be tolerant. */
+ return c;
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ c = do_getc_escaped (c, false);
+ }
+ return c | 0x80;
+
+ case 'S': /* shift */
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c != '-')
+ /* Invalid input. But be tolerant. */
+ return c;
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ c = do_getc_escaped (c, false);
+ }
+ return (c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c);
+
+ case 'H': /* hyper */
+ case 'A': /* alt */
+ case 's': /* super */
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c != '-')
+ /* Invalid input. But be tolerant. */
+ return c;
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ c = do_getc_escaped (c, false);
+ }
+ return c;
+
+ case 'C': /* ctrl */
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c != '-')
+ /* Invalid input. But be tolerant. */
+ return c;
+ /*FALLTHROUGH*/
+ case '^':
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ c = do_getc_escaped (c, false);
+ }
+ if (c == '?')
+ return 0x7F;
+ if ((c & 0x5F) >= 0x41 && (c & 0x5F) <= 0x5A)
+ return c & 0x9F;
+ if ((c & 0x7F) >= 0x40 && (c & 0x7F) <= 0x5F)
+ return c & 0x9F;
+#if 0 /* We cannot handle NUL bytes in strings. */
+ if (c == ' ')
+ return 0x00;
+#endif
+ return c;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ /* An octal escape, as in ANSI C. */
+ {
+ int n = c - '0';
+
+ c = do_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = do_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ do_ungetc (c);
+ }
+ }
+ else
+ do_ungetc (c);
+ }
+ return (unsigned char) n;
+ }
+
+ case 'x':
+ /* A hexadecimal escape, as in ANSI C. */
+ {
+ int n = 0;
+
+ for (;;)
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ else if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ else
+ {
+ do_ungetc (c);
+ break;
+ }
+ }
+ return (unsigned char) n;
+ }
+
+ default:
+ /* Ignore Emacs multibyte character stuff. All the strings we are
+ interested in are ASCII strings. */
+ return c;
+ }
+}
+
+/* Read the next object.
+ 'first_in_list' and 'new_backquote_flag' are used for reading old
+ backquote syntax and new backquote syntax. */
+static void
+read_object (struct object *op, bool first_in_list, bool new_backquote_flag,
+ flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ int c;
+
+ c = do_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ op->type = t_eof;
+ return;
+
+ case '\n':
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between
+ both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ continue;
+
+ case '(':
+ {
+ int arg = 0; /* Current argument number. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;; arg++)
+ {
+ struct object inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_object (&inner, arg == 0, new_backquote_flag,
+ inner_context);
+
+ /* Recognize end of list. */
+ if (inner.type == t_listclose)
+ {
+ op->type = t_other;
+ /* Don't bother converting "()" to "NIL". */
+ last_non_comment_line = line_number;
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return;
+ }
+
+ /* Dots are not allowed in every position. ']' is not allowed.
+ But be tolerant. */
+
+ /* EOF inside list is illegal. But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ if (arg == 0)
+ {
+ /* This is the function position. */
+ if (inner.type == t_symbol)
+ {
+ char *symbol_name = string_of_object (&inner);
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords,
+ symbol_name, strlen (symbol_name),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ symbol_name, strlen (symbol_name)));
+
+ free (symbol_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (argparser != NULL && inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_object (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+ }
+
+ free_object (&inner);
+ }
+
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case ')':
+ /* Tell the caller about the end of list.
+ Unmatched closing parenthesis is illegal. But be tolerant. */
+ op->type = t_listclose;
+ last_non_comment_line = line_number;
+ return;
+
+ case '[':
+ {
+ for (;;)
+ {
+ struct object inner;
+
+ read_object (&inner, false, new_backquote_flag, null_context);
+
+ /* Recognize end of vector. */
+ if (inner.type == t_vectorclose)
+ {
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /* Dots and ')' are not allowed. But be tolerant. */
+
+ /* EOF inside vector is illegal. But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ free_object (&inner);
+ }
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case ']':
+ /* Tell the caller about the end of vector.
+ Unmatched closing bracket is illegal. But be tolerant. */
+ op->type = t_vectorclose;
+ last_non_comment_line = line_number;
+ return;
+
+ case '\'':
+ {
+ struct object inner;
+
+ read_object (&inner, false, new_backquote_flag, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '`':
+ if (first_in_list)
+ goto default_label;
+ {
+ struct object inner;
+
+ read_object (&inner, false, true, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case ',':
+ if (!new_backquote_flag)
+ goto default_label;
+ {
+ int c = do_getc ();
+ /* The ,@ handling inside lists is wrong anyway, because
+ ,@form expands to an unknown number of elements. */
+ if (c != EOF && c != '@' && c != '.')
+ do_ungetc (c);
+ }
+ {
+ struct object inner;
+
+ read_object (&inner, false, false, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case ';':
+ {
+ bool all_semicolons = true;
+
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ if (c != ';')
+ all_semicolons = false;
+ if (!all_semicolons)
+ {
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ }
+ comment_line_end (0);
+ continue;
+ }
+
+ case '"':
+ {
+ op->token = XMALLOC (struct token);
+ init_token (op->token);
+ op->line_number_at_start = line_number;
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == '"')
+ break;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ c = do_getc_escaped (c, true);
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == IGNORABLE_ESCAPE)
+ /* Ignore escaped newline and escaped space. */
+ ;
+ else
+ {
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+ }
+ else
+ {
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+ }
+ op->type = t_string;
+
+ if (extract_all)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = op->line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_object (op),
+ null_context, &pos, NULL, savable_comment);
+ }
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '?':
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ else if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ else
+ {
+ c = do_getc_escaped (c, false);
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ }
+ }
+ /* Impossible to deal with Emacs multibyte character stuff here. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '#':
+ /* Dispatch macro handling. */
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+
+ switch (c)
+ {
+ case '^':
+ c = do_getc ();
+ if (c == '^')
+ c = do_getc ();
+ if (c == '[')
+ {
+ /* Read a char table, same syntax as a vector. */
+ for (;;)
+ {
+ struct object inner;
+
+ read_object (&inner, false, new_backquote_flag,
+ null_context);
+
+ /* Recognize end of vector. */
+ if (inner.type == t_vectorclose)
+ {
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /* Dots and ')' are not allowed. But be tolerant. */
+
+ /* EOF inside vector is illegal. But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ free_object (&inner);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ else
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ if (c != EOF)
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '&':
+ /* Read a bit vector. */
+ {
+ struct object length;
+ read_object (&length, first_in_list, new_backquote_flag,
+ null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&length);
+ }
+ c = do_getc ();
+ if (c == '"')
+ {
+ struct object string;
+ read_object (&string, first_in_list, new_backquote_flag,
+ null_context);
+ free_object (&string);
+ }
+ else
+ /* Invalid input. Be tolerant, no error message. */
+ do_ungetc (c);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '[':
+ /* Read a compiled function, same syntax as a vector. */
+ case '(':
+ /* Read a string with properties, same syntax as a list. */
+ {
+ struct object inner;
+ do_ungetc (c);
+ read_object (&inner, false, new_backquote_flag, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '@':
+ /* Read a comment consisting of a given number of bytes. */
+ {
+ unsigned int nskip = 0;
+
+ for (;;)
+ {
+ c = do_getc ();
+ if (!(c >= '0' && c <= '9'))
+ break;
+ nskip = 10 * nskip + (c - '0');
+ }
+ if (c != EOF)
+ {
+ do_ungetc (c);
+ for (; nskip > 0; nskip--)
+ if (do_getc () == EOF)
+ break;
+ }
+ continue;
+ }
+
+ case '$':
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '\'':
+ case ':':
+ case 'S': case 's': /* XEmacs only */
+ {
+ struct object inner;
+ read_object (&inner, false, new_backquote_flag, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Read Common Lisp style #n# or #n=. */
+ for (;;)
+ {
+ c = do_getc ();
+ if (!(c >= '0' && c <= '9'))
+ break;
+ }
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+ if (c == '=')
+ {
+ read_object (op, false, new_backquote_flag, outer_context);
+ last_non_comment_line = line_number;
+ return;
+ }
+ if (c == '#')
+ {
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ if (c == 'R' || c == 'r')
+ {
+ /* Read an integer. */
+ c = do_getc ();
+ if (c == '+' || c == '-')
+ c = do_getc ();
+ for (; c != EOF; c = do_getc ())
+ if (!c_isalnum (c))
+ {
+ do_ungetc (c);
+ break;
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ /* Invalid input. Be tolerant, no error message. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case 'X': case 'x':
+ case 'O': case 'o':
+ case 'B': case 'b':
+ {
+ /* Read an integer. */
+ c = do_getc ();
+ if (c == '+' || c == '-')
+ c = do_getc ();
+ for (; c != EOF; c = do_getc ())
+ if (!c_isalnum (c))
+ {
+ do_ungetc (c);
+ break;
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '*': /* XEmacs only */
+ {
+ /* Read a bit-vector. */
+ do
+ c = do_getc ();
+ while (c == '0' || c == '1');
+ if (c != EOF)
+ do_ungetc (c);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '+': /* XEmacs only */
+ case '-': /* XEmacs only */
+ /* Simply assume every feature expression is true. */
+ {
+ struct object inner;
+ read_object (&inner, false, new_backquote_flag, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ continue;
+ }
+
+ default:
+ /* Invalid input. Be tolerant, no error message. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /*NOTREACHED*/
+ abort ();
+
+ case '.':
+ c = do_getc ();
+ if (c != EOF)
+ {
+ do_ungetc (c);
+ if (c <= ' ' /* FIXME: Assumes ASCII compatible encoding */
+ || strchr ("\"'`,(", c) != NULL)
+ {
+ op->type = t_dot;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+ c = '.';
+ /*FALLTHROUGH*/
+ default:
+ default_label:
+ if (c <= ' ') /* FIXME: Assumes ASCII compatible encoding */
+ continue;
+ /* Read a token. */
+ {
+ bool symbol;
+
+ op->token = XMALLOC (struct token);
+ symbol = read_token (op->token, c);
+ if (symbol)
+ {
+ op->type = t_symbol;
+ last_non_comment_line = line_number;
+ return;
+ }
+ else
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+void
+extract_elisp (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When read_object returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ do
+ {
+ struct object toplevel_object;
+
+ read_object (&toplevel_object, false, false, null_context);
+
+ if (toplevel_object.type == t_eof)
+ break;
+
+ free_object (&toplevel_object);
+ }
+ while (!feof (fp));
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-elisp.h b/gettext-tools/src/x-elisp.h
new file mode 100644
index 0000000..f939957
--- /dev/null
+++ b/gettext-tools/src/x-elisp.h
@@ -0,0 +1,54 @@
+/* xgettext Emacs Lisp backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_ELISP \
+ { "el", "EmacsLisp" }, \
+
+#define SCANNERS_ELISP \
+ { "EmacsLisp", extract_elisp, \
+ &flag_table_elisp, &formatstring_elisp, NULL, NULL }, \
+
+/* Scan an Emacs Lisp file and add its translatable strings to mdlp. */
+extern void extract_elisp (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_elisp_extract_all (void);
+extern void x_elisp_keyword (const char *name);
+
+extern void init_flag_table_elisp (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-glade.c b/gettext-tools/src/x-glade.c
new file mode 100644
index 0000000..eebce00
--- /dev/null
+++ b/gettext-tools/src/x-glade.c
@@ -0,0 +1,612 @@
+/* xgettext glade backend.
+ Copyright (C) 2002-2003, 2005-2009, 2013 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-glade.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "basename.h"
+#include "progname.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "gettext.h"
+#include "libexpat-compat.h"
+
+#define _(s) gettext(s)
+
+
+/* Glade is an XML based format with three variants. The syntax for
+ each format is defined as follows.
+
+ - Glade 1
+ Some example files are contained in libglade-0.16.
+
+ - Glade 2
+ See http://library.gnome.org/devel/libglade/unstable/libglade-dtd.html
+
+ - GtkBuilder
+ See https://developer.gnome.org/gtk3/stable/GtkBuilder.html#BUILDER-UI */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+/* The keywords correspond to the translatable elements in Glade 1.
+ For Glade 2 and GtkBuilder, translatable content is determined by
+ the translatable="..." attribute, thus those keywords are not used. */
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_glade_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_glade_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ hash_insert_entry (&keywords, name, strlen (name), NULL);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_glade_keyword ("label");
+ x_glade_keyword ("title");
+ x_glade_keyword ("text");
+ x_glade_keyword ("format");
+ x_glade_keyword ("copyright");
+ x_glade_keyword ("comments");
+ x_glade_keyword ("preview_text");
+ x_glade_keyword ("tooltip");
+ default_keywords = false;
+ }
+}
+
+
+
+/* ============================= XML parsing. ============================= */
+
+#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
+
+/* Accumulator for the extracted messages. */
+static message_list_ty *mlp;
+
+/* Logical filename, used to label the extracted messages. */
+static char *logical_file_name;
+
+/* XML parser. */
+static XML_Parser parser;
+
+struct element_state
+{
+ bool extract_string;
+ bool extract_context; /* used by Glade 2 */
+ char *extracted_comment; /* used by Glade 2 or GtkBuilder */
+ char *extracted_context; /* used by GtkBuilder */
+ int lineno;
+ char *buffer;
+ size_t bufmax;
+ size_t buflen;
+};
+static struct element_state *stack;
+static size_t stack_size;
+
+/* Ensures stack_size >= size. */
+static void
+ensure_stack_size (size_t size)
+{
+ if (size > stack_size)
+ {
+ stack_size = 2 * stack_size;
+ if (stack_size < size)
+ stack_size = size;
+ stack =
+ (struct element_state *)
+ xrealloc (stack, stack_size * sizeof (struct element_state));
+ }
+}
+
+static size_t stack_depth;
+
+/* Parser logic for each Glade compatible file format. */
+struct element_parser
+{
+ void (*start_element) (struct element_state *p, const char *name,
+ const char **attributes);
+ void (*end_element) (struct element_state *p, const char *name);
+};
+static struct element_parser *element_parser;
+
+static void
+start_element_null (struct element_state *p, const char *name,
+ const char **attributes)
+{
+}
+
+static void
+end_element_null (struct element_state *p, const char *name)
+{
+}
+
+static void
+start_element_glade1 (struct element_state *p, const char *name,
+ const char **attributes)
+{
+ void *hash_result;
+
+ /* In Glade 1, a few specific elements are translatable without
+ --extract-all option. */
+ p->extract_string = extract_all;
+ if (!p->extract_string)
+ p->extract_string =
+ (hash_find_entry (&keywords, name, strlen (name), &hash_result) == 0);
+}
+
+static void
+end_element_glade1 (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->buffer != NULL)
+ {
+ remember_a_message (mlp, NULL, p->buffer,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+ p->buffer = NULL;
+ }
+}
+
+static void
+start_element_glade2 (struct element_state *p, const char *name,
+ const char **attributes)
+{
+ /* In Glade 2, all <property> and <atkproperty> elements are translatable
+ that have the attribute translatable="yes".
+ See <http://library.gnome.org/devel/libglade/unstable/libglade-dtd.html>.
+ The translator comment is found in the attribute comments="...".
+ See <http://live.gnome.org/TranslationProject/DevGuidelines/Use comments>.
+ If the element has the attribute context="yes", the content of
+ the element is in the form "msgctxt|msgid". */
+ if (strcmp (name, "property") == 0 || strcmp (name, "atkproperty") == 0)
+ {
+ bool has_translatable = false;
+ bool has_context = false;
+ const char *extracted_comment = NULL;
+ const char **attp = attributes;
+ while (*attp != NULL)
+ {
+ if (strcmp (attp[0], "translatable") == 0)
+ has_translatable = (strcmp (attp[1], "yes") == 0);
+ else if (strcmp (attp[0], "comments") == 0)
+ extracted_comment = attp[1];
+ else if (strcmp (attp[0], "context") == 0)
+ has_context = (strcmp (attp[1], "yes") == 0);
+ attp += 2;
+ }
+ p->extract_string = has_translatable;
+ p->extract_context = has_context;
+ p->extracted_comment =
+ (has_translatable && extracted_comment != NULL
+ ? xstrdup (extracted_comment)
+ : NULL);
+ }
+
+ /* In Glade 2, the attribute description="..." of <atkaction>
+ element is also translatable. */
+ if (strcmp (name, "atkaction") == 0)
+ {
+ const char **attp = attributes;
+ while (*attp != NULL)
+ {
+ if (strcmp (attp[0], "description") == 0)
+ {
+ if (strcmp (attp[1], "") != 0)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = XML_GetCurrentLineNumber (parser);
+
+ remember_a_message (mlp, NULL, xstrdup (attp[1]),
+ null_context, &pos,
+ NULL, savable_comment);
+ }
+ break;
+ }
+ attp += 2;
+ }
+ }
+}
+
+static void
+end_element_glade2 (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+ char *msgid = NULL;
+ char *msgctxt = NULL;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->extract_context)
+ {
+ char *separator = strchr (p->buffer, '|');
+
+ if (separator == NULL)
+ {
+ error_with_progname = false;
+ error_at_line (0, 0,
+ pos.file_name,
+ pos.line_number,
+ _("\
+Missing context for the string extracted from '%s' element"),
+ name);
+ error_with_progname = true;
+ }
+ else
+ {
+ *separator = '\0';
+ msgid = xstrdup (separator + 1);
+ msgctxt = xstrdup (p->buffer);
+ }
+ }
+ else
+ {
+ msgid = p->buffer;
+ p->buffer = NULL;
+ }
+
+ if (msgid != NULL)
+ remember_a_message (mlp, msgctxt, msgid,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+}
+
+static void
+start_element_gtkbuilder (struct element_state *p, const char *name,
+ const char **attributes)
+{
+ /* In GtkBuilder (used by Glade 3), all elements are translatable
+ that have the attribute translatable="yes".
+ See <https://developer.gnome.org/gtk3/stable/GtkBuilder.html#BUILDER-UI>.
+ The translator comment is found in the attribute comments="..."
+ and context is found in the attribute context="...". */
+ bool has_translatable = false;
+ const char *extracted_comment = NULL;
+ const char *extracted_context = NULL;
+ const char **attp = attributes;
+ while (*attp != NULL)
+ {
+ if (strcmp (attp[0], "translatable") == 0)
+ has_translatable = (strcmp (attp[1], "yes") == 0);
+ else if (strcmp (attp[0], "comments") == 0)
+ extracted_comment = attp[1];
+ else if (strcmp (attp[0], "context") == 0)
+ extracted_context = attp[1];
+ attp += 2;
+ }
+ p->extract_string = has_translatable;
+ p->extracted_comment =
+ (has_translatable && extracted_comment != NULL
+ ? xstrdup (extracted_comment)
+ : NULL);
+ p->extracted_context =
+ (has_translatable && extracted_context != NULL
+ ? xstrdup (extracted_context)
+ : NULL);
+}
+
+static void
+end_element_gtkbuilder (struct element_state *p, const char *name)
+{
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->buffer != NULL)
+ {
+ remember_a_message (mlp, p->extracted_context, p->buffer,
+ null_context, &pos,
+ p->extracted_comment, savable_comment);
+ p->buffer = NULL;
+ p->extracted_context = NULL;
+ }
+}
+
+static struct element_parser element_parser_null =
+{
+ start_element_null,
+ end_element_null
+};
+
+static struct element_parser element_parser_glade1 =
+{
+ start_element_glade1,
+ end_element_glade1
+};
+
+static struct element_parser element_parser_glade2 =
+{
+ start_element_glade2,
+ end_element_glade2
+};
+
+static struct element_parser element_parser_gtkbuilder =
+{
+ start_element_gtkbuilder,
+ end_element_gtkbuilder
+};
+
+/* Callback called when <element> is seen. */
+static void
+start_element_handler (void *userData, const char *name,
+ const char **attributes)
+{
+ struct element_state *p;
+
+ if (!stack_depth)
+ {
+ if (strcmp (name, "GTK-Interface") == 0)
+ element_parser = &element_parser_glade1;
+ else if (strcmp (name, "glade-interface") == 0)
+ element_parser = &element_parser_glade2;
+ else if (strcmp (name, "interface") == 0)
+ element_parser = &element_parser_gtkbuilder;
+ else
+ {
+ element_parser = &element_parser_null;
+ error_with_progname = false;
+ error_at_line (0, 0,
+ logical_file_name,
+ XML_GetCurrentLineNumber (parser),
+ _("\
+The root element <%s> is not allowed in a valid Glade file"),
+ name);
+ error_with_progname = true;
+ }
+ }
+
+ /* Increase stack depth. */
+ stack_depth++;
+ ensure_stack_size (stack_depth + 1);
+
+ /* Don't extract a string for the containing element. */
+ stack[stack_depth - 1].extract_string = false;
+
+ p = &stack[stack_depth];
+ p->extract_string = false;
+ p->extract_context = false;
+ p->extracted_comment = NULL;
+ p->extracted_context = NULL;
+
+ element_parser->start_element (p, name, attributes);
+
+ p->lineno = XML_GetCurrentLineNumber (parser);
+ p->buffer = NULL;
+ p->bufmax = 0;
+ p->buflen = 0;
+ if (!p->extract_string)
+ savable_comment_reset ();
+}
+
+/* Callback called when </element> is seen. */
+static void
+end_element_handler (void *userData, const char *name)
+{
+ struct element_state *p = &stack[stack_depth];
+
+ /* Actually extract string. */
+ if (p->extract_string)
+ {
+ /* Don't extract the empty string. */
+ if (p->buflen > 0)
+ {
+ if (p->buflen == p->bufmax)
+ p->buffer = (char *) xrealloc (p->buffer, p->buflen + 1);
+ p->buffer[p->buflen] = '\0';
+
+ element_parser->end_element (p, name);
+ }
+ }
+
+ /* Free memory for this stack level. */
+ if (p->extracted_comment != NULL)
+ free (p->extracted_comment);
+ if (p->extracted_context != NULL)
+ free (p->extracted_context);
+ if (p->buffer != NULL)
+ free (p->buffer);
+
+ /* Decrease stack depth. */
+ stack_depth--;
+
+ savable_comment_reset ();
+}
+
+/* Callback called when some text is seen. */
+static void
+character_data_handler (void *userData, const char *s, int len)
+{
+ struct element_state *p = &stack[stack_depth];
+
+ /* Accumulate character data. */
+ if (len > 0)
+ {
+ if (p->buflen + len > p->bufmax)
+ {
+ p->bufmax = 2 * p->bufmax;
+ if (p->bufmax < p->buflen + len)
+ p->bufmax = p->buflen + len;
+ p->buffer = (char *) xrealloc (p->buffer, p->bufmax);
+ }
+ memcpy (p->buffer + p->buflen, s, len);
+ p->buflen += len;
+ }
+}
+
+/* Callback called when some comment text is seen. */
+static void
+comment_handler (void *userData, const char *data)
+{
+ /* Split multiline comment into lines, and remove leading and trailing
+ whitespace. */
+ char *copy = xstrdup (data);
+ char *p;
+ char *q;
+
+ for (p = copy; (q = strchr (p, '\n')) != NULL; p = q + 1)
+ {
+ while (p[0] == ' ' || p[0] == '\t')
+ p++;
+ while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+ *q = '\0';
+ savable_comment_add (p);
+ }
+ q = p + strlen (p);
+ while (p[0] == ' ' || p[0] == '\t')
+ p++;
+ while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+ *q = '\0';
+ savable_comment_add (p);
+ free (copy);
+}
+
+
+static void
+do_extract_glade (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ /* expat feeds us strings in UTF-8 encoding. */
+ xgettext_current_source_encoding = po_charset_utf8;
+
+ logical_file_name = xstrdup (logical_filename);
+
+ init_keywords ();
+
+ parser = XML_ParserCreate (NULL);
+ if (parser == NULL)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ XML_SetElementHandler (parser, start_element_handler, end_element_handler);
+ XML_SetCharacterDataHandler (parser, character_data_handler);
+ XML_SetCommentHandler (parser, comment_handler);
+
+ stack_depth = 0;
+ element_parser = &element_parser_null;
+
+ while (!feof (fp))
+ {
+ char buf[4096];
+ int count = fread (buf, 1, sizeof buf, fp);
+
+ if (count == 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_filename);
+ /* EOF reached. */
+ break;
+ }
+
+ if (XML_Parse (parser, buf, count, 0) == 0)
+ error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
+ (unsigned long) XML_GetCurrentLineNumber (parser),
+ (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
+ XML_ErrorString (XML_GetErrorCode (parser)));
+ }
+
+ if (XML_Parse (parser, NULL, 0, 1) == 0)
+ error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
+ (unsigned long) XML_GetCurrentLineNumber (parser),
+ (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
+ XML_ErrorString (XML_GetErrorCode (parser)));
+
+ XML_ParserFree (parser);
+
+ /* Close scanner. */
+ logical_file_name = NULL;
+ parser = NULL;
+}
+
+#endif
+
+void
+extract_glade (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
+ if (LIBEXPAT_AVAILABLE ())
+ do_extract_glade (fp, real_filename, logical_filename, mdlp);
+ else
+#endif
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+Language \"glade\" is not supported. %s relies on expat.\n\
+This version was built without expat.\n"),
+ basename (program_name)));
+ exit (EXIT_FAILURE);
+ }
+}
diff --git a/gettext-tools/src/x-glade.h b/gettext-tools/src/x-glade.h
new file mode 100644
index 0000000..f212d2b
--- /dev/null
+++ b/gettext-tools/src/x-glade.h
@@ -0,0 +1,53 @@
+/* xgettext glade backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_GLADE \
+ { "glade", "glade" }, \
+ { "glade2", "glade" }, \
+ { "ui", "glade" }, \
+
+#define SCANNERS_GLADE \
+ { "glade", extract_glade, NULL, NULL, NULL, NULL }, \
+
+/* Scan a glade XML file and add its translatable strings to mdlp. */
+extern void extract_glade (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_glade_extract_all (void);
+extern void x_glade_keyword (const char *name);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-gsettings.c b/gettext-tools/src/x-gsettings.c
new file mode 100644
index 0000000..14a2584
--- /dev/null
+++ b/gettext-tools/src/x-gsettings.c
@@ -0,0 +1,386 @@
+/* xgettext GSettings schema file backend.
+ Copyright (C) 2002-2003, 2005-2009, 2013 Free Software Foundation, Inc.
+
+ This file was written by Daiki Ueno <ueno@gnu.org>, 2013.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-gsettings.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "basename.h"
+#include "progname.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "gettext.h"
+#include "libexpat-compat.h"
+
+#define _(s) gettext(s)
+
+
+/* GSettings schema file is an XML based format.
+ The syntax is defined in glib/gio/gschema.dtd and:
+ https://developer.gnome.org/gio/unstable/GSettings.html */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+
+/* ============================= XML parsing. ============================= */
+
+#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
+
+/* Accumulator for the extracted messages. */
+static message_list_ty *mlp;
+
+/* Logical filename, used to label the extracted messages. */
+static char *logical_file_name;
+
+/* XML parser. */
+static XML_Parser parser;
+
+enum whitespace_type_ty
+{
+ none,
+ normalize,
+ strip
+};
+typedef enum whitespace_type_ty whitespace_type_ty;
+
+struct element_state
+{
+ bool extract_string;
+ whitespace_type_ty whitespace;
+ char *extracted_context;
+ int lineno;
+ char *buffer;
+ size_t bufmax;
+ size_t buflen;
+};
+static struct element_state *stack;
+static size_t stack_size;
+
+static char *
+normalize_whitespace (const char *text, whitespace_type_ty whitespace)
+{
+ if (whitespace == none)
+ return xstrdup (text);
+ else
+ {
+ char *result, *p;
+
+ /* Strip whitespaces at the beginning/end of the text. */
+ result = xstrdup (text + strspn (text, " \t\n"));
+ for (p = result + strlen (result);
+ p > result && (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n');
+ p--)
+ ;
+ if (p > result)
+ *++p = '\0';
+
+ /* Normalize whitespaces within the text. */
+ if (whitespace == normalize)
+ {
+ char *end = result + strlen (result);
+ for (p = result; *p != '\0';)
+ {
+ size_t len = strspn (p, " \t\n");
+ if (len > 0)
+ {
+ *p = ' ';
+ memmove (p + 1, p + len, end - (p + len));
+ end -= len - 1;
+ *end = '\0';
+ p++;
+ }
+ p += strcspn (p, " \t\n");
+ }
+ }
+ return result;
+ }
+}
+
+/* Ensures stack_size >= size. */
+static void
+ensure_stack_size (size_t size)
+{
+ if (size > stack_size)
+ {
+ stack_size = 2 * stack_size;
+ if (stack_size < size)
+ stack_size = size;
+ stack =
+ (struct element_state *)
+ xrealloc (stack, stack_size * sizeof (struct element_state));
+ }
+}
+
+static size_t stack_depth;
+
+/* Callback called when <element> is seen. */
+static void
+start_element_handler (void *userData, const char *name,
+ const char **attributes)
+{
+ struct element_state *p;
+
+ /* Increase stack depth. */
+ stack_depth++;
+ ensure_stack_size (stack_depth + 1);
+
+ /* Don't extract a string for the containing element. */
+ stack[stack_depth - 1].extract_string = false;
+
+ p = &stack[stack_depth];
+ p->extract_string = extract_all;
+ p->extracted_context = NULL;
+
+ if (!p->extract_string)
+ {
+ bool has_translatable = false;
+ whitespace_type_ty whitespace = none;
+ const char *extracted_context = NULL;
+ if (strcmp (name, "summary") == 0 || strcmp (name, "description") == 0)
+ {
+ has_translatable = true;
+ whitespace = normalize;
+ }
+ else if (strcmp (name, "default") == 0)
+ {
+ const char *extracted_l10n = NULL;
+ const char **attp = attributes;
+ while (*attp != NULL)
+ {
+ if (strcmp (attp[0], "context") == 0)
+ extracted_context = attp[1];
+ else if (strcmp (attp[0], "l10n") == 0)
+ extracted_l10n = attp[1];
+ attp += 2;
+ }
+ if (extracted_l10n != NULL)
+ {
+ has_translatable = true;
+ whitespace = strip;
+ }
+ }
+ p->extract_string = has_translatable;
+ p->whitespace = whitespace;
+ p->extracted_context =
+ (has_translatable && extracted_context != NULL
+ ? xstrdup (extracted_context)
+ : NULL);
+ }
+
+ p->lineno = XML_GetCurrentLineNumber (parser);
+ p->buffer = NULL;
+ p->bufmax = 0;
+ p->buflen = 0;
+ if (!p->extract_string)
+ savable_comment_reset ();
+}
+
+/* Callback called when </element> is seen. */
+static void
+end_element_handler (void *userData, const char *name)
+{
+ struct element_state *p = &stack[stack_depth];
+
+ /* Actually extract string. */
+ if (p->extract_string)
+ {
+ /* Don't extract the empty string. */
+ if (p->buflen > 0)
+ {
+ lex_pos_ty pos;
+
+ if (p->buflen == p->bufmax)
+ p->buffer = (char *) xrealloc (p->buffer, p->buflen + 1);
+ p->buffer[p->buflen] = '\0';
+
+ pos.file_name = logical_file_name;
+ pos.line_number = p->lineno;
+
+ if (p->buffer != NULL)
+ {
+ remember_a_message (mlp, p->extracted_context,
+ normalize_whitespace (p->buffer,
+ p->whitespace),
+ null_context, &pos,
+ NULL, savable_comment);
+ p->extracted_context = NULL;
+ }
+ }
+ }
+
+ /* Free memory for this stack level. */
+ if (p->extracted_context != NULL)
+ free (p->extracted_context);
+ if (p->buffer != NULL)
+ free (p->buffer);
+
+ /* Decrease stack depth. */
+ stack_depth--;
+
+ savable_comment_reset ();
+}
+
+/* Callback called when some text is seen. */
+static void
+character_data_handler (void *userData, const char *s, int len)
+{
+ struct element_state *p = &stack[stack_depth];
+
+ /* Accumulate character data. */
+ if (len > 0)
+ {
+ if (p->buflen + len > p->bufmax)
+ {
+ p->bufmax = 2 * p->bufmax;
+ if (p->bufmax < p->buflen + len)
+ p->bufmax = p->buflen + len;
+ p->buffer = (char *) xrealloc (p->buffer, p->bufmax);
+ }
+ memcpy (p->buffer + p->buflen, s, len);
+ p->buflen += len;
+ }
+}
+
+/* Callback called when some comment text is seen. */
+static void
+comment_handler (void *userData, const char *data)
+{
+ /* Split multiline comment into lines, and remove leading and trailing
+ whitespace. */
+ char *copy = xstrdup (data);
+ char *p;
+ char *q;
+
+ for (p = copy; (q = strchr (p, '\n')) != NULL; p = q + 1)
+ {
+ while (p[0] == ' ' || p[0] == '\t')
+ p++;
+ while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+ *q = '\0';
+ savable_comment_add (p);
+ }
+ q = p + strlen (p);
+ while (p[0] == ' ' || p[0] == '\t')
+ p++;
+ while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+ *q = '\0';
+ savable_comment_add (p);
+ free (copy);
+}
+
+
+static void
+do_extract_gsettings (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ /* expat feeds us strings in UTF-8 encoding. */
+ xgettext_current_source_encoding = po_charset_utf8;
+
+ logical_file_name = xstrdup (logical_filename);
+
+ parser = XML_ParserCreate (NULL);
+ if (parser == NULL)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ XML_SetElementHandler (parser, start_element_handler, end_element_handler);
+ XML_SetCharacterDataHandler (parser, character_data_handler);
+ XML_SetCommentHandler (parser, comment_handler);
+
+ stack_depth = 0;
+
+ while (!feof (fp))
+ {
+ char buf[4096];
+ int count = fread (buf, 1, sizeof buf, fp);
+
+ if (count == 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_filename);
+ /* EOF reached. */
+ break;
+ }
+
+ if (XML_Parse (parser, buf, count, 0) == 0)
+ error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
+ (unsigned long) XML_GetCurrentLineNumber (parser),
+ (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
+ XML_ErrorString (XML_GetErrorCode (parser)));
+ }
+
+ if (XML_Parse (parser, NULL, 0, 1) == 0)
+ error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
+ (unsigned long) XML_GetCurrentLineNumber (parser),
+ (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
+ XML_ErrorString (XML_GetErrorCode (parser)));
+
+ XML_ParserFree (parser);
+
+ /* Close scanner. */
+ logical_file_name = NULL;
+ parser = NULL;
+}
+
+#endif
+
+void
+extract_gsettings (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+#if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
+ if (LIBEXPAT_AVAILABLE ())
+ do_extract_gsettings (fp, real_filename, logical_filename, mdlp);
+ else
+#endif
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+Language \"gsettings\" is not supported. %s relies on expat.\n\
+This version was built without expat.\n"),
+ basename (program_name)));
+ exit (EXIT_FAILURE);
+ }
+}
diff --git a/gettext-tools/src/x-gsettings.h b/gettext-tools/src/x-gsettings.h
new file mode 100644
index 0000000..b06016d
--- /dev/null
+++ b/gettext-tools/src/x-gsettings.h
@@ -0,0 +1,45 @@
+/* xgettext GSettings schema file backend.
+ Copyright (C) 2002-2003, 2006, 2013 Free Software Foundation, Inc.
+ Written by Daiki Ueno <ueno@gnu.org>, 2013.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_GSETTINGS \
+ { "gschema.xml", "gsettings" }, \
+
+#define SCANNERS_GSETTINGS \
+ { "gsettings", extract_gsettings, NULL, NULL, NULL, NULL }, \
+
+/* Scan a gsettings XML file and add its translatable strings to mdlp. */
+extern void extract_gsettings (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-java.c b/gettext-tools/src/x-java.c
new file mode 100644
index 0000000..58c1ad7
--- /dev/null
+++ b/gettext-tools/src/x-java.c
@@ -0,0 +1,1497 @@
+/* xgettext Java backend.
+ Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-java.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The Java syntax is defined in the
+ Java Language Specification, Second Edition,
+ (available from http://java.sun.com/),
+ chapter 3 "Lexical Structure". */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_java_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_java_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Java
+ identifier sequence with dots.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_java_keyword ("GettextResource.gettext:2"); /* static method */
+ x_java_keyword ("GettextResource.ngettext:2,3"); /* static method */
+ x_java_keyword ("GettextResource.pgettext:2c,3"); /* static method */
+ x_java_keyword ("GettextResource.npgettext:2c,3,4"); /* static method */
+ x_java_keyword ("gettext");
+ x_java_keyword ("ngettext:1,2");
+ x_java_keyword ("pgettext:1c,2");
+ x_java_keyword ("npgettext:1c,2,3");
+ x_java_keyword ("getString"); /* ResourceBundle.getString */
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_java ()
+{
+ xgettext_record_flag ("GettextResource.gettext:2:pass-java-format");
+ xgettext_record_flag ("GettextResource.ngettext:2:pass-java-format");
+ xgettext_record_flag ("GettextResource.ngettext:3:pass-java-format");
+ xgettext_record_flag ("GettextResource.pgettext:3:pass-java-format");
+ xgettext_record_flag ("GettextResource.npgettext:3:pass-java-format");
+ xgettext_record_flag ("GettextResource.npgettext:4:pass-java-format");
+ xgettext_record_flag ("gettext:1:pass-java-format");
+ xgettext_record_flag ("ngettext:1:pass-java-format");
+ xgettext_record_flag ("ngettext:2:pass-java-format");
+ xgettext_record_flag ("pgettext:2:pass-java-format");
+ xgettext_record_flag ("npgettext:2:pass-java-format");
+ xgettext_record_flag ("npgettext:3:pass-java-format");
+ xgettext_record_flag ("getString:1:pass-java-format");
+ xgettext_record_flag ("MessageFormat:1:java-format");
+ xgettext_record_flag ("MessageFormat.format:1:java-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next single-byte character from the input file.
+ Pushback can consist of an unlimited number of 'u' followed by up to 4
+ other characters. */
+
+/* Special coding of multiple 'u's in the pushback buffer. */
+#define MULTIPLE_U(count) (0x1000 + (count))
+
+static int phase1_pushback[5];
+static unsigned int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ {
+ c = phase1_pushback[--phase1_pushback_length];
+ if (c >= MULTIPLE_U (0))
+ {
+ if (c > MULTIPLE_U (1))
+ phase1_pushback[phase1_pushback_length++] = c - 1;
+ return 'u';
+ }
+ else
+ return c;
+ }
+
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+
+ return c;
+}
+
+/* Supports any number of 'u' and up to 4 arbitrary characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == 'u')
+ {
+ if (phase1_pushback_length > 0
+ && phase1_pushback[phase1_pushback_length - 1] >= MULTIPLE_U (0))
+ phase1_pushback[phase1_pushback_length - 1]++;
+ else
+ {
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = MULTIPLE_U (1);
+ }
+ }
+ else
+ {
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+ }
+}
+
+
+/* Fetch the next single-byte character or Unicode character from the file.
+ (Here, as in the Java Language Specification, when we say "Unicode
+ character", we actually mean "UTF-16 encoding unit".) */
+
+/* Return value of phase 2, 3, 4 when EOF is reached. */
+#define P2_EOF 0xffff
+
+/* Convert an UTF-16 code point to a return value that can be distinguished
+ from a single-byte return value. */
+#define UNICODE(code) (0x10000 + (code))
+
+/* Test a return value of phase 2, 3, 4 whether it designates an UTF-16 code
+ point. */
+#define IS_UNICODE(p2_result) ((p2_result) >= 0x10000)
+
+/* Extract the UTF-16 code of a return value that satisfies IS_UNICODE. */
+#define UTF16_VALUE(p2_result) ((p2_result) - 0x10000)
+
+/* Reduces a return value of phase 2, 3, 4 by unmasking the UNICODE bit,
+ so that it can be more easily compared against an ASCII character.
+ (RED (c) == 'x') is equivalent to (c == 'x' || c == UNICODE ('x')). */
+#define RED(p2_result) ((p2_result) & 0xffff)
+
+static int phase2_pushback[1];
+static int phase2_pushback_length;
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return P2_EOF;
+ if (c == '\\')
+ {
+ c = phase1_getc ();
+ if (c == 'u')
+ {
+ unsigned int u_count = 1;
+ unsigned char buf[4];
+ unsigned int n;
+ int i;
+
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c != 'u')
+ break;
+ u_count++;
+ }
+ phase1_ungetc (c);
+
+ n = 0;
+ for (i = 0; i < 4; i++)
+ {
+ c = phase1_getc ();
+
+ if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ else
+ {
+ phase1_ungetc (c);
+ while (--i >= 0)
+ phase1_ungetc (buf[i]);
+ for (; u_count > 0; u_count--)
+ phase1_ungetc ('u');
+ return '\\';
+ }
+
+ buf[i] = c;
+ }
+ return UNICODE (n);
+ }
+ phase1_ungetc (c);
+ return '\\';
+ }
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != P2_EOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* Fetch the next single-byte character or Unicode character from the file.
+ With line number handling.
+ Convert line terminators to '\n' or UNICODE ('\n'). */
+
+static int phase3_pushback[2];
+static int phase3_pushback_length;
+
+static int
+phase3_getc ()
+{
+ int c;
+
+ if (phase3_pushback_length)
+ {
+ c = phase3_pushback[--phase3_pushback_length];
+ if (c == '\n')
+ ++line_number;
+ return c;
+ }
+
+ c = phase2_getc ();
+
+ /* Handle line terminators. */
+ if (RED (c) == '\r')
+ {
+ int c1 = phase2_getc ();
+
+ if (RED (c1) != '\n')
+ phase2_ungetc (c1);
+
+ /* Seen line terminator CR or CR/LF. */
+ if (c == '\r' || c1 == '\n')
+ {
+ ++line_number;
+ return '\n';
+ }
+ else
+ return UNICODE ('\n');
+ }
+ else if (RED (c) == '\n')
+ {
+ /* Seen line terminator LF. */
+ if (c == '\n')
+ {
+ ++line_number;
+ return '\n';
+ }
+ else
+ return UNICODE ('\n');
+ }
+
+ return c;
+}
+
+/* Supports 2 characters of pushback. */
+static void
+phase3_ungetc (int c)
+{
+ if (c != P2_EOF)
+ {
+ if (c == '\n')
+ --line_number;
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = c;
+ }
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* 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 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;
+};
+
+/* Initialize a 'struct string_buffer' to empty. */
+static inline void
+init_string_buffer (struct string_buffer *bp, lexical_context_ty lcontext)
+{
+ 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;
+}
+
+/* Auxiliary function: Append a byte to bp->curr. */
+static inline void
+string_buffer_append_byte (struct 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
+string_buffer_append_unicode_grow (struct 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
+string_buffer_append_unicode (struct 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 ();
+
+ string_buffer_append_unicode_grow (bp, count);
+ memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
+ bp->utf8_buflen += count;
+}
+
+/* Auxiliary function: Handle the attempt to append a lone surrogate to
+ bp->utf8. */
+static void
+string_buffer_append_lone_surrogate (struct string_buffer *bp, unsigned int uc)
+{
+ /* A half surrogate is invalid, therefore use U+FFFD instead.
+ It appears to be valid Java: The Java Language Specification,
+ 3rd ed., says "The Java programming language represents text
+ in sequences of 16-bit code units, using the UTF-16 encoding."
+ but does not impose constraints on the use of \uxxxx escape
+ sequences for surrogates. And the JDK's javac happily groks
+ half surrogates.
+ But a half surrogate is invalid in UTF-8:
+ - RFC 3629 says
+ "The definition of UTF-8 prohibits encoding character
+ numbers between U+D800 and U+DFFF".
+ - Unicode 4.0 chapter 3
+ <http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf>
+ section 3.9, p.77, says
+ "Because surrogate code points are not Unicode scalar
+ values, any UTF-8 byte sequence that would otherwise
+ map to code points D800..DFFF is ill-formed."
+ and in table 3-6, p. 78, does not mention D800..DFFF.
+ - The unicode.org FAQ question "How do I convert an unpaired
+ UTF-16 surrogate to UTF-8?" has the answer
+ "By representing such an unpaired surrogate on its own
+ as a 3-byte sequence, the resulting UTF-8 data stream
+ would become ill-formed."
+ So use U+FFFD instead. */
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: lone surrogate U+%04X"),
+ logical_file_name, line_number, uc);
+ error_with_progname = true;
+ string_buffer_append_unicode (bp, 0xfffd);
+}
+
+/* Auxiliary function: Flush bp->utf16_surr into bp->utf8_buffer. */
+static inline void
+string_buffer_flush_utf16_surr (struct string_buffer *bp)
+{
+ if (bp->utf16_surr != 0)
+ {
+ string_buffer_append_lone_surrogate (bp, bp->utf16_surr);
+ bp->utf16_surr = 0;
+ }
+}
+
+/* Auxiliary function: Flush bp->curr_buffer into bp->utf8_buffer. */
+static inline void
+string_buffer_flush_curr_buffer (struct string_buffer *bp, int lineno)
+{
+ if (bp->curr_buflen > 0)
+ {
+ char *curr;
+ size_t count;
+
+ string_buffer_append_byte (bp, '\0');
+
+ /* Convert from the source encoding to UTF-8. */
+ curr = from_current_source_encoding (bp->curr_buffer, bp->lcontext,
+ logical_file_name, lineno);
+
+ /* Append it to bp->utf8_buffer. */
+ count = strlen (curr);
+ string_buffer_append_unicode_grow (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;
+ }
+}
+
+/* Append a character or Unicode character to a 'struct string_buffer'. */
+static void
+string_buffer_append (struct string_buffer *bp, int c)
+{
+ if (IS_UNICODE (c))
+ {
+ /* Append a Unicode character. */
+
+ /* Switch from multibyte character mode to Unicode character mode. */
+ string_buffer_flush_curr_buffer (bp, line_number);
+
+ /* Test whether this character and the previous one form a Unicode
+ surrogate character pair. */
+ if (bp->utf16_surr != 0
+ && (c >= UNICODE (0xdc00) && c < UNICODE (0xe000)))
+ {
+ unsigned short utf16buf[2];
+ ucs4_t uc;
+
+ utf16buf[0] = bp->utf16_surr;
+ utf16buf[1] = UTF16_VALUE (c);
+ if (u16_mbtouc (&uc, utf16buf, 2) != 2)
+ abort ();
+
+ string_buffer_append_unicode (bp, uc);
+ bp->utf16_surr = 0;
+ }
+ else
+ {
+ string_buffer_flush_utf16_surr (bp);
+
+ if (c >= UNICODE (0xd800) && c < UNICODE (0xdc00))
+ bp->utf16_surr = UTF16_VALUE (c);
+ else if (c >= UNICODE (0xdc00) && c < UNICODE (0xe000))
+ string_buffer_append_lone_surrogate (bp, UTF16_VALUE (c));
+ else
+ string_buffer_append_unicode (bp, UTF16_VALUE (c));
+ }
+ }
+ else
+ {
+ /* Append a single byte. */
+
+ /* Switch from Unicode character mode to multibyte character mode. */
+ 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')
+ string_buffer_flush_curr_buffer (bp, line_number - 1);
+
+ string_buffer_append_byte (bp, (unsigned char) c);
+ }
+}
+
+/* Return the string buffer's contents. */
+static char *
+string_buffer_result (struct string_buffer *bp)
+{
+ /* Flush all into bp->utf8_buffer. */
+ string_buffer_flush_utf16_surr (bp);
+ string_buffer_flush_curr_buffer (bp, line_number);
+ /* NUL-terminate it. */
+ string_buffer_append_unicode_grow (bp, 1);
+ bp->utf8_buffer[bp->utf8_buflen] = '\0';
+ /* Return it. */
+ return bp->utf8_buffer;
+}
+
+/* Free the memory pointed to by a 'struct string_buffer'. */
+static inline void
+free_string_buffer (struct string_buffer *bp)
+{
+ free (bp->utf8_buffer);
+ free (bp->curr_buffer);
+}
+
+
+/* ======================== Accumulating comments. ======================== */
+
+
+/* Accumulating a single comment line. */
+
+static struct string_buffer comment_buffer;
+
+static inline void
+comment_start ()
+{
+ comment_buffer.utf8_buflen = 0;
+ comment_buffer.utf16_surr = 0;
+ comment_buffer.curr_buflen = 0;
+ comment_buffer.lcontext = lc_comment;
+}
+
+static inline bool
+comment_at_start ()
+{
+ return (comment_buffer.utf8_buflen == 0 && comment_buffer.utf16_surr == 0
+ && comment_buffer.curr_buflen == 0);
+}
+
+static inline void
+comment_add (int c)
+{
+ string_buffer_append (&comment_buffer, c);
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ char *buffer = string_buffer_result (&comment_buffer);
+ size_t buflen = strlen (buffer);
+
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* Replace each comment that is not inside a character constant or string
+ literal with a space or newline character. */
+
+static int
+phase4_getc ()
+{
+ int c0;
+ int c;
+ bool last_was_star;
+
+ c0 = phase3_getc ();
+ if (RED (c0) != '/')
+ return c0;
+ c = phase3_getc ();
+ switch (RED (c))
+ {
+ default:
+ phase3_ungetc (c);
+ return c0;
+
+ case '*':
+ /* C style comment. */
+ comment_start ();
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (c == P2_EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (RED (c) == ' ' || RED (c) == '\t')))
+ comment_add (c);
+ switch (RED (c))
+ {
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = line_number;
+ return ' ';
+
+ case '/':
+ /* C++ style comment. */
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase3_getc ();
+ if (RED (c) == '\n' || c == P2_EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (RED (c) == ' ' || RED (c) == '\t')))
+ comment_add (c);
+ }
+ phase3_ungetc (c); /* push back the newline, to decrement line_number */
+ comment_line_end (0);
+ phase3_getc (); /* read the newline again */
+ return '\n';
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase4_ungetc (int c)
+{
+ phase3_ungetc (c);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_lbrace, /* { */
+ token_type_rbrace, /* } */
+ token_type_comma, /* , */
+ token_type_dot, /* . */
+ token_type_string_literal, /* "abc" */
+ token_type_number, /* 1.23 */
+ token_type_symbol, /* identifier, keyword, null */
+ token_type_plus, /* + */
+ token_type_other /* character literal, misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ int line_number;
+};
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string_literal)
+ drop_reference (tp->comment);
+}
+
+
+/* Read an escape sequence inside a string literal or character literal. */
+static inline int
+do_getc_escaped ()
+{
+ int c;
+
+ /* Use phase 3, because phase 4 elides comments. */
+ c = phase3_getc ();
+ if (c == P2_EOF)
+ return UNICODE ('\\');
+ switch (RED (c))
+ {
+ case 'b':
+ return UNICODE (0x08);
+ case 't':
+ return UNICODE (0x09);
+ case 'n':
+ return UNICODE (0x0a);
+ case 'f':
+ return UNICODE (0x0c);
+ case 'r':
+ return UNICODE (0x0d);
+ case '"':
+ return UNICODE ('"');
+ case '\'':
+ return UNICODE ('\'');
+ case '\\':
+ return UNICODE ('\\');
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ int n = RED (c) - '0';
+ bool maybe3digits = (n < 4);
+
+ c = phase3_getc ();
+ if (RED (c) >= '0' && RED (c) <= '7')
+ {
+ n = (n << 3) + (RED (c) - '0');
+ if (maybe3digits)
+ {
+ c = phase3_getc ();
+ if (RED (c) >= '0' && RED (c) <= '7')
+ n = (n << 3) + (RED (c) - '0');
+ else
+ phase3_ungetc (c);
+ }
+ }
+ else
+ phase3_ungetc (c);
+
+ return UNICODE (n);
+ }
+ default:
+ /* Invalid escape sequence. */
+ phase3_ungetc (c);
+ return UNICODE ('\\');
+ }
+}
+
+/* Read a string literal or character literal. */
+static void
+accumulate_escaped (struct string_buffer *literal, int delimiter)
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 3, because phase 4 elides comments. */
+ c = phase3_getc ();
+ if (c == P2_EOF || RED (c) == delimiter)
+ break;
+ if (RED (c) == '\n')
+ {
+ phase3_ungetc (c);
+ error_with_progname = false;
+ if (delimiter == '\'')
+ error (0, 0, _("%s:%d: warning: unterminated character constant"),
+ logical_file_name, line_number);
+ else
+ error (0, 0, _("%s:%d: warning: unterminated string constant"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ break;
+ }
+ if (RED (c) == '\\')
+ c = do_getc_escaped ();
+ string_buffer_append (literal, c);
+ }
+}
+
+
+/* Combine characters into tokens. Discard whitespace. */
+
+static token_ty phase5_pushback[3];
+static int phase5_pushback_length;
+
+static void
+phase5_get (token_ty *tp)
+{
+ int c;
+
+ if (phase5_pushback_length)
+ {
+ *tp = phase5_pushback[--phase5_pushback_length];
+ return;
+ }
+ tp->string = NULL;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase4_getc ();
+
+ if (c == P2_EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+
+ switch (RED (c))
+ {
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\t':
+ case '\f':
+ /* Ignore whitespace and comments. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (RED (c))
+ {
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case '{':
+ tp->type = token_type_lbrace;
+ return;
+
+ case '}':
+ tp->type = token_type_rbrace;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '.':
+ c = phase4_getc ();
+ if (!(RED (c) >= '0' && RED (c) <= '9'))
+ {
+ phase4_ungetc (c);
+ tp->type = token_type_dot;
+ return;
+ }
+ /* FALLTHROUGH */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ /* Don't need to verify the complicated syntax of integers and
+ floating-point numbers. We assume a valid Java input.
+ The simplified syntax that we recognize as number is: any
+ sequence of alphanumeric characters, additionally '+' and '-'
+ immediately after 'e' or 'E' except in hexadecimal numbers. */
+ bool hexadecimal = false;
+
+ for (;;)
+ {
+ c = phase4_getc ();
+ if (RED (c) >= '0' && RED (c) <= '9')
+ continue;
+ if ((RED (c) >= 'A' && RED (c) <= 'Z')
+ || (RED (c) >= 'a' && RED (c) <= 'z'))
+ {
+ if (RED (c) == 'X' || RED (c) == 'x')
+ hexadecimal = true;
+ if ((RED (c) == 'E' || RED (c) == 'e') && !hexadecimal)
+ {
+ c = phase4_getc ();
+ if (!(RED (c) == '+' || RED (c) == '-'))
+ phase4_ungetc (c);
+ }
+ continue;
+ }
+ if (RED (c) == '.')
+ continue;
+ break;
+ }
+ phase4_ungetc (c);
+ tp->type = token_type_number;
+ return;
+ }
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ /* Although Java allows identifiers containing many Unicode
+ characters, we recognize only identifiers consisting of ASCII
+ characters. This avoids conversion hassles w.r.t. the --keyword
+ arguments, and shouldn't be a big problem in practice. */
+ {
+ static char *buffer;
+ static int bufmax;
+ int bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = RED (c);
+ c = phase4_getc ();
+ if (!((RED (c) >= 'A' && RED (c) <= 'Z')
+ || (RED (c) >= 'a' && RED (c) <= 'z')
+ || (RED (c) >= '0' && RED (c) <= '9')
+ || RED (c) == '_'))
+ break;
+ }
+ phase4_ungetc (c);
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ return;
+ }
+
+ case '"':
+ /* String literal. */
+ {
+ struct string_buffer literal;
+
+ init_string_buffer (&literal, lc_string);
+ accumulate_escaped (&literal, '"');
+ tp->string = xstrdup (string_buffer_result (&literal));
+ free_string_buffer (&literal);
+ tp->comment = add_reference (savable_comment);
+ tp->type = token_type_string_literal;
+ return;
+ }
+
+ case '\'':
+ /* Character literal. */
+ {
+ struct string_buffer literal;
+
+ init_string_buffer (&literal, lc_outside);
+ accumulate_escaped (&literal, '\'');
+ free_string_buffer (&literal);
+ tp->type = token_type_other;
+ return;
+ }
+
+ case '+':
+ c = phase4_getc ();
+ if (RED (c) == '+')
+ /* Operator ++ */
+ tp->type = token_type_other;
+ else if (RED (c) == '=')
+ /* Operator += */
+ tp->type = token_type_other;
+ else
+ {
+ /* Operator + */
+ phase4_ungetc (c);
+ tp->type = token_type_plus;
+ }
+ return;
+
+ default:
+ /* Misc. operator. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports 3 tokens of pushback. */
+static void
+phase5_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = *tp;
+ }
+}
+
+
+/* Compile-time optimization of string literal concatenation.
+ Combine "string1" + ... + "stringN" to the concatenated string if
+ - the token before this expression is not ')' (because then the first
+ string could be part of a cast expression),
+ - the token after this expression is not '.' (because then the last
+ string could be part of a method call expression). */
+
+static token_ty phase6_pushback[2];
+static int phase6_pushback_length;
+
+static token_type_ty phase6_last;
+
+static void
+phase6_get (token_ty *tp)
+{
+ if (phase6_pushback_length)
+ {
+ *tp = phase6_pushback[--phase6_pushback_length];
+ return;
+ }
+
+ phase5_get (tp);
+ if (tp->type == token_type_string_literal && phase6_last != token_type_rparen)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase5_get (&token2);
+ if (token2.type == token_type_plus)
+ {
+ token_ty token3;
+
+ phase5_get (&token3);
+ if (token3.type == token_type_string_literal)
+ {
+ token_ty token_after;
+
+ phase5_get (&token_after);
+ if (token_after.type != token_type_dot)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ phase5_unget (&token_after);
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase5_unget (&token_after);
+ }
+ phase5_unget (&token3);
+ }
+ phase5_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+ phase6_last = tp->type;
+}
+
+/* Supports 2 tokens of pushback. */
+static void
+phase6_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase6_pushback_length == SIZEOF (phase6_pushback))
+ abort ();
+ phase6_pushback[phase6_pushback_length++] = *tp;
+ }
+}
+
+
+static void
+x_java_lex (token_ty *tp)
+{
+ phase6_get (tp);
+}
+
+/* Supports 2 tokens of pushback. */
+static void
+x_java_unlex (token_ty *tp)
+{
+ phase6_unget (tp);
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis or brace,
+ depending on TERMINATOR.
+ Extracted messages are added to MLP.
+ Return true upon eof, false upon closing parenthesis or brace. */
+static bool
+extract_parenthesized (message_list_ty *mlp, token_type_ty terminator,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_java_lex (&token);
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ /* Combine symbol1 . ... . symbolN to a single strings, so that
+ we can recognize static function calls like
+ GettextResource.gettext. The information present for
+ symbolI.....symbolN has precedence over the information for
+ symbolJ.....symbolN with J > I. */
+ char *sum = token.string;
+ size_t sum_len = strlen (sum);
+ const char *dottedname;
+ flag_context_list_ty *context_list;
+
+ for (;;)
+ {
+ token_ty token2;
+
+ x_java_lex (&token2);
+ if (token2.type == token_type_dot)
+ {
+ token_ty token3;
+
+ x_java_lex (&token3);
+ if (token3.type == token_type_symbol)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum =
+ (char *) xrealloc (sum, sum_len + 1 + addend_len + 1);
+ sum[sum_len] = '.';
+ memcpy (sum + sum_len + 1, addend, addend_len + 1);
+ sum_len += 1 + addend_len;
+
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ x_java_unlex (&token3);
+ }
+ x_java_unlex (&token2);
+ break;
+ }
+
+ for (dottedname = sum;;)
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, dottedname, strlen (dottedname),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ break;
+ }
+
+ dottedname = strchr (dottedname, '.');
+ if (dottedname == NULL)
+ {
+ state = 0;
+ break;
+ }
+ dottedname++;
+ }
+
+ for (dottedname = sum;;)
+ {
+ context_list =
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ dottedname, strlen (dottedname));
+ if (context_list != NULL)
+ break;
+
+ dottedname = strchr (dottedname, '.');
+ if (dottedname == NULL)
+ break;
+ dottedname++;
+ }
+ next_context_iter = flag_context_list_iterator (context_list);
+
+ free (sum);
+ continue;
+ }
+
+ case token_type_lparen:
+ if (extract_parenthesized (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ if (terminator == token_type_rparen)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return false;
+ }
+ if (terminator == token_type_rbrace)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: ')' found where '}' was expected"),
+ logical_file_name, token.line_number);
+ error_with_progname = true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbrace:
+ if (extract_parenthesized (mlp, token_type_rbrace,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rbrace:
+ if (terminator == token_type_rbrace)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return false;
+ }
+ if (terminator == token_type_rparen)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: '}' found where ')' was expected"),
+ logical_file_name, token.line_number);
+ error_with_progname = true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string_literal:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ token.comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+
+ case token_type_dot:
+ case token_type_number:
+ case token_type_plus:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_java (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ phase6_last = token_type_eof;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_parenthesized (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-java.h b/gettext-tools/src/x-java.h
new file mode 100644
index 0000000..49f2af9
--- /dev/null
+++ b/gettext-tools/src/x-java.h
@@ -0,0 +1,50 @@
+/* xgettext Java backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Tommy Johansson <tommy.johansson@kanalen.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_JAVA \
+ { "java", "Java" }, \
+
+#define SCANNERS_JAVA \
+ { "Java", extract_java, \
+ &flag_table_java, &formatstring_java, NULL, NULL }, \
+
+extern void extract_java (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_java_keyword (const char *keyword);
+extern void x_java_extract_all (void);
+
+extern void init_flag_table_java (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-javascript.c b/gettext-tools/src/x-javascript.c
new file mode 100644
index 0000000..35c9a9e
--- /dev/null
+++ b/gettext-tools/src/x-javascript.c
@@ -0,0 +1,1673 @@
+/* xgettext JavaScript backend.
+ Copyright (C) 2002-2003, 2005-2009, 2013 Free Software Foundation, Inc.
+
+ This file was written by Andreas Stricker <andy@knitter.ch>, 2010
+ It's based on x-python from Bruno Haible.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-javascript.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "basename.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "xalloc.h"
+#include "c-strstr.h"
+#include "c-ctype.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* The JavaScript aka ECMA-Script syntax is defined in ECMA-262
+ specification:
+ http://www.ecma-international.org/publications/standards/Ecma-262.htm */
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_javascript_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_javascript_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_javascript_keyword ("gettext");
+ x_javascript_keyword ("dgettext:2");
+ x_javascript_keyword ("dcgettext:2");
+ x_javascript_keyword ("ngettext:1,2");
+ x_javascript_keyword ("dngettext:2,3");
+ x_javascript_keyword ("pgettext:1c,2");
+ x_javascript_keyword ("dpgettext:2c,3");
+ x_javascript_keyword ("_");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_javascript ()
+{
+ xgettext_record_flag ("gettext:1:pass-javascript-format");
+ xgettext_record_flag ("dgettext:2:pass-javascript-format");
+ xgettext_record_flag ("dcgettext:2:pass-javascript-format");
+ xgettext_record_flag ("ngettext:1:pass-javascript-format");
+ xgettext_record_flag ("ngettext:2:pass-javascript-format");
+ xgettext_record_flag ("dngettext:2:pass-javascript-format");
+ xgettext_record_flag ("dngettext:3:pass-javascript-format");
+ xgettext_record_flag ("pgettext:2:pass-javascript-format");
+ xgettext_record_flag ("dpgettext:3:pass-javascript-format");
+ xgettext_record_flag ("_:1:pass-javascript-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 1. line_number handling. */
+
+/* Maximum used, roughly a safer MB_LEN_MAX. */
+#define MAX_PHASE1_PUSHBACK 16
+static unsigned char phase1_pushback[MAX_PHASE1_PUSHBACK];
+static int phase1_pushback_length;
+
+/* Read the next single byte from the input file. */
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ c = phase1_pushback[--phase1_pushback_length];
+ else
+ {
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+ }
+
+ if (c == '\n')
+ ++line_number;
+
+ return c;
+}
+
+/* Supports MAX_PHASE1_PUSHBACK characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* Phase 2: Conversion to Unicode.
+ For now, we expect JavaScript files to be encoded as UTF-8. */
+
+/* End-of-file indicator for functions returning an UCS-4 character. */
+#define UEOF -1
+
+static lexical_context_ty lexical_context;
+
+/* Maximum used, length of "<![CDATA[" tag minus one. */
+static int phase2_pushback[8];
+static int phase2_pushback_length;
+
+/* Read the next Unicode UCS-4 character from the input file. */
+static int
+phase2_getc ()
+{
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (xgettext_current_source_encoding == po_charset_ascii)
+ {
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ if (!c_isascii (c))
+ {
+ multiline_error (xstrdup (""),
+ xasprintf ("%s\n%s\n",
+ non_ascii_error_message (lexical_context,
+ real_file_name,
+ line_number),
+ _("\
+Please specify the source encoding through --from-code\n")));
+ exit (EXIT_FAILURE);
+ }
+ return c;
+ }
+ else if (xgettext_current_source_encoding != po_charset_utf8)
+ {
+#if HAVE_ICONV
+ /* Use iconv on an increasing number of bytes. Read only as many bytes
+ through phase1_getc as needed. This is needed to give reasonable
+ interactive behaviour when fp is connected to an interactive tty. */
+ unsigned char buf[MAX_PHASE1_PUSHBACK];
+ size_t bufcount;
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = (unsigned char) c;
+ bufcount = 1;
+
+ for (;;)
+ {
+ unsigned char scratchbuf[6];
+ const char *inptr = (const char *) &buf[0];
+ size_t insize = bufcount;
+ char *outptr = (char *) &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+
+ size_t res = iconv (xgettext_current_source_iconv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ /* We expect that a character has been produced if and only if
+ some input bytes have been consumed. */
+ if ((insize < bufcount) != (outsize < sizeof (scratchbuf)))
+ abort ();
+ if (outsize == sizeof (scratchbuf))
+ {
+ /* No character has been produced. Must be an error. */
+ if (res != (size_t)(-1))
+ abort ();
+
+ if (errno == EILSEQ)
+ {
+ /* An invalid multibyte sequence was encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the correct source encoding through --from-code\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ else if (errno == EINVAL)
+ {
+ /* An incomplete multibyte character. */
+ int c;
+
+ if (bufcount == MAX_PHASE1_PUSHBACK)
+ {
+ /* An overlong incomplete multibyte sequence was
+ encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Long incomplete multibyte sequence.\n\
+Please specify the correct source encoding through --from-code\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+
+ /* Read one more byte and retry iconv. */
+ c = phase1_getc ();
+ if (c == EOF)
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of file.\n\
+Please specify the correct source encoding through --from-code\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ if (c == '\n')
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of line.\n\
+Please specify the correct source encoding through --from-code\n"),
+ real_file_name, line_number - 1));
+ exit (EXIT_FAILURE);
+ }
+ buf[bufcount++] = (unsigned char) c;
+ }
+ else
+ error (EXIT_FAILURE, errno, _("%s:%d: iconv failure"),
+ real_file_name, line_number);
+ }
+ else
+ {
+ size_t outbytes = sizeof (scratchbuf) - outsize;
+ size_t bytes = bufcount - insize;
+ ucs4_t uc;
+
+ /* We expect that one character has been produced. */
+ if (bytes == 0)
+ abort ();
+ if (outbytes == 0)
+ abort ();
+ /* Push back the unused bytes. */
+ while (insize > 0)
+ phase1_ungetc (buf[--insize]);
+ /* Convert the character from UTF-8 to UCS-4. */
+ if (u8_mbtoucr (&uc, scratchbuf, outbytes) < (int) outbytes)
+ {
+ /* scratchbuf contains an out-of-range Unicode character
+ (> 0x10ffff). */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the source encoding through --from-code\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ return uc;
+ }
+ }
+#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. */
+ abort ();
+#endif
+ }
+ else
+ {
+ /* Read an UTF-8 encoded character. */
+ unsigned char buf[6];
+ unsigned int count;
+ int c;
+ ucs4_t uc;
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = c;
+ count = 1;
+
+ if (buf[0] >= 0xc0)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[1] = c;
+ count = 2;
+ }
+
+ if (buf[0] >= 0xe0
+ && ((buf[1] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[2] = c;
+ count = 3;
+ }
+
+ if (buf[0] >= 0xf0
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[3] = c;
+ count = 4;
+ }
+
+ if (buf[0] >= 0xf8
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[4] = c;
+ count = 5;
+ }
+
+ if (buf[0] >= 0xfc
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40)
+ && ((buf[4] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[5] = c;
+ count = 6;
+ }
+
+ u8_mbtouc (&uc, buf, count);
+ return uc;
+ }
+}
+
+/* Supports max (9, UNINAME_MAX + 3) pushback characters. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != UEOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* A string buffer type that allows appending Unicode characters.
+ Returns the entire string in UTF-8 encoding. */
+
+struct unicode_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;
+};
+
+/* Initialize a 'struct unicode_string_buffer' to empty. */
+static inline void
+init_unicode_string_buffer (struct unicode_string_buffer *bp)
+{
+ bp->utf8_buffer = NULL;
+ bp->utf8_buflen = 0;
+ bp->utf8_allocated = 0;
+}
+
+/* Auxiliary function: Ensure count more bytes are available in bp->utf8. */
+static inline void
+unicode_string_buffer_append_unicode_grow (struct unicode_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
+unicode_string_buffer_append_unicode (struct unicode_string_buffer *bp,
+ unsigned int 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 ();
+
+ unicode_string_buffer_append_unicode_grow (bp, count);
+ memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
+ bp->utf8_buflen += count;
+}
+
+/* Return the string buffer's contents. */
+static char *
+unicode_string_buffer_result (struct unicode_string_buffer *bp)
+{
+ /* NUL-terminate it. */
+ unicode_string_buffer_append_unicode_grow (bp, 1);
+ bp->utf8_buffer[bp->utf8_buflen] = '\0';
+ /* Return it. */
+ return bp->utf8_buffer;
+}
+
+/* Free the memory pointed to by a 'struct unicode_string_buffer'. */
+static inline void
+free_unicode_string_buffer (struct unicode_string_buffer *bp)
+{
+ free (bp->utf8_buffer);
+}
+
+
+/* ======================== Accumulating comments. ======================== */
+
+
+/* Accumulating a single comment line. */
+
+static struct unicode_string_buffer comment_buffer;
+
+static inline void
+comment_start ()
+{
+ lexical_context = lc_comment;
+ comment_buffer.utf8_buflen = 0;
+}
+
+static inline bool
+comment_at_start ()
+{
+ return (comment_buffer.utf8_buflen == 0);
+}
+
+static inline void
+comment_add (int c)
+{
+ unicode_string_buffer_append_unicode (&comment_buffer, c);
+}
+
+static inline const char *
+comment_line_end (size_t chars_to_remove)
+{
+ char *buffer = unicode_string_buffer_result (&comment_buffer);
+ size_t buflen = strlen (buffer) - chars_to_remove;
+
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ lexical_context = lc_outside;
+ return buffer;
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ======================== Recognizing comments. ======================== */
+
+
+/* Canonicalized encoding name for the current input file. */
+static const char *xgettext_current_file_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_current_file_source_encoding to UTF-8 (except from
+ ASCII or UTF-8, when this conversion is a no-op). */
+static iconv_t xgettext_current_file_source_iconv;
+#endif
+
+/* Tracking whether the current line is a continuation line or contains a
+ non-blank character. */
+static bool continuation_or_nonblank_line = false;
+
+
+/* Phase 3: Outside strings, replace backslash-newline with nothing and a
+ comment with nothing. */
+
+static int
+phase3_getc ()
+{
+ int c;
+
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == '\\')
+ {
+ c = phase2_getc ();
+ if (c != '\n')
+ {
+ phase2_ungetc (c);
+ /* This shouldn't happen usually, because "A backslash is
+ illegal elsewhere on a line outside a string literal." */
+ return '\\';
+ }
+ /* Eat backslash-newline. */
+ continuation_or_nonblank_line = true;
+ }
+ else if (c == '/')
+ {
+ c = phase2_getc ();
+ if (c == '/')
+ {
+ /* C++ style comment. */
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == UEOF || c == '\n')
+ {
+ comment_line_end (0);
+ break;
+ }
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ continuation_or_nonblank_line = false;
+ return c;
+ }
+ else if (c == '*')
+ {
+ /* C style comment. */
+ bool last_was_star = false;
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == UEOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ continuation_or_nonblank_line = false;
+ }
+ else
+ {
+ phase2_ungetc (c);
+ return '/';
+ }
+ }
+ else
+ {
+ if (c == '\n')
+ continuation_or_nonblank_line = false;
+ else if (!(c == ' ' || c == '\t' || c == '\f'))
+ continuation_or_nonblank_line = true;
+ return c;
+ }
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase3_ungetc (int c)
+{
+ phase2_ungetc (c);
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* Return value of phase7_getuc when EOF is reached. */
+#define P7_EOF (-1)
+#define P7_STRING_END (-2)
+
+/* Convert an UTF-16 or UTF-32 code point to a return value that can be
+ distinguished from a single-byte return value. */
+#define UNICODE(code) (0x100 + (code))
+
+/* Test a return value of phase7_getuc whether it designates an UTF-16 or
+ UTF-32 code point. */
+#define IS_UNICODE(p7_result) ((p7_result) >= 0x100)
+
+/* Extract the UTF-16 or UTF-32 code of a return value that satisfies
+ IS_UNICODE. */
+#define UNICODE_VALUE(p7_result) ((p7_result) - 0x100)
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_lbracket, /* [ */
+ token_type_rbracket, /* ] */
+ token_type_plus, /* + */
+ token_type_regexp, /* /.../ */
+ token_type_operator, /* - * / % . < > = ~ ! | & ? : ^ */
+ token_type_equal, /* = */
+ token_type_string, /* "abc", 'abc' */
+ token_type_keyword, /* return, else */
+ token_type_symbol, /* symbol, number */
+ token_type_other /* misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string, token_type_symbol,
+ token_type_keyword */
+ refcounted_string_list_ty *comment; /* for token_type_string */
+ int line_number;
+};
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string)
+ drop_reference (tp->comment);
+}
+
+
+/* JavaScript provides strings with either double or single quotes:
+ "abc" or 'abc'
+ Both may contain special sequences after a backslash:
+ \', \", \\, \b, \f, \n, \r, \t, \v
+ Special characters can be entered using hexadecimal escape
+ sequences or deprecated octal escape sequences:
+ \xXX, \OOO
+ Any unicode point can be entered using Unicode escape sequences:
+ \uNNNN
+ If a sequence after a backslash is not a legitimate character
+ escape sequence, the character value is the sequence itself without
+ a backslash. For example, \xxx is treated as xxx. */
+
+static int
+phase7_getuc (int quote_char)
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 2, because phase 3 elides comments. */
+ c = phase2_getc ();
+
+ if (c == UEOF)
+ return P7_EOF;
+
+ if (c == quote_char)
+ return P7_STRING_END;
+
+ if (c == '\n')
+ {
+ phase2_ungetc (c);
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated string"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ return P7_STRING_END;
+ }
+
+ if (c != '\\')
+ return UNICODE (c);
+
+ /* Dispatch according to the character following the backslash. */
+ c = phase2_getc ();
+ if (c == UEOF)
+ return P7_EOF;
+
+ switch (c)
+ {
+ case '\n':
+ continue;
+ case 'b':
+ return UNICODE ('\b');
+ case 'f':
+ return UNICODE ('\f');
+ case 'n':
+ return UNICODE ('\n');
+ case 'r':
+ return UNICODE ('\r');
+ case 't':
+ return UNICODE ('\t');
+ case 'v':
+ return UNICODE ('\v');
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = phase2_getc ();
+ if (c != UEOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = phase2_getc ();
+ if (c != UEOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ phase2_ungetc (c);
+ }
+ }
+ else
+ phase2_ungetc (c);
+ }
+ return UNICODE (n);
+ }
+ case 'x':
+ {
+ int c1 = phase2_getc ();
+ int n1;
+
+ if (c1 >= '0' && c1 <= '9')
+ n1 = c1 - '0';
+ else if (c1 >= 'A' && c1 <= 'F')
+ n1 = c1 - 'A' + 10;
+ else if (c1 >= 'a' && c1 <= 'f')
+ n1 = c1 - 'a' + 10;
+ else
+ n1 = -1;
+
+ if (n1 >= 0)
+ {
+ int c2 = phase2_getc ();
+ int n2;
+
+ if (c2 >= '0' && c2 <= '9')
+ n2 = c2 - '0';
+ else if (c2 >= 'A' && c2 <= 'F')
+ n2 = c2 - 'A' + 10;
+ else if (c2 >= 'a' && c2 <= 'f')
+ n2 = c2 - 'a' + 10;
+ else
+ n2 = -1;
+
+ if (n2 >= 0)
+ {
+ int n = (n1 << 4) + n2;
+ return UNICODE (n);
+ }
+
+ phase2_ungetc (c2);
+ }
+ phase2_ungetc (c1);
+ return UNICODE (c);
+ }
+ case 'u':
+ {
+ unsigned char buf[4];
+ unsigned int n = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ int c1 = phase2_getc ();
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ {
+ phase2_ungetc (c1);
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ return UNICODE (c);
+ }
+
+ buf[i] = c1;
+ }
+ return UNICODE (n);
+ }
+ default:
+ return UNICODE (c);
+ }
+ }
+}
+
+
+/* Combine characters into tokens. Discard whitespace except newlines at
+ the end of logical lines. */
+
+static token_ty phase5_pushback[2];
+static int phase5_pushback_length;
+
+static token_type_ty last_token_type = token_type_other;
+
+static void
+phase5_scan_regexp ()
+{
+ int c;
+
+ /* Scan for end of RegExp literal ('/'). */
+ for (;;)
+ {
+ /* Must use phase2 as there can't be comments. */
+ c = phase2_getc ();
+ if (c == '/')
+ break;
+ if (c == '\\')
+ {
+ c = phase2_getc ();
+ if (c != UEOF)
+ continue;
+ }
+ if (c == UEOF)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: RegExp literal terminated too early"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ return;
+ }
+ }
+
+ /* Scan for modifier flags (ECMA-262 5th section 15.10.4.1). */
+ c = phase2_getc ();
+ if (!(c == 'g' || c == 'i' || c == 'm'))
+ phase2_ungetc (c);
+}
+
+static int xml_element_depth = 0;
+static bool inside_embedded_js_in_xml = false;
+
+static bool
+phase5_scan_xml_markup (token_ty *tp)
+{
+ struct
+ {
+ const char *start;
+ const char *end;
+ } markers[] =
+ {
+ { "!--", "--" },
+ { "![CDATA[", "]]" },
+ { "?", "?" }
+ };
+ int i;
+
+ for (i = 0; i < SIZEOF (markers); i++)
+ {
+ const char *start = markers[i].start;
+ const char *end = markers[i].end;
+ int j;
+
+ /* Look for a start marker. */
+ for (j = 0; start[j] != '\0'; j++)
+ {
+ int c;
+
+ assert (phase2_pushback_length + j < SIZEOF (phase2_pushback));
+ c = phase2_getc ();
+ if (c == UEOF)
+ goto eof;
+ if (c != start[j])
+ {
+ int k = j;
+
+ phase2_ungetc (c);
+ k--;
+
+ for (; k >= 0; k--)
+ phase2_ungetc (start[k]);
+ break;
+ }
+ }
+
+ if (start[j] != '\0')
+ continue;
+
+ /* Skip until the end marker. */
+ for (;;)
+ {
+ int c;
+
+ for (j = 0; end[j] != '\0'; j++)
+ {
+ assert (phase2_pushback_length + 1 < SIZEOF (phase2_pushback));
+ c = phase2_getc ();
+ if (c == UEOF)
+ goto eof;
+ if (c != end[j])
+ {
+ /* Don't push the first character back so the next
+ iteration start from the second character. */
+ if (j > 0)
+ {
+ int k = j;
+
+ phase2_ungetc (c);
+ k--;
+
+ for (; k > 0; k--)
+ phase2_ungetc (end[k]);
+ }
+ break;
+ }
+ }
+
+ if (end[j] != '\0')
+ continue;
+
+ c = phase2_getc ();
+ if (c == UEOF)
+ goto eof;
+ if (c != '>')
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: %s is not allowed"),
+ logical_file_name, line_number,
+ end);
+ error_with_progname = true;
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+
+ eof:
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: unterminated XML markup"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ return false;
+}
+
+static void
+phase5_get (token_ty *tp)
+{
+ int c;
+
+ if (phase5_pushback_length)
+ {
+ *tp = phase5_pushback[--phase5_pushback_length];
+ last_token_type = tp->type;
+ return;
+ }
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase3_getc ();
+
+ switch (c)
+ {
+ case UEOF:
+ tp->type = last_token_type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\t':
+ case '\f':
+ /* Ignore whitespace and comments. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case '.':
+ {
+ int c1 = phase3_getc ();
+ phase3_ungetc (c1);
+ if (!(c1 >= '0' && c1 <= '9'))
+ {
+
+ tp->type = last_token_type = token_type_other;
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Symbol, or part of a number. */
+ {
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase3_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ default:
+ phase3_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ if (strcmp (buffer, "return") == 0
+ || strcmp (buffer, "else") == 0)
+ tp->type = last_token_type = token_type_keyword;
+ else
+ tp->type = last_token_type = token_type_symbol;
+ return;
+ }
+
+ /* Strings. */
+ {
+ struct mixed_string_buffer *bp;
+ int quote_char;
+
+ case '"': case '\'':
+ quote_char = c;
+ lexical_context = lc_string;
+ /* Start accumulating the string. */
+ bp = mixed_string_buffer_alloc (lexical_context,
+ logical_file_name,
+ line_number);
+ for (;;)
+ {
+ int uc = phase7_getuc (quote_char);
+
+ /* Keep line_number in sync. */
+ bp->line_number = line_number;
+
+ if (uc == P7_EOF || uc == P7_STRING_END)
+ break;
+
+ if (IS_UNICODE (uc))
+ {
+ assert (UNICODE_VALUE (uc) >= 0
+ && UNICODE_VALUE (uc) < 0x110000);
+ mixed_string_buffer_append_unicode (bp,
+ UNICODE_VALUE (uc));
+ }
+ else
+ mixed_string_buffer_append_char (bp, uc);
+ }
+ tp->string = mixed_string_buffer_done (bp);
+ tp->comment = add_reference (savable_comment);
+ lexical_context = lc_outside;
+ tp->type = last_token_type = token_type_string;
+ return;
+ }
+
+ case '+':
+ tp->type = last_token_type = token_type_plus;
+ return;
+
+ /* Identify operators. The multiple character ones are simply ignored
+ * as they are recognized here and are otherwise not relevant. */
+ case '-': case '*': /* '+' and '/' are not listed here! */
+ case '%':
+ case '~': case '!': case '|': case '&': case '^':
+ case '?': case ':':
+ tp->type = last_token_type = token_type_operator;
+ return;
+
+ case '=':
+ tp->type = last_token_type = token_type_equal;
+ return;
+
+ case '<':
+ {
+ /* We assume:
+ - XMLMarkup and XMLElement are only allowed after '=' or '('
+ - embedded JavaScript expressions in XML do not recurse
+ */
+ if (xml_element_depth > 0
+ || (!inside_embedded_js_in_xml
+ && (last_token_type == token_type_equal
+ || last_token_type == token_type_lparen)))
+ {
+ /* Comments, PI, or CDATA. */
+ if (phase5_scan_xml_markup (tp))
+ return;
+ c = phase2_getc ();
+
+ /* Closing tag. */
+ if (c == '/')
+ lexical_context = lc_xml_close_tag;
+
+ /* Opening element. */
+ else
+ {
+ phase2_ungetc (c);
+ lexical_context = lc_xml_open_tag;
+ xml_element_depth++;
+ }
+
+ tp->type = last_token_type = token_type_other;
+ }
+ else
+ tp->type = last_token_type = token_type_operator;
+ }
+ return;
+
+ case '>':
+ if (xml_element_depth > 0 && !inside_embedded_js_in_xml)
+ {
+ switch (lexical_context)
+ {
+ case lc_xml_open_tag:
+ lexical_context = lc_xml_content;
+ break;
+
+ case lc_xml_close_tag:
+ if (xml_element_depth-- > 0)
+ lexical_context = lc_xml_content;
+ else
+ lexical_context = lc_outside;
+ break;
+
+ default:
+ break;
+ }
+ tp->type = last_token_type = token_type_other;
+ }
+ else
+ tp->type = last_token_type = token_type_operator;
+ return;
+
+ case '/':
+ if (xml_element_depth > 0 && !inside_embedded_js_in_xml)
+ {
+ /* If it appears in an opening tag of an XML element, it's
+ part of '/>'. */
+ if (lexical_context == lc_xml_open_tag)
+ {
+ c = phase2_getc ();
+ if (c == '>')
+ lexical_context = lc_outside;
+ else
+ phase2_ungetc (c);
+ }
+ tp->type = last_token_type = token_type_other;
+ return;
+ }
+
+ /* Either a division operator or the start of a regular
+ expression literal. If the '/' token is spotted after a
+ symbol it's a division, otherwise it's a regular
+ expression. */
+ if (last_token_type == token_type_symbol
+ || last_token_type == token_type_rparen
+ || last_token_type == token_type_rbracket)
+ tp->type = last_token_type = token_type_operator;
+ else
+ {
+ phase5_scan_regexp (tp);
+ tp->type = last_token_type = token_type_regexp;
+ }
+ return;
+
+ case '{':
+ if (xml_element_depth > 0 && !inside_embedded_js_in_xml)
+ inside_embedded_js_in_xml = true;
+ tp->type = last_token_type = token_type_other;
+ return;
+
+ case '}':
+ if (xml_element_depth > 0 && inside_embedded_js_in_xml)
+ inside_embedded_js_in_xml = false;
+ tp->type = last_token_type = token_type_other;
+ return;
+
+ case '(':
+ tp->type = last_token_type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = last_token_type = token_type_rparen;
+ return;
+
+ case ',':
+ tp->type = last_token_type = token_type_comma;
+ return;
+
+ case '[':
+ tp->type = last_token_type = token_type_lbracket;
+ return;
+
+ case ']':
+ tp->type = last_token_type = token_type_rbracket;
+ return;
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = last_token_type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports only one pushback token. */
+static void
+phase5_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = *tp;
+ }
+}
+
+
+/* String concatenation with '+'. */
+
+static void
+x_javascript_lex (token_ty *tp)
+{
+ phase5_get (tp);
+ if (tp->type == token_type_string)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase5_get (&token2);
+ if (token2.type == token_type_plus)
+ {
+ token_ty token3;
+
+ phase5_get (&token3);
+ if (token3.type == token_type_string)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase5_unget (&token3);
+ }
+ phase5_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid JavaScript program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis or bracket.
+ Extracted messages are added to MLP.
+ DELIM can be either token_type_rparen or token_type_rbracket, or
+ token_type_eof to accept both.
+ Return true upon eof, false upon closing parenthesis or bracket. */
+static bool
+extract_balanced (message_list_ty *mlp,
+ token_type_ty delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_javascript_lex (&token);
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_balanced (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ if (delim == token_type_rparen || delim == token_type_eof)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbracket:
+ if (extract_balanced (mlp, token_type_rbracket,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rbracket:
+ if (delim == token_type_rbracket || delim == token_type_eof)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ token.comment);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+
+ case token_type_keyword:
+ case token_type_plus:
+ case token_type_regexp:
+ case token_type_operator:
+ case token_type_equal:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_javascript (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ lexical_context = lc_outside;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ xml_element_depth = 0;
+
+ xgettext_current_file_source_encoding = xgettext_global_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_file_source_iconv = xgettext_global_source_iconv;
+#endif
+
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_source_iconv = xgettext_current_file_source_iconv;
+#endif
+
+ continuation_or_nonblank_line = false;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_balanced returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_balanced (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-javascript.h b/gettext-tools/src/x-javascript.h
new file mode 100644
index 0000000..a2f6418
--- /dev/null
+++ b/gettext-tools/src/x-javascript.h
@@ -0,0 +1,52 @@
+/* xgettext JavaScript backend.
+ Copyright (C) 2002-2003, 2006, 2013 Free Software Foundation, Inc.
+ This file was written by Andreas Stricker <andy@knitter.ch>, 2010.
+ It's based on x-python from Bruno Haible.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_JAVASCRIPT \
+ { "js", "JavaScript" }, \
+
+#define SCANNERS_JAVASCRIPT \
+ { "JavaScript", extract_javascript, \
+ &flag_table_javascript, &formatstring_javascript, NULL, NULL }, \
+
+/* Scan a Python file and add its translatable strings to mdlp. */
+extern void extract_javascript (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_javascript_keyword (const char *keyword);
+extern void x_javascript_extract_all (void);
+
+extern void init_flag_table_javascript (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-librep.c b/gettext-tools/src/x-librep.c
new file mode 100644
index 0000000..c91d22a
--- /dev/null
+++ b/gettext-tools/src/x-librep.c
@@ -0,0 +1,1133 @@
+/* xgettext librep backend.
+ Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-librep.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "c-ctype.h"
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+
+/* Summary of librep syntax:
+ - ';' starts a comment until end of line.
+ - Block comments start with '#|' and end with '|#'.
+ - Numbers are constituted of an optional prefix (#b, #B for binary,
+ #o, #O for octal, #d, #D for decimal, #x, #X for hexadecimal,
+ #e, #E for exact, #i, #I for inexact), an optional sign (+ or -), and
+ the digits.
+ - Characters are written as '?' followed by the character, possibly
+ with an escape sequence, for examples '?a', '?\n', '?\177'.
+ - Strings are delimited by double quotes. Backslash introduces an escape
+ sequence. The following are understood: '\n', '\r', '\f', '\t', '\a',
+ '\\', '\^C', '\012' (octal), '\x12' (hexadecimal).
+ - Symbols: can contain meta-characters - whitespace or any from ()[]'";|\' -
+ if preceded by backslash or enclosed in |...|.
+ - Keywords: written as #:SYMBOL.
+ - () delimit lists.
+ - [] delimit vectors.
+ The reader is implemented in librep-0.14/src/lisp.c. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_librep_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_librep_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Lisp
+ symbol. */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_librep_keyword ("_");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_librep ()
+{
+ xgettext_record_flag ("_:1:pass-librep-format");
+ xgettext_record_flag ("format:2:librep-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* A token consists of a sequence of characters. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ char *chars; /* the token's constituents */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (char *) xrealloc (tp->chars, tp->allocated * sizeof (char));
+ }
+}
+
+/* Read the next token. If 'first' is given, it points to the first
+ character, which has already been read. Returns true for a symbol,
+ false for a number. */
+static bool
+read_token (struct token *tp, const int *first)
+{
+ int c;
+ /* Variables for speculative number parsing: */
+ int radix = -1;
+ int nfirst = 0;
+ bool exact = true;
+ bool rational = false;
+ bool exponent = false;
+ bool had_sign = false;
+ bool expecting_prefix = false;
+
+ init_token (tp);
+
+ if (first)
+ c = *first;
+ else
+ c = do_getc ();
+
+ for (;; c = do_getc ())
+ {
+ switch (c)
+ {
+ case EOF:
+ goto done;
+
+ case ' ': case '\t': case '\n': case '\f': case '\r':
+ case '(': case ')': case '[': case ']':
+ case '\'': case '"': case ';': case ',': case '`':
+ goto done;
+
+ case '\\':
+ radix = 0;
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid, but be tolerant. */
+ break;
+ grow_token (tp);
+ tp->chars[tp->charcount++] = c;
+ break;
+
+ case '|':
+ radix = 0;
+ for (;;)
+ {
+ c = do_getc ();
+ if (c == EOF || c == '|')
+ break;
+ grow_token (tp);
+ tp->chars[tp->charcount++] = c;
+ }
+ break;
+
+ default:
+ if (radix != 0)
+ {
+ if (expecting_prefix)
+ {
+ switch (c)
+ {
+ case 'B': case 'b':
+ radix = 2;
+ break;
+ case 'O': case 'o':
+ radix = 8;
+ break;
+ case 'D': case 'd':
+ radix = 10;
+ break;
+ case 'X': case 'x':
+ radix = 16;
+ break;
+ case 'E': case 'e':
+ case 'I': case 'i':
+ break;
+ default:
+ radix = 0;
+ break;
+ }
+ expecting_prefix = false;
+ nfirst = tp->charcount + 1;
+ }
+ else if (tp->charcount == nfirst
+ && (c == '+' || c == '-' || c == '#'))
+ {
+ if (c == '#')
+ {
+ if (had_sign)
+ radix = 0;
+ else
+ expecting_prefix = true;
+ }
+ else
+ had_sign = true;
+ nfirst = tp->charcount + 1;
+ }
+ else
+ {
+ switch (radix)
+ {
+ case -1:
+ if (c == '.')
+ {
+ radix = 10;
+ exact = false;
+ }
+ else if (!(c >= '0' && c <= '9'))
+ radix = 0;
+ else if (c == '0')
+ radix = 1;
+ else
+ radix = 10;
+ break;
+
+ case 1:
+ switch (c)
+ {
+ case 'X': case 'x':
+ radix = 16;
+ nfirst = tp->charcount + 1;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ radix = 8;
+ nfirst = tp->charcount;
+ break;
+ case '.': case 'E': case 'e':
+ radix = 10;
+ exact = false;
+ break;
+ case '/':
+ radix = 10;
+ rational = true;
+ break;
+ default:
+ radix = 0;
+ break;
+ }
+ break;
+
+ default:
+ switch (c)
+ {
+ case '.':
+ if (exact && radix == 10 && !rational)
+ exact = false;
+ else
+ radix = 0;
+ break;
+ case '/':
+ if (exact && !rational)
+ rational = true;
+ else
+ radix = 0;
+ break;
+ case 'E': case 'e':
+ if (radix == 10)
+ {
+ if (!rational && !exponent)
+ {
+ exponent = true;
+ exact = false;
+ }
+ else
+ radix = 0;
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ if (exponent && (c == '+' || c == '-'))
+ break;
+ if ((radix <= 10
+ && !(c >= '0' && c <= '0' + radix - 1))
+ || (radix == 16 && !c_isxdigit (c)))
+ radix = 0;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (c == '#')
+ goto done;
+ }
+ grow_token (tp);
+ tp->chars[tp->charcount++] = c;
+ }
+ }
+ done:
+ if (c != EOF)
+ do_ungetc (c);
+ if (radix > 0 && nfirst < tp->charcount)
+ return false; /* number */
+ else
+ return true; /* symbol */
+}
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ============== Reading of objects. See CLHS 2 "Syntax". ============== */
+
+
+/* We are only interested in symbols (e.g. GETTEXT or NGETTEXT) and strings.
+ Other objects need not to be represented precisely. */
+enum object_type
+{
+ t_symbol, /* symbol */
+ t_string, /* string */
+ t_other, /* other kind of real object */
+ t_dot, /* '.' pseudo object */
+ t_close, /* ')' or ']' pseudo object */
+ t_eof /* EOF marker */
+};
+
+struct object
+{
+ enum object_type type;
+ struct token *token; /* for t_symbol and t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct object'. */
+static inline void
+free_object (struct object *op)
+{
+ if (op->type == t_symbol || op->type == t_string)
+ {
+ free_token (op->token);
+ free (op->token);
+ }
+}
+
+/* Convert a t_symbol/t_string token to a char*. */
+static char *
+string_of_object (const struct object *op)
+{
+ char *str;
+ int n;
+
+ if (!(op->type == t_symbol || op->type == t_string))
+ abort ();
+ n = op->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, op->token->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+/* Returns the character represented by an escape sequence. */
+static int
+do_getc_escaped (int c)
+{
+ switch (c)
+ {
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 'f':
+ return '\f';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case 'a':
+ return '\a';
+ case '^':
+ c = do_getc ();
+ if (c == EOF)
+ return EOF;
+ return c & 0x1f;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = do_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = do_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ do_ungetc (c);
+ }
+ }
+ else
+ do_ungetc (c);
+ }
+ return (unsigned char) n;
+ }
+ case 'x':
+ {
+ int n = 0;
+
+ for (;;)
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ else if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ else
+ {
+ do_ungetc (c);
+ break;
+ }
+ }
+ return (unsigned char) n;
+ }
+ default:
+ return c;
+ }
+}
+
+/* Read the next object. */
+static void
+read_object (struct object *op, flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ int c;
+
+ c = do_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ op->type = t_eof;
+ return;
+
+ case '\n':
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between
+ both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ continue;
+
+ case ' ': case '\t': case '\f': case '\r':
+ continue;
+
+ case '(':
+ {
+ int arg = 0; /* Current argument number. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;; arg++)
+ {
+ struct object inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_object (&inner, inner_context);
+
+ /* Recognize end of list. */
+ if (inner.type == t_close)
+ {
+ op->type = t_other;
+ /* Don't bother converting "()" to "NIL". */
+ last_non_comment_line = line_number;
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return;
+ }
+
+ /* Dots are not allowed in every position.
+ But be tolerant. */
+
+ /* EOF inside list is illegal. But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ if (arg == 0)
+ {
+ /* This is the function position. */
+ if (inner.type == t_symbol)
+ {
+ char *symbol_name = string_of_object (&inner);
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords,
+ symbol_name, strlen (symbol_name),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ symbol_name, strlen (symbol_name)));
+
+ free (symbol_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (argparser != NULL && inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_object (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+ }
+
+ free_object (&inner);
+ }
+
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '[':
+ {
+ for (;;)
+ {
+ struct object inner;
+
+ read_object (&inner, null_context);
+
+ /* Recognize end of vector. */
+ if (inner.type == t_close)
+ {
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /* Dots are not allowed. But be tolerant. */
+
+ /* EOF inside vector is illegal. But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ free_object (&inner);
+ }
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case ')': case ']':
+ /* Tell the caller about the end of list or vector.
+ Unmatched closing parenthesis is illegal. But be tolerant. */
+ op->type = t_close;
+ last_non_comment_line = line_number;
+ return;
+
+ case ',':
+ {
+ int c = do_getc ();
+ /* The ,@ handling inside lists is wrong anyway, because
+ ,@form expands to an unknown number of elements. */
+ if (c != EOF && c != '@')
+ do_ungetc (c);
+ }
+ /*FALLTHROUGH*/
+ case '\'':
+ case '`':
+ {
+ struct object inner;
+
+ read_object (&inner, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case ';':
+ {
+ bool all_semicolons = true;
+
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF || c == '\n' || c == '\f' || c == '\r')
+ break;
+ if (c != ';')
+ all_semicolons = false;
+ if (!all_semicolons)
+ {
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ }
+ comment_line_end (0);
+ continue;
+ }
+
+ case '"':
+ {
+ op->token = XMALLOC (struct token);
+ init_token (op->token);
+ op->line_number_at_start = line_number;
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == '"')
+ break;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == '\n')
+ /* Ignore escaped newline. */
+ ;
+ else
+ {
+ c = do_getc_escaped (c);
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+ }
+ else
+ {
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+ }
+ op->type = t_string;
+
+ if (extract_all)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = op->line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_object (op),
+ null_context, &pos, NULL, savable_comment);
+ }
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '?':
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ else if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ else
+ {
+ c = do_getc_escaped (c);
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ }
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '#':
+ /* Dispatch macro handling. */
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+
+ switch (c)
+ {
+ case '!':
+ if (ftell (fp) == 2)
+ /* Skip comment until !# */
+ {
+ c = do_getc ();
+ for (;;)
+ {
+ if (c == EOF)
+ break;
+ if (c == '!')
+ {
+ c = do_getc ();
+ if (c == EOF || c == '#')
+ break;
+ }
+ else
+ c = do_getc ();
+ }
+ if (c == EOF)
+ {
+ /* EOF not allowed here. But be tolerant. */
+ op->type = t_eof;
+ return;
+ }
+ continue;
+ }
+ /*FALLTHROUGH*/
+ case '\'':
+ case ':':
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '[':
+ case '(':
+ {
+ struct object inner;
+ do_ungetc (c);
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '|':
+ {
+ int depth = 0;
+
+ comment_start ();
+ c = do_getc ();
+ for (;;)
+ {
+ if (c == EOF)
+ break;
+ if (c == '|')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ if (c == '#')
+ {
+ if (depth == 0)
+ {
+ comment_line_end (0);
+ break;
+ }
+ depth--;
+ comment_add ('|');
+ comment_add ('#');
+ c = do_getc ();
+ }
+ else
+ comment_add ('|');
+ }
+ else if (c == '#')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ comment_add ('#');
+ if (c == '|')
+ {
+ depth++;
+ comment_add ('|');
+ c = do_getc ();
+ }
+ }
+ else
+ {
+ /* We skip all leading white space. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ if (c == '\n')
+ {
+ comment_line_end (1);
+ comment_start ();
+ }
+ c = do_getc ();
+ }
+ }
+ if (c == EOF)
+ {
+ /* EOF not allowed here. But be tolerant. */
+ op->type = t_eof;
+ return;
+ }
+ last_comment_line = line_number;
+ continue;
+ }
+
+ case '\\':
+ {
+ struct token token;
+ int first = '\\';
+ read_token (&token, &first);
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case 'T': case 't':
+ case 'F': case 'f':
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case 'B': case 'b':
+ case 'O': case 'o':
+ case 'D': case 'd':
+ case 'X': case 'x':
+ case 'E': case 'e':
+ case 'I': case 'i':
+ {
+ struct token token;
+ do_ungetc (c);
+ c = '#';
+ read_token (&token, &c);
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ default:
+ /* Invalid input. Be tolerant, no error message. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /*NOTREACHED*/
+ abort ();
+
+ default:
+ /* Read a token. */
+ {
+ bool symbol;
+
+ op->token = XMALLOC (struct token);
+ symbol = read_token (op->token, &c);
+ if (op->token->charcount == 1 && op->token->chars[0] == '.')
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_dot;
+ last_non_comment_line = line_number;
+ return;
+ }
+ if (!symbol)
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ /* Distinguish between "foo" and "foo#bar". */
+ c = do_getc ();
+ if (c == '#')
+ {
+ struct token second_token;
+
+ free_token (op->token);
+ free (op->token);
+ read_token (&second_token, NULL);
+ free_token (&second_token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ else
+ {
+ if (c != EOF)
+ do_ungetc (c);
+ op->type = t_symbol;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+void
+extract_librep (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When read_object returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ do
+ {
+ struct object toplevel_object;
+
+ read_object (&toplevel_object, null_context);
+
+ if (toplevel_object.type == t_eof)
+ break;
+
+ free_object (&toplevel_object);
+ }
+ while (!feof (fp));
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-librep.h b/gettext-tools/src/x-librep.h
new file mode 100644
index 0000000..5747db1
--- /dev/null
+++ b/gettext-tools/src/x-librep.h
@@ -0,0 +1,54 @@
+/* xgettext librep backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_LIBREP \
+ { "jl", "librep" }, \
+
+#define SCANNERS_LIBREP \
+ { "librep", extract_librep, \
+ &flag_table_librep, &formatstring_librep, NULL, NULL }, \
+
+/* Scan a librep file and add its translatable strings to mdlp. */
+extern void extract_librep (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_librep_extract_all (void);
+extern void x_librep_keyword (const char *name);
+
+extern void init_flag_table_librep (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-lisp.c b/gettext-tools/src/x-lisp.c
new file mode 100644
index 0000000..20d88a2
--- /dev/null
+++ b/gettext-tools/src/x-lisp.c
@@ -0,0 +1,1425 @@
+/* xgettext Lisp backend.
+ Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-lisp.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+
+/* The Common Lisp syntax is described in the Common Lisp HyperSpec, chapter 2.
+ Since we are interested only in strings and in forms similar to
+ (gettext msgid ...)
+ or (ngettext msgid msgid_plural ...)
+ we make the following simplifications:
+
+ - Assume the keywords and strings are in an ASCII compatible encoding.
+ This means we can read the input file one byte at a time, instead of
+ one character at a time. No need to worry about multibyte characters:
+ If they occur as part of identifiers, they most probably act as
+ constituent characters, and the byte based approach will do the same.
+
+ - Assume the read table is the standard Common Lisp read table.
+ Non-standard read tables are mostly used to read data, not programs.
+
+ - Assume the read table case is :UPCASE, and *READ-BASE* is 10.
+
+ - Don't interpret #n= and #n#, they usually don't appear in programs.
+
+ - Don't interpret #+, #-, they are unlikely to appear in a gettext form.
+
+ The remaining syntax rules are:
+
+ - The syntax code assigned to each character, and how tokens are built
+ up from characters (single escape, multiple escape etc.).
+
+ - Comment syntax: ';' and '#| ... |#'.
+
+ - String syntax: "..." with single escapes.
+
+ - Read macros and dispatch macro character '#'. Needed to be able to
+ tell which is the n-th argument of a function call.
+
+ */
+
+
+/* ========================= Lexer customization. ========================= */
+
+/* 'readtable_case' is the case conversion that is applied to non-escaped
+ parts of symbol tokens. In Common Lisp: (readtable-case *readtable*). */
+
+enum rtcase
+{
+ case_upcase,
+ case_downcase,
+ case_preserve,
+ case_invert
+};
+
+static enum rtcase readtable_case = case_upcase;
+
+/* 'read_base' is the assumed radix of integers and rational numbers.
+ In Common Lisp: *read-base*. */
+static int read_base = 10;
+
+/* 'read_preserve_whitespace' specifies whether a whitespace character
+ that terminates a token must be pushed back on the input stream.
+ We set it to true, because the special newline side effect in read_object()
+ requires that read_object() sees every newline not inside a token. */
+static bool read_preserve_whitespace = true;
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_lisp_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_lisp_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+ size_t len;
+ char *symname;
+ size_t i;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Lisp symbol.
+ Extract the symbol name part. */
+ colon = strchr (name, ':');
+ if (colon != NULL && colon < end)
+ {
+ name = colon + 1;
+ if (name < end && *name == ':')
+ name++;
+ colon = strchr (name, ':');
+ if (colon != NULL && colon < end)
+ return;
+ }
+
+ /* Uppercase it. */
+ len = end - name;
+ symname = XNMALLOC (len, char);
+ for (i = 0; i < len; i++)
+ symname[i] =
+ (name[i] >= 'a' && name[i] <= 'z' ? name[i] - 'a' + 'A' : name[i]);
+
+ insert_keyword_callshape (&keywords, symname, len, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_lisp_keyword ("gettext"); /* I18N:GETTEXT */
+ x_lisp_keyword ("ngettext:1,2"); /* I18N:NGETTEXT */
+ x_lisp_keyword ("gettext-noop");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_lisp ()
+{
+ xgettext_record_flag ("gettext:1:pass-lisp-format");
+ xgettext_record_flag ("ngettext:1:pass-lisp-format");
+ xgettext_record_flag ("ngettext:2:pass-lisp-format");
+ xgettext_record_flag ("gettext-noop:1:pass-lisp-format");
+ xgettext_record_flag ("format:2:lisp-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* ========= Reading of tokens. See CLHS 2.2 "Reader Algorithm". ========= */
+
+
+/* Syntax code. See CLHS 2.1.4 "Character Syntax Types". */
+
+enum syntax_code
+{
+ syntax_illegal, /* non-printable, except whitespace */
+ syntax_single_esc, /* '\' (single escape) */
+ syntax_multi_esc, /* '|' (multiple escape) */
+ syntax_constituent, /* everything else (constituent) */
+ syntax_whitespace, /* TAB,LF,FF,CR,' ' (whitespace) */
+ syntax_eof, /* EOF */
+ syntax_t_macro, /* '()'"' (terminating macro) */
+ syntax_nt_macro /* '#' (non-terminating macro) */
+};
+
+/* Returns the syntax code of a character. */
+static enum syntax_code
+syntax_code_of (unsigned char c)
+{
+ switch (c)
+ {
+ case '\\':
+ return syntax_single_esc;
+ case '|':
+ return syntax_multi_esc;
+ case '\t': case '\n': case '\f': case '\r': case ' ':
+ return syntax_whitespace;
+ case '(': case ')': case '\'': case '"': case ',': case ';': case '`':
+ return syntax_t_macro;
+ case '#':
+ return syntax_nt_macro;
+ default:
+ if (c < ' ' && c != '\b')
+ return syntax_illegal;
+ else
+ return syntax_constituent;
+ }
+}
+
+struct char_syntax
+{
+ int ch; /* character */
+ enum syntax_code scode; /* syntax code */
+};
+
+/* Returns the next character and its syntax code. */
+static void
+read_char_syntax (struct char_syntax *p)
+{
+ int c = do_getc ();
+
+ p->ch = c;
+ p->scode = (c == EOF ? syntax_eof : syntax_code_of (c));
+}
+
+/* Every character in a token has an attribute assigned. The attributes
+ help during interpretation of the token. See
+ CLHS 2.3 "Interpretation of Tokens" for the possible interpretations,
+ and CLHS 2.1.4.2 "Constituent Traits". */
+
+enum attribute
+{
+ a_illg, /* invalid constituent */
+ a_pack_m, /* ':' package marker */
+ a_alpha, /* normal alphabetic */
+ a_escaped, /* alphabetic but not subject to case conversion */
+ a_ratio, /* '/' */
+ a_dot, /* '.' */
+ a_sign, /* '+-' */
+ a_extens, /* '_^' extension characters */
+ a_digit, /* '0123456789' */
+ a_letterdigit,/* 'A'-'Z','a'-'z' below base, except 'esfdlESFDL' */
+ a_expodigit, /* 'esfdlESFDL' below base */
+ a_letter, /* 'A'-'Z','a'-'z', except 'esfdlESFDL' */
+ a_expo /* 'esfdlESFDL' */
+};
+
+#define is_letter_attribute(a) ((a) >= a_letter)
+#define is_number_attribute(a) ((a) >= a_ratio)
+
+/* Returns the attribute of a character, assuming base 10. */
+static enum attribute
+attribute_of (unsigned char c)
+{
+ switch (c)
+ {
+ case ':':
+ return a_pack_m;
+ case '/':
+ return a_ratio;
+ case '.':
+ return a_dot;
+ case '+': case '-':
+ return a_sign;
+ case '_': case '^':
+ return a_extens;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return a_digit;
+ case 'a': case 'b': case 'c': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ return a_letter;
+ case 'e': case 's': case 'd': case 'f': case 'l':
+ case 'E': case 'S': case 'D': case 'F': case 'L':
+ return a_expo;
+ default:
+ /* Treat everything as valid. Never return a_illg. */
+ return a_alpha;
+ }
+}
+
+struct token_char
+{
+ unsigned char ch; /* character */
+ unsigned char attribute; /* attribute */
+};
+
+/* A token consists of a sequence of characters with associated attribute. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ struct token_char *chars; /* the token's constituents */
+ bool with_escape; /* whether single-escape or multiple escape occurs */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, struct token_char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (struct token_char *) xrealloc (tp->chars, tp->allocated * sizeof (struct token_char));
+ }
+}
+
+/* Read the next token. If 'first' is given, it points to the first
+ character, which has already been read.
+ The algorithm follows CLHS 2.2 "Reader Algorithm". */
+static void
+read_token (struct token *tp, const struct char_syntax *first)
+{
+ bool multiple_escape_flag;
+ struct char_syntax curr;
+
+ init_token (tp);
+ tp->with_escape = false;
+
+ multiple_escape_flag = false;
+ if (first)
+ curr = *first;
+ else
+ read_char_syntax (&curr);
+
+ for (;; read_char_syntax (&curr))
+ {
+ switch (curr.scode)
+ {
+ case syntax_illegal:
+ /* Invalid input. Be tolerant, no error message. */
+ do_ungetc (curr.ch);
+ return;
+
+ case syntax_single_esc:
+ tp->with_escape = true;
+ read_char_syntax (&curr);
+ if (curr.scode == syntax_eof)
+ /* Invalid input. Be tolerant, no error message. */
+ return;
+ grow_token (tp);
+ tp->chars[tp->charcount].ch = curr.ch;
+ tp->chars[tp->charcount].attribute = a_escaped;
+ tp->charcount++;
+ break;
+
+ case syntax_multi_esc:
+ multiple_escape_flag = !multiple_escape_flag;
+ tp->with_escape = true;
+ break;
+
+ case syntax_constituent:
+ case syntax_nt_macro:
+ grow_token (tp);
+ if (multiple_escape_flag)
+ {
+ tp->chars[tp->charcount].ch = curr.ch;
+ tp->chars[tp->charcount].attribute = a_escaped;
+ tp->charcount++;
+ }
+ else
+ {
+ tp->chars[tp->charcount].ch = curr.ch;
+ tp->chars[tp->charcount].attribute = attribute_of (curr.ch);
+ tp->charcount++;
+ }
+ break;
+
+ case syntax_whitespace:
+ case syntax_t_macro:
+ if (multiple_escape_flag)
+ {
+ grow_token (tp);
+ tp->chars[tp->charcount].ch = curr.ch;
+ tp->chars[tp->charcount].attribute = a_escaped;
+ tp->charcount++;
+ }
+ else
+ {
+ if (curr.scode != syntax_whitespace || read_preserve_whitespace)
+ do_ungetc (curr.ch);
+ return;
+ }
+ break;
+
+ case syntax_eof:
+ if (multiple_escape_flag)
+ /* Invalid input. Be tolerant, no error message. */
+ ;
+ return;
+ }
+ }
+}
+
+/* A potential number is a token which
+ 1. consists only of digits, '+','-','/','^','_','.' and number markers.
+ The base for digits is context dependent, but always 10 if a dot '.'
+ occurs. A number marker is a non-digit letter which is not adjacent
+ to a non-digit letter.
+ 2. has at least one digit.
+ 3. starts with a digit, '+','-','.','^' or '_'.
+ 4. does not end with '+' or '-'.
+ See CLHS 2.3.1.1 "Potential Numbers as Tokens".
+ */
+
+static inline bool
+has_a_dot (const struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (tp->chars[i].attribute == a_dot)
+ return true;
+ return false;
+}
+
+static inline bool
+all_a_number (const struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (!is_number_attribute (tp->chars[i].attribute))
+ return false;
+ return true;
+}
+
+static inline void
+a_letter_to_digit (const struct token *tp, int base)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (is_letter_attribute (tp->chars[i].attribute))
+ {
+ int c = tp->chars[i].ch;
+
+ if (c >= 'a')
+ c -= 'a' - 'A';
+ if (c - 'A' + 10 < base)
+ tp->chars[i].attribute -= 2; /* a_letter -> a_letterdigit,
+ a_expo -> a_expodigit */
+ }
+}
+
+static inline bool
+has_a_digit (const struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (tp->chars[i].attribute == a_digit
+ || tp->chars[i].attribute == a_letterdigit
+ || tp->chars[i].attribute == a_expodigit)
+ return true;
+ return false;
+}
+
+static inline bool
+has_adjacent_letters (const struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 1; i < n; i++)
+ if (is_letter_attribute (tp->chars[i-1].attribute)
+ && is_letter_attribute (tp->chars[i].attribute))
+ return true;
+ return false;
+}
+
+static bool
+is_potential_number (const struct token *tp, int *basep)
+{
+ /* CLHS 2.3.1.1.1:
+ "A potential number cannot contain any escape characters." */
+ if (tp->with_escape)
+ return false;
+
+ if (has_a_dot (tp))
+ *basep = 10;
+
+ if (!all_a_number (tp))
+ return false;
+
+ a_letter_to_digit (tp, *basep);
+
+ if (!has_a_digit (tp))
+ return false;
+
+ if (has_adjacent_letters (tp))
+ return false;
+
+ if (!(tp->chars[0].attribute >= a_dot
+ && tp->chars[0].attribute <= a_expodigit))
+ return false;
+
+ if (tp->chars[tp->charcount - 1].attribute == a_sign)
+ return false;
+
+ return true;
+}
+
+/* A number is one of integer, ratio, float. Each has a particular syntax.
+ See CLHS 2.3.1 "Numbers as Tokens".
+ But note a mistake: The exponent rule should read:
+ exponent ::= exponent-marker [sign] {decimal-digit}+
+ (see 22.1.3.1.3 "Printing Floats"). */
+
+enum number_type
+{
+ n_none,
+ n_integer,
+ n_ratio,
+ n_float
+};
+
+static enum number_type
+is_number (const struct token *tp, int *basep)
+{
+ struct token_char *ptr_limit;
+ struct token_char *ptr1;
+
+ if (!is_potential_number (tp, basep))
+ return n_none;
+
+ /* is_potential_number guarantees
+ - all attributes are >= a_ratio,
+ - there is at least one a_digit or a_letterdigit or a_expodigit, and
+ - if there is an a_dot, then *basep = 10. */
+
+ ptr1 = &tp->chars[0];
+ ptr_limit = &tp->chars[tp->charcount];
+
+ if (ptr1->attribute == a_sign)
+ ptr1++;
+
+ /* Test for syntax
+ * { a_sign | }
+ * { a_digit < base }+ { a_ratio { a_digit < base }+ | }
+ */
+ {
+ bool seen_a_ratio = false;
+ bool seen_a_digit = false; /* seen a digit in last digit block? */
+ struct token_char *ptr;
+
+ for (ptr = ptr1;; ptr++)
+ {
+ if (ptr >= ptr_limit)
+ {
+ if (!seen_a_digit)
+ break;
+ if (seen_a_ratio)
+ return n_ratio;
+ else
+ return n_integer;
+ }
+ if (ptr->attribute == a_digit
+ || ptr->attribute == a_letterdigit
+ || ptr->attribute == a_expodigit)
+ {
+ int c = ptr->ch;
+
+ c = (c < 'A' ? c - '0' : c < 'a' ? c - 'A' + 10 : c - 'a' + 10);
+ if (c >= *basep)
+ break;
+ seen_a_digit = true;
+ }
+ else if (ptr->attribute == a_ratio)
+ {
+ if (seen_a_ratio || !seen_a_digit)
+ break;
+ seen_a_ratio = true;
+ seen_a_digit = false;
+ }
+ else
+ break;
+ }
+ }
+
+ /* Test for syntax
+ * { a_sign | }
+ * { a_digit }* { a_dot { a_digit }* | }
+ * { a_expo { a_sign | } { a_digit }+ | }
+ *
+ * If there is an exponent part, there must be digits before the dot or
+ * after the dot. The result is a float.
+ * If there is no exponen:
+ * If there is no dot, it would an integer in base 10, but is has already
+ * been verified to not be an integer in the current base.
+ * If there is a dot:
+ * If there are digits after the dot, it's a float.
+ * Otherwise, if there are digits before the dot, it's an integer.
+ */
+ *basep = 10;
+ {
+ bool seen_a_dot = false;
+ bool seen_a_dot_with_leading_digits = false;
+ bool seen_a_digit = false; /* seen a digit in last digit block? */
+ struct token_char *ptr;
+
+ for (ptr = ptr1;; ptr++)
+ {
+ if (ptr >= ptr_limit)
+ {
+ /* no exponent */
+ if (!seen_a_dot)
+ return n_none;
+ if (seen_a_digit)
+ return n_float;
+ if (seen_a_dot_with_leading_digits)
+ return n_integer;
+ else
+ return n_none;
+ }
+ if (ptr->attribute == a_digit)
+ {
+ seen_a_digit = true;
+ }
+ else if (ptr->attribute == a_dot)
+ {
+ if (seen_a_dot)
+ return n_none;
+ seen_a_dot = true;
+ if (seen_a_digit)
+ seen_a_dot_with_leading_digits = true;
+ seen_a_digit = false;
+ }
+ else if (ptr->attribute == a_expo || ptr->attribute == a_expodigit)
+ break;
+ else
+ return n_none;
+ }
+ ptr++;
+ if (!seen_a_dot_with_leading_digits || !seen_a_digit)
+ return n_none;
+ if (ptr >= ptr_limit)
+ return n_none;
+ if (ptr->attribute == a_sign)
+ ptr++;
+ seen_a_digit = false;
+ for (;; ptr++)
+ {
+ if (ptr >= ptr_limit)
+ break;
+ if (ptr->attribute != a_digit)
+ return n_none;
+ seen_a_digit = true;
+ }
+ if (!seen_a_digit)
+ return n_none;
+ return n_float;
+ }
+}
+
+/* A token representing a symbol must be case converted.
+ For portability, we convert only ASCII characters here. */
+
+static void
+upcase_token (struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (tp->chars[i].attribute != a_escaped)
+ {
+ unsigned char c = tp->chars[i].ch;
+ if (c >= 'a' && c <= 'z')
+ tp->chars[i].ch = c - 'a' + 'A';
+ }
+}
+
+static void
+downcase_token (struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (tp->chars[i].attribute != a_escaped)
+ {
+ unsigned char c = tp->chars[i].ch;
+ if (c >= 'A' && c <= 'Z')
+ tp->chars[i].ch = c - 'A' + 'a';
+ }
+}
+
+static void
+case_convert_token (struct token *tp)
+{
+ int n = tp->charcount;
+ int i;
+
+ switch (readtable_case)
+ {
+ case case_upcase:
+ upcase_token (tp);
+ break;
+
+ case case_downcase:
+ downcase_token (tp);
+ break;
+
+ case case_preserve:
+ break;
+
+ case case_invert:
+ {
+ bool seen_uppercase = false;
+ bool seen_lowercase = false;
+ for (i = 0; i < n; i++)
+ if (tp->chars[i].attribute != a_escaped)
+ {
+ unsigned char c = tp->chars[i].ch;
+ if (c >= 'a' && c <= 'z')
+ seen_lowercase = true;
+ if (c >= 'A' && c <= 'Z')
+ seen_uppercase = true;
+ }
+ if (seen_uppercase)
+ {
+ if (!seen_lowercase)
+ downcase_token (tp);
+ }
+ else
+ {
+ if (seen_lowercase)
+ upcase_token (tp);
+ }
+ }
+ break;
+ }
+}
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ============== Reading of objects. See CLHS 2 "Syntax". ============== */
+
+
+/* We are only interested in symbols (e.g. GETTEXT or NGETTEXT) and strings.
+ Other objects need not to be represented precisely. */
+enum object_type
+{
+ t_symbol, /* symbol */
+ t_string, /* string */
+ t_other, /* other kind of real object */
+ t_dot, /* '.' pseudo object */
+ t_close, /* ')' pseudo object */
+ t_eof /* EOF marker */
+};
+
+struct object
+{
+ enum object_type type;
+ struct token *token; /* for t_symbol and t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct object'. */
+static inline void
+free_object (struct object *op)
+{
+ if (op->type == t_symbol || op->type == t_string)
+ {
+ free_token (op->token);
+ free (op->token);
+ }
+}
+
+/* Convert a t_symbol/t_string token to a char*. */
+static char *
+string_of_object (const struct object *op)
+{
+ char *str;
+ const struct token_char *p;
+ char *q;
+ int n;
+
+ if (!(op->type == t_symbol || op->type == t_string))
+ abort ();
+ n = op->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ q = str;
+ for (p = op->token->chars; n > 0; p++, n--)
+ *q++ = p->ch;
+ *q = '\0';
+ return str;
+}
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+/* Read the next object. */
+static void
+read_object (struct object *op, flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ struct char_syntax curr;
+
+ read_char_syntax (&curr);
+
+ switch (curr.scode)
+ {
+ case syntax_eof:
+ op->type = t_eof;
+ return;
+
+ case syntax_whitespace:
+ if (curr.ch == '\n')
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between
+ both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ continue;
+
+ case syntax_illegal:
+ op->type = t_other;
+ return;
+
+ case syntax_single_esc:
+ case syntax_multi_esc:
+ case syntax_constituent:
+ /* Start reading a token. */
+ op->token = XMALLOC (struct token);
+ read_token (op->token, &curr);
+ last_non_comment_line = line_number;
+
+ /* Interpret the token. */
+
+ /* Dots. */
+ if (!op->token->with_escape
+ && op->token->charcount == 1
+ && op->token->chars[0].attribute == a_dot)
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_dot;
+ return;
+ }
+ /* Tokens consisting entirely of dots are illegal, but be tolerant
+ here. */
+
+ /* Number. */
+ {
+ int base = read_base;
+
+ if (is_number (op->token, &base) != n_none)
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_other;
+ return;
+ }
+ }
+
+ /* We interpret all other tokens as symbols (including 'reserved
+ tokens', i.e. potential numbers which are not numbers). */
+ case_convert_token (op->token);
+ op->type = t_symbol;
+ return;
+
+ case syntax_t_macro:
+ case syntax_nt_macro:
+ /* Read a macro. */
+ switch (curr.ch)
+ {
+ case '(':
+ {
+ int arg = 0; /* Current argument number. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;; arg++)
+ {
+ struct object inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_object (&inner, inner_context);
+
+ /* Recognize end of list. */
+ if (inner.type == t_close)
+ {
+ op->type = t_other;
+ /* Don't bother converting "()" to "NIL". */
+ last_non_comment_line = line_number;
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return;
+ }
+
+ /* Dots are not allowed in every position.
+ But be tolerant. */
+
+ /* EOF inside list is illegal.
+ But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ if (arg == 0)
+ {
+ /* This is the function position. */
+ if (inner.type == t_symbol)
+ {
+ char *symbol_name = string_of_object (&inner);
+ int i;
+ int prefix_len;
+ void *keyword_value;
+
+ /* Omit any package name. */
+ i = inner.token->charcount;
+ while (i > 0
+ && inner.token->chars[i-1].attribute != a_pack_m)
+ i--;
+ prefix_len = i;
+
+ if (hash_find_entry (&keywords,
+ symbol_name + prefix_len,
+ strlen (symbol_name + prefix_len),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ symbol_name, strlen (symbol_name)));
+
+ free (symbol_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (argparser != NULL && inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_object (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+ }
+
+ free_object (&inner);
+ }
+
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case ')':
+ /* Tell the caller about the end of list.
+ Unmatched closing parenthesis is illegal.
+ But be tolerant. */
+ op->type = t_close;
+ last_non_comment_line = line_number;
+ return;
+
+ case ',':
+ {
+ int c = do_getc ();
+ /* The ,@ handling inside lists is wrong anyway, because
+ ,@form expands to an unknown number of elements. */
+ if (c != EOF && c != '@' && c != '.')
+ do_ungetc (c);
+ }
+ /*FALLTHROUGH*/
+ case '\'':
+ case '`':
+ {
+ struct object inner;
+
+ read_object (&inner, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case ';':
+ {
+ bool all_semicolons = true;
+
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ if (c != ';')
+ all_semicolons = false;
+ if (!all_semicolons)
+ {
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ }
+ comment_line_end (0);
+ continue;
+ }
+
+ case '"':
+ {
+ op->token = XMALLOC (struct token);
+ init_token (op->token);
+ op->line_number_at_start = line_number;
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == '"')
+ break;
+ if (c == '\\') /* syntax_single_esc */
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ }
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++].ch = c;
+ }
+ op->type = t_string;
+
+ if (extract_all)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = op->line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_object (op),
+ null_context, &pos,
+ NULL, savable_comment);
+ }
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '#':
+ /* Dispatch macro handling. */
+ {
+ int c;
+
+ for (;;)
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+ if (!(c >= '0' && c <= '9'))
+ break;
+ }
+
+ switch (c)
+ {
+ case '(':
+ case '"':
+ do_ungetc (c);
+ /*FALLTHROUGH*/
+ case '\'':
+ case ':':
+ case '.':
+ case ',':
+ case 'A': case 'a':
+ case 'C': case 'c':
+ case 'P': case 'p':
+ case 'S': case 's':
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '|':
+ {
+ int depth = 0;
+ int c;
+
+ comment_start ();
+ c = do_getc ();
+ for (;;)
+ {
+ if (c == EOF)
+ break;
+ if (c == '|')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ if (c == '#')
+ {
+ if (depth == 0)
+ {
+ comment_line_end (0);
+ break;
+ }
+ depth--;
+ comment_add ('|');
+ comment_add ('#');
+ c = do_getc ();
+ }
+ else
+ comment_add ('|');
+ }
+ else if (c == '#')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ comment_add ('#');
+ if (c == '|')
+ {
+ depth++;
+ comment_add ('|');
+ c = do_getc ();
+ }
+ }
+ else
+ {
+ /* We skip all leading white space. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ if (c == '\n')
+ {
+ comment_line_end (1);
+ comment_start ();
+ }
+ c = do_getc ();
+ }
+ }
+ if (c == EOF)
+ {
+ /* EOF not allowed here. But be tolerant. */
+ op->type = t_eof;
+ return;
+ }
+ last_comment_line = line_number;
+ continue;
+ }
+
+ case '\\':
+ {
+ struct token token;
+ struct char_syntax first;
+ first.ch = '\\';
+ first.scode = syntax_single_esc;
+ read_token (&token, &first);
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case 'B': case 'b':
+ case 'O': case 'o':
+ case 'X': case 'x':
+ case 'R': case 'r':
+ case '*':
+ {
+ struct token token;
+ read_token (&token, NULL);
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '=':
+ /* Ignore read labels. */
+ continue;
+
+ case '#':
+ /* Don't bother looking up the corresponding object. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case '+':
+ case '-':
+ /* Simply assume every feature expression is true. */
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ continue;
+ }
+
+ default:
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ /*NOTREACHED*/
+ abort ();
+ }
+
+ default:
+ /*NOTREACHED*/
+ abort ();
+ }
+
+ default:
+ /*NOTREACHED*/
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_lisp (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When read_object returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ do
+ {
+ struct object toplevel_object;
+
+ read_object (&toplevel_object, null_context);
+
+ if (toplevel_object.type == t_eof)
+ break;
+
+ free_object (&toplevel_object);
+ }
+ while (!feof (fp));
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-lisp.h b/gettext-tools/src/x-lisp.h
new file mode 100644
index 0000000..97b593d
--- /dev/null
+++ b/gettext-tools/src/x-lisp.h
@@ -0,0 +1,54 @@
+/* xgettext Lisp backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_LISP \
+ { "lisp", "Lisp" }, \
+
+#define SCANNERS_LISP \
+ { "Lisp", extract_lisp, \
+ &flag_table_lisp, &formatstring_lisp, NULL, NULL }, \
+
+/* Scan a Lisp file and add its translatable strings to mdlp. */
+extern void extract_lisp (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_lisp_extract_all (void);
+extern void x_lisp_keyword (const char *name);
+
+extern void init_flag_table_lisp (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-lua.c b/gettext-tools/src/x-lua.c
new file mode 100644
index 0000000..9fcc20d
--- /dev/null
+++ b/gettext-tools/src/x-lua.c
@@ -0,0 +1,1215 @@
+/* xgettext Lua backend.
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
+
+ This file was written by Ľubomír Remák <lubomirr@lubomirr.eu>, 2012.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Specification. */
+#include "x-lua.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+#include "po-charset.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* The Lua syntax is defined in the Lua manual section 9,
+ which can be found at
+ http://www.lua.org/manual/5.2/manual.html#9 */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+/* A hash table for keywords. */
+static hash_table keywords;
+static bool default_keywords = true;
+
+/* Set extract_all flag (gettext will extract all strings). */
+void
+x_lua_extract_all ()
+{
+ extract_all = true;
+}
+
+/* Adds a keyword. Copied from other lexers. */
+void
+x_lua_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_lua_keyword ("_");
+ x_lua_keyword ("gettext.gettext");
+ x_lua_keyword ("gettext.dgettext:2");
+ x_lua_keyword ("gettext.dcgettext:2");
+ x_lua_keyword ("gettext.ngettext:1,2");
+ x_lua_keyword ("gettext.dngettext:2,3");
+ x_lua_keyword ("gettext.dcngettext:2,3");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_lua ()
+{
+ xgettext_record_flag ("_:1:pass-lua-format");
+ xgettext_record_flag ("gettext.gettext:1:pass-lua-format");
+ xgettext_record_flag ("gettext.dgettext:2:pass-lua-format");
+ xgettext_record_flag ("gettext.dcgettext:2:pass-lua-format");
+ xgettext_record_flag ("gettext.ngettext:1:pass-lua-format");
+ xgettext_record_flag ("gettext.ngettext:2:pass-lua-format");
+ xgettext_record_flag ("gettext.dngettext:2:pass-lua-format");
+ xgettext_record_flag ("gettext.dngettext:3:pass-lua-format");
+ xgettext_record_flag ("gettext.dcngettext:2:pass-lua-format");
+ xgettext_record_flag ("gettext.dcngettext:3:pass-lua-format");
+ xgettext_record_flag ("string.format:1:lua-format");
+}
+
+/* ======================== Reading of characters. ======================== */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 1. line_number handling. */
+
+static unsigned char phase1_pushback[2];
+static int phase1_pushback_length;
+
+static int first_character = 1;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ c = phase1_pushback[--phase1_pushback_length];
+ else
+ {
+ c = getc (fp);
+
+ if (first_character)
+ {
+ first_character = 0;
+
+ /* Ignore shebang line. No pushback required in this case. */
+ if (c == '#')
+ {
+ while (c != '\n' && c != EOF)
+ c = getc (fp);
+ if (c == '\n')
+ {
+ line_number++;
+ c = getc (fp);
+ }
+ }
+ }
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+ }
+
+ if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Supports 2 characters of pushback. */
+
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+/* Accumulating comments. */
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+/* Eats characters until '\n' and adds them to the comment. */
+static void
+eat_comment_line ()
+{
+ for (;;)
+ {
+ int c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ {
+ comment_line_end (0);
+ break;
+ }
+
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+}
+
+static int
+phase2_getc ()
+{
+ int c;
+ int lineno;
+
+ c = phase1_getc ();
+
+ if (c == '-')
+ {
+ c = phase1_getc ();
+
+ if (c == '-')
+ {
+ /* It starts with '--', so it must be either a short or a long
+ comment. */
+ c = phase1_getc ();
+
+ if (c == '[')
+ {
+ c = phase1_getc ();
+
+ int esigns = 0;
+ while (c == '=')
+ {
+ esigns++;
+ c = phase1_getc ();
+ }
+
+ if (c == '[')
+ {
+ /* Long comment. */
+ bool right_bracket = false;
+ bool end = false;
+ int esigns2 = 0;
+
+ lineno = line_number;
+ comment_start ();
+ while (!end)
+ {
+ c = phase1_getc ();
+
+ if (c == EOF)
+ break;
+
+ /* Ignore leading spaces and tabs. */
+ if (buflen == 0 && (c == ' ' || c == '\t'))
+ continue;
+
+ comment_add (c);
+
+ switch (c)
+ {
+ case ']':
+ if (!right_bracket)
+ {
+ right_bracket = true;
+ esigns2 = 0;
+ }
+ else
+ {
+ if (esigns2 == esigns)
+ {
+ comment_line_end (2 + esigns);
+ end = true;
+ }
+ }
+ break;
+
+ case '=':
+ if (right_bracket)
+ esigns2++;
+ break;
+
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ lineno = line_number;
+ /* Intentionally not breaking. */
+
+ default:
+ right_bracket = false;
+ }
+ }
+ last_comment_line = lineno;
+ return ' ';
+ }
+ else
+ {
+ /* One line (short) comment, starting with '--[=...='. */
+ lineno = last_comment_line;
+ comment_start ();
+ comment_add ('[');
+ while (esigns--)
+ comment_add ('=');
+ phase1_ungetc (c);
+ eat_comment_line ();
+ last_comment_line = lineno;
+ return '\n';
+ }
+ }
+ else
+ {
+ /* One line (short) comment. */
+ lineno = line_number;
+ comment_start ();
+ phase1_ungetc (c);
+ eat_comment_line ();
+ last_comment_line = lineno;
+ return '\n';
+ }
+ }
+ else
+ {
+ /* Minus sign. */
+ phase1_ungetc (c);
+ return '-';
+ }
+ }
+ else
+ return c;
+}
+
+/* ========================== Reading of tokens. ========================== */
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_lbracket, /* [ */
+ token_type_rbracket, /* ] */
+ token_type_comma, /* , */
+ token_type_dot, /* . */
+ token_type_doubledot, /* .. */
+ token_type_operator1, /* + - * / % not # - ^ */
+ token_type_operator2, /* < > <= >= ~= == and or */
+ token_type_string,
+ token_type_number,
+ token_type_symbol,
+ token_type_other
+};
+
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ int line_number;
+};
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string)
+ drop_reference (tp->comment);
+}
+
+/* Our current string. */
+static int string_buf_length;
+static int string_buf_alloc;
+static char *string_buf;
+
+static void
+string_start ()
+{
+ string_buf_length = 0;
+}
+
+static void
+string_add (int c)
+{
+ if (string_buf_length >= string_buf_alloc)
+ {
+ string_buf_alloc = 2 * string_buf_alloc + 10;
+ string_buf = xrealloc (string_buf, string_buf_alloc);
+ }
+
+ string_buf[string_buf_length++] = c;
+}
+
+static void
+string_end ()
+{
+ string_buf[string_buf_length] = '\0';
+}
+
+
+/* We need 3 pushback tokens for string optimization. */
+static int phase3_pushback_length;
+static token_ty phase3_pushback[3];
+
+
+static void
+phase3_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = *tp;
+ }
+}
+
+static void
+phase3_get (token_ty *tp)
+{
+ int c;
+ int c2;
+ int c_start;
+
+ if (phase3_pushback_length)
+ {
+ *tp = phase3_pushback[--phase3_pushback_length];
+ return;
+ }
+
+ tp->string = NULL;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase2_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* Intentionally not breaking. */
+ case ' ':
+ case '\t':
+ case '\f':
+ continue;
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '^':
+ case '%':
+ case '#':
+ tp->type = token_type_operator1;
+ return;
+ case '<':
+ case '>':
+ case '=':
+ c2 = phase1_getc ();
+ if (c2 != '=')
+ phase1_ungetc (c2);
+ tp->type = token_type_operator2;
+ return;
+ case '~':
+ c2 = phase1_getc ();
+ if (c2 == '=')
+ {
+ tp->type = token_type_operator2;
+ return;
+ }
+ else
+ phase1_ungetc (c2);
+ continue;
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case ';':
+ tp->type = token_type_other;
+ return;
+
+ /* There are three operators beginning with a dot. '.',
+ '..' and '...'. The most useful for us is the string
+ concatenation operator ('..'). */
+ case '.':
+ c = phase1_getc ();
+ if (c == '.')
+ {
+ c = phase1_getc ();
+ if (c == '.')
+ {
+ tp->type = token_type_other;
+ return;
+ }
+ else
+ {
+ phase1_ungetc (c);
+ tp->type = token_type_doubledot;
+ return;
+ }
+ }
+ else if (c >= '0' && c <= '9')
+ {
+ /* It's a number. We aren't interested in the actual
+ numeric value, so ignore the dot and let next
+ iteration eat the number. */
+ phase1_ungetc (c);
+ continue;
+ }
+ else
+ {
+ phase1_ungetc (c);
+ tp->type = token_type_dot;
+ return;
+ }
+
+ case '"':
+ case '\'':
+ c_start = c;
+ string_start ();
+
+ for (;;)
+ {
+ /* We need unprocessed characters from phase 1. */
+ c = phase1_getc ();
+
+ /* We got '\', this is probably an escape sequence. */
+ if (c == '\\')
+ {
+ c = phase1_getc ();
+ switch (c)
+ {
+ case 'a':
+ string_add ('\a');
+ break;
+ case 'b':
+ string_add ('\b');
+ break;
+ case 'f':
+ string_add ('\f');
+ break;
+ case 'n':
+ string_add ('\n');
+ break;
+ case 'r':
+ string_add ('\r');
+ break;
+ case 't':
+ string_add ('\t');
+ break;
+ case 'v':
+ string_add ('\v');
+ break;
+ case 'x':
+ {
+ int num = 0;
+ int i = 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ c = phase1_getc ();
+ if (c >= '0' && c <= '9')
+ num += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ num += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ num += c - 'A' + 10;
+ else
+ {
+ phase1_ungetc (c);
+ break;
+ }
+
+ if (i == 0)
+ num *= 16;
+ }
+
+ if (i == 2)
+ string_add (num);
+ }
+
+ break;
+ case 'z':
+ /* Ignore the following whitespace. */
+ do
+ {
+ c = phase1_getc ();
+ }
+ while (c == ' ' || c == '\n' || c == '\t' || c == '\r'
+ || c == '\f' || c == '\v');
+
+ phase1_ungetc (c);
+
+ break;
+ default:
+ /* Check if it's a '\ddd' sequence. */
+ if (c >= '0' && c <= '9')
+ {
+ int num = 0;
+ int i = 0;
+
+ while (c >= '0' && c <= '9' && i < 3)
+ {
+ num *= 10;
+ num += (c - '0');
+ c = phase1_getc ();
+ i++;
+ }
+
+ /* The last read character is either a
+ non-number or another number after our
+ '\ddd' sequence. We need to ungetc it. */
+ phase1_ungetc (c);
+
+ /* The sequence number is too big, this
+ causes a lexical error. Ignore it. */
+ if (num < 256)
+ string_add (num);
+ }
+ else
+ string_add (c);
+ }
+ }
+ else if (c == c_start || c == EOF || c == '\n')
+ {
+ /* End of string. */
+ string_end ();
+ tp->string = xstrdup (string_buf);
+ tp->comment = add_reference (savable_comment);
+ tp->type = token_type_string;
+ return;
+ }
+ else
+ string_add (c);
+ }
+ break;
+
+ case '[':
+ c = phase1_getc ();
+
+ /* Count the number of equal signs. */
+ int esigns = 0;
+ while (c == '=')
+ {
+ esigns++;
+ c = phase1_getc ();
+ }
+
+ if (c != '[')
+ {
+ /* We did not find what we were looking for, ungetc it. */
+ phase1_ungetc (c);
+ if (esigns == 0)
+ {
+ /* Our current character isn't '[' and we got 0 equal
+ signs, so the first '[' must have been a left
+ bracket. */
+ tp->type = token_type_lbracket;
+ return;
+ }
+ else
+ /* Lexical error, ignore it. */
+ continue;
+ }
+
+ string_start ();
+
+ for (;;)
+ {
+ c = phase1_getc ();
+
+ if (c == ']')
+ {
+ c = phase1_getc ();
+
+ /* Count the number of equal signs. */
+ int esigns2 = 0;
+ while (c == '=')
+ {
+ esigns2++;
+ c = phase1_getc ();
+ }
+
+ if (c == ']' && esigns == esigns2)
+ {
+ /* We got ']==...==]', where the number of equal
+ signs matches the number of equal signs in
+ the opening bracket. */
+ string_end ();
+ tp->string = xstrdup (string_buf);
+ tp->comment = add_reference (savable_comment);
+ tp->type = token_type_string;
+ return;
+ }
+ else
+ {
+ /* Otherwise we got either ']==' garbage or
+ ']==...==]' with a different number of equal
+ signs.
+
+ Add ']' and equal signs to the string, and
+ ungetc the current character, because the
+ second ']' might be a part of another closing
+ long bracket, e.g. '==]===]'. */
+ phase1_ungetc (c);
+
+ string_add (']');
+ while (esigns2--)
+ string_add ('=');
+ }
+ }
+ else
+ {
+ if (c == EOF)
+ {
+ string_end ();
+ tp->string = xstrdup (string_buf);
+ tp->comment = add_reference (savable_comment);
+ tp->type = token_type_string;
+ return;
+ }
+ else
+ string_add (c);
+ }
+ }
+ break;
+
+ case ']':
+ tp->type = token_type_rbracket;
+ return;
+
+ default:
+ if (c >= '0' && c <= '9')
+ {
+ while (c >= '0' && c <= '9')
+ c = phase1_getc ();
+
+ if (c == '.')
+ {
+ c = phase1_getc ();
+ while (c >= '0' && c <= '9')
+ c = phase1_getc ();
+ }
+
+ if (c == 'e' || c == 'E')
+ {
+ if (c == '+' || c == '-')
+ c = phase1_getc ();
+ while (c >= '0' && c <= '9')
+ c = phase1_getc ();
+ }
+
+ phase1_ungetc (c);
+
+ tp->type = token_type_number;
+ return;
+ }
+ else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+ || c == '_')
+ {
+ string_start ();
+ while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+ || c == '_' || (c >= '0' && c <= '9'))
+ {
+ string_add (c);
+ c = phase1_getc ();
+ }
+ string_end ();
+ phase1_ungetc (c);
+
+ if (strcmp (string_buf, "not") == 0)
+ tp->type = token_type_operator1;
+ else if (strcmp (string_buf, "and") == 0)
+ tp->type = token_type_operator2;
+ else if (strcmp (string_buf, "or") == 0)
+ tp->type = token_type_operator2;
+ else
+ {
+ tp->string = xstrdup (string_buf);
+ tp->type = token_type_symbol;
+ }
+ return;
+ }
+ else
+ tp->type = token_type_other;
+ }
+ }
+}
+
+/* String and symbol concatenation. */
+
+static token_type_ty phase4_last;
+
+/* We need 3 pushback tokens for string and symbol concatenation. */
+static int phase4_pushback_length;
+static token_ty phase4_pushback[3];
+
+static void
+phase4_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase4_pushback_length == SIZEOF (phase4_pushback))
+ abort ();
+ phase4_pushback[phase4_pushback_length++] = *tp;
+ }
+}
+
+static void
+phase4_get (token_ty *tp)
+{
+ if (phase4_pushback_length)
+ {
+ *tp = phase4_pushback[--phase4_pushback_length];
+ phase4_last = tp->type;
+ return;
+ }
+
+ phase3_get (tp);
+ if (tp->type == token_type_string
+ && !(phase4_last == token_type_operator1
+ || phase4_last == token_type_dot
+ || phase4_last == token_type_symbol
+ || phase4_last == token_type_doubledot
+ || phase4_last == token_type_rparen))
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase3_get (&token2);
+ if (token2.type == token_type_doubledot)
+ {
+ token_ty token3;
+
+ phase3_get (&token3);
+ if (token3.type == token_type_string)
+ {
+ token_ty token_after;
+
+ phase3_get (&token_after);
+ if (token_after.type != token_type_operator1)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ phase3_unget (&token_after);
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase3_unget (&token_after);
+ }
+ phase3_unget (&token3);
+ }
+ phase3_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+ phase4_last = tp->type;
+}
+
+static void
+phase5_get (token_ty *tp)
+{
+ phase4_get (tp);
+
+ /* Combine symbol1 . ... . symbolN to a single strings, so that
+ we can recognize function calls like
+ gettext.gettext. The information present for
+ symbolI.....symbolN has precedence over the information for
+ symbolJ.....symbolN with J > I. */
+ if (tp->type == token_type_symbol)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase4_get (&token2);
+ if (token2.type == token_type_dot)
+ {
+ token_ty token3;
+
+ phase4_get (&token3);
+ if (token3.type == token_type_symbol)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + 1 + addend_len + 1);
+ sum[sum_len] = '.';
+ memcpy (sum + sum_len + 1, addend, addend_len + 1);
+ sum_len += 1 + addend_len;
+
+ free_token (&token2);
+ free_token (&token3);
+ continue;
+ }
+ phase4_unget (&token3);
+ }
+ phase4_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+}
+
+static void
+x_lua_lex (token_ty *tok)
+{
+ phase5_get (tok);
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid Lua program, and leave the complaints about the
+ grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ keyword msgid
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+/* Extract messages until the next balanced closing parenthesis or bracket.
+ Extracted messages are added to MLP.
+ DELIM can be either token_type_rparen or token_type_rbracket, or
+ token_type_eof to accept both.
+ Return true upon eof, false upon closing parenthesis or bracket. */
+static bool
+extract_balanced (message_list_ty *mlp, token_type_ty delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_lua_lex (&token);
+
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_balanced (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ break;
+
+ case token_type_rparen:
+ if (delim == token_type_rparen || delim == token_type_eof)
+ {
+ arglist_parser_done (argparser, arg);
+ return false;
+ }
+
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbracket:
+ if (extract_balanced (mlp, token_type_rbracket,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ break;
+
+ case token_type_rbracket:
+ if (delim == token_type_rbracket || delim == token_type_eof)
+ {
+ arglist_parser_done (argparser, arg);
+ return false;
+ }
+
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ arglist_parser_done (argparser, arg);
+ return true;
+
+ case token_type_string:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ {
+ /* A string immediately after a symbol means a function call. */
+ if (state)
+ {
+ struct arglist_parser *tmp_argparser;
+ tmp_argparser = arglist_parser_alloc (mlp, next_shapes);
+
+ arglist_parser_remember (tmp_argparser, 1, token.string,
+ inner_context, pos.file_name,
+ pos.line_number, token.comment);
+ arglist_parser_done (tmp_argparser, 1);
+ }
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context, pos.file_name,
+ pos.line_number, token.comment);
+ }
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_dot:
+ case token_type_doubledot:
+ case token_type_operator1:
+ case token_type_operator2:
+ case token_type_number:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+void
+extract_lua (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_balanced (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-lua.h b/gettext-tools/src/x-lua.h
new file mode 100644
index 0000000..3161638
--- /dev/null
+++ b/gettext-tools/src/x-lua.h
@@ -0,0 +1,48 @@
+/* xgettext Lua backend.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+ Written by Ľubomír Remák <lubomirrk@lubomirr.eu>, 2011
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define EXTENSIONS_LUA \
+ { "lua", "Lua" }, \
+
+#define SCANNERS_LUA \
+ { "Lua", extract_lua, \
+ &flag_table_lua, &formatstring_lua, NULL, NULL }, \
+
+ /* Scan a Lua file and add its translatable strings to mdlp. */
+ extern void extract_lua (FILE * fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty * flag_table,
+ msgdomain_list_ty * mdlp);
+
+ extern void x_lua_keyword (const char *keyword);
+ extern void x_lua_extract_all (void);
+
+ extern void init_flag_table_lua (void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-perl.c b/gettext-tools/src/x-perl.c
new file mode 100644
index 0000000..571f6de
--- /dev/null
+++ b/gettext-tools/src/x-perl.c
@@ -0,0 +1,3592 @@
+/* xgettext Perl backend.
+ Copyright (C) 2002-2010 Free Software Foundation, Inc.
+
+ This file was written by Guido Flohr <guido@imperia.net>, 2002-2010.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-perl.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "uniname.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+/* The Perl syntax is defined in perlsyn.pod. Try the command
+ "man perlsyn" or "perldoc perlsyn".
+ Also, the syntax after the 'sub' keyword is specified in perlsub.pod.
+ Try the command "man perlsub" or "perldoc perlsub".
+ Perl 5.10 has new operators '//' and '//=', see
+ <http://perldoc.perl.org/perldelta.html#Defined-or-operator>. */
+
+#define DEBUG_PERL 0
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_perl_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_perl_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_perl_keyword ("gettext");
+ x_perl_keyword ("%gettext");
+ x_perl_keyword ("$gettext");
+ x_perl_keyword ("dgettext:2");
+ x_perl_keyword ("dcgettext:2");
+ x_perl_keyword ("ngettext:1,2");
+ x_perl_keyword ("dngettext:2,3");
+ x_perl_keyword ("dcngettext:2,3");
+ x_perl_keyword ("gettext_noop");
+#if 0
+ x_perl_keyword ("__");
+ x_perl_keyword ("$__");
+ x_perl_keyword ("%__");
+ x_perl_keyword ("__x");
+ x_perl_keyword ("__n:1,2");
+ x_perl_keyword ("__nx:1,2");
+ x_perl_keyword ("__xn:1,2");
+ x_perl_keyword ("N__");
+#endif
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_perl ()
+{
+ xgettext_record_flag ("gettext:1:pass-perl-format");
+ xgettext_record_flag ("gettext:1:pass-perl-brace-format");
+ xgettext_record_flag ("%gettext:1:pass-perl-format");
+ xgettext_record_flag ("%gettext:1:pass-perl-brace-format");
+ xgettext_record_flag ("$gettext:1:pass-perl-format");
+ xgettext_record_flag ("$gettext:1:pass-perl-brace-format");
+ xgettext_record_flag ("dgettext:2:pass-perl-format");
+ xgettext_record_flag ("dgettext:2:pass-perl-brace-format");
+ xgettext_record_flag ("dcgettext:2:pass-perl-format");
+ xgettext_record_flag ("dcgettext:2:pass-perl-brace-format");
+ xgettext_record_flag ("ngettext:1:pass-perl-format");
+ xgettext_record_flag ("ngettext:2:pass-perl-format");
+ xgettext_record_flag ("ngettext:1:pass-perl-brace-format");
+ xgettext_record_flag ("ngettext:2:pass-perl-brace-format");
+ xgettext_record_flag ("dngettext:2:pass-perl-format");
+ xgettext_record_flag ("dngettext:3:pass-perl-format");
+ xgettext_record_flag ("dngettext:2:pass-perl-brace-format");
+ xgettext_record_flag ("dngettext:3:pass-perl-brace-format");
+ xgettext_record_flag ("dcngettext:2:pass-perl-format");
+ xgettext_record_flag ("dcngettext:3:pass-perl-format");
+ xgettext_record_flag ("dcngettext:2:pass-perl-brace-format");
+ xgettext_record_flag ("dcngettext:3:pass-perl-brace-format");
+ xgettext_record_flag ("gettext_noop:1:pass-perl-format");
+ xgettext_record_flag ("gettext_noop:1:pass-perl-brace-format");
+ xgettext_record_flag ("printf:1:perl-format"); /* argument 1 or 2 ?? */
+ xgettext_record_flag ("sprintf:1:perl-format");
+#if 0
+ xgettext_record_flag ("__:1:pass-perl-format");
+ xgettext_record_flag ("__:1:pass-perl-brace-format");
+ xgettext_record_flag ("%__:1:pass-perl-format");
+ xgettext_record_flag ("%__:1:pass-perl-brace-format");
+ xgettext_record_flag ("$__:1:pass-perl-format");
+ xgettext_record_flag ("$__:1:pass-perl-brace-format");
+ xgettext_record_flag ("__x:1:perl-brace-format");
+ xgettext_record_flag ("__n:1:pass-perl-format");
+ xgettext_record_flag ("__n:2:pass-perl-format");
+ xgettext_record_flag ("__n:1:pass-perl-brace-format");
+ xgettext_record_flag ("__n:2:pass-perl-brace-format");
+ xgettext_record_flag ("__nx:1:perl-brace-format");
+ xgettext_record_flag ("__nx:2:perl-brace-format");
+ xgettext_record_flag ("__xn:1:perl-brace-format");
+ xgettext_record_flag ("__xn:2:perl-brace-format");
+ xgettext_record_flag ("N__:1:pass-perl-format");
+ xgettext_record_flag ("N__:1:pass-perl-brace-format");
+#endif
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+/* The current line buffer. */
+static char *linebuf;
+
+/* The size of the current line. */
+static int linesize;
+
+/* The position in the current line. */
+static int linepos;
+
+/* The size of the input buffer. */
+static size_t linebuf_size;
+
+/* Number of lines eaten for here documents. */
+static int eaten_here;
+
+/* Paranoia: EOF marker for __END__ or __DATA__. */
+static bool end_of_file;
+
+
+/* 1. line_number handling. */
+
+/* Returns the next character from the input stream or EOF. */
+static int
+phase1_getc ()
+{
+ line_number += eaten_here;
+ eaten_here = 0;
+
+ if (end_of_file)
+ return EOF;
+
+ if (linepos >= linesize)
+ {
+ linesize = getline (&linebuf, &linebuf_size, fp);
+
+ if (linesize < 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ end_of_file = true;
+ return EOF;
+ }
+
+ linepos = 0;
+ ++line_number;
+
+ /* Undosify. This is important for catching the end of <<EOF and
+ <<'EOF'. We could rely on stdio doing this for us but
+ it is not uncommon to to come across Perl scripts with CRLF
+ newline conventions on systems that do not follow this
+ convention. */
+ if (linesize >= 2 && linebuf[linesize - 1] == '\n'
+ && linebuf[linesize - 2] == '\r')
+ {
+ linebuf[linesize - 2] = '\n';
+ linebuf[linesize - 1] = '\0';
+ --linesize;
+ }
+ }
+
+ return linebuf[linepos++];
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (linepos == 0)
+ /* Attempt to ungetc across line boundary. Shouldn't happen.
+ No two phase1_ungetc calls are permitted in a row. */
+ abort ();
+
+ --linepos;
+ }
+}
+
+/* Read a here document and return its contents.
+ The delimiter is an UTF-8 encoded string; the resulting string is UTF-8
+ encoded as well. */
+
+static char *
+get_here_document (const char *delimiter)
+{
+ /* Accumulator for the entire here document, including a NUL byte
+ at the end. */
+ static char *buffer;
+ static size_t bufmax = 0;
+ size_t bufpos = 0;
+ /* Current line being appended. */
+ static char *my_linebuf = NULL;
+ static size_t my_linebuf_size = 0;
+
+ /* Allocate the initial buffer. Later on, bufmax > 0. */
+ if (bufmax == 0)
+ {
+ buffer = XNMALLOC (1, char);
+ buffer[0] = '\0';
+ bufmax = 1;
+ }
+
+ for (;;)
+ {
+ int read_bytes = getline (&my_linebuf, &my_linebuf_size, fp);
+ char *my_line_utf8;
+ bool chomp;
+
+ if (read_bytes < 0)
+ {
+ if (ferror (fp))
+ {
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ }
+ else
+ {
+ error_with_progname = false;
+ error (EXIT_SUCCESS, 0, _("\
+%s:%d: can't find string terminator \"%s\" anywhere before EOF"),
+ real_file_name, line_number, delimiter);
+ error_with_progname = true;
+
+ break;
+ }
+ }
+
+ ++eaten_here;
+
+ /* Convert to UTF-8. */
+ my_line_utf8 =
+ from_current_source_encoding (my_linebuf, lc_string, logical_file_name,
+ line_number + eaten_here);
+ if (my_line_utf8 != my_linebuf)
+ {
+ if (strlen (my_line_utf8) >= my_linebuf_size)
+ {
+ my_linebuf_size = strlen (my_line_utf8) + 1;
+ my_linebuf = xrealloc (my_linebuf, my_linebuf_size);
+ }
+ strcpy (my_linebuf, my_line_utf8);
+ free (my_line_utf8);
+ }
+
+ /* Undosify. This is important for catching the end of <<EOF and
+ <<'EOF'. We could rely on stdio doing this for us but you
+ it is not uncommon to to come across Perl scripts with CRLF
+ newline conventions on systems that do not follow this
+ convention. */
+ if (read_bytes >= 2 && my_linebuf[read_bytes - 1] == '\n'
+ && my_linebuf[read_bytes - 2] == '\r')
+ {
+ my_linebuf[read_bytes - 2] = '\n';
+ my_linebuf[read_bytes - 1] = '\0';
+ --read_bytes;
+ }
+
+ /* Temporarily remove the trailing newline from my_linebuf. */
+ chomp = false;
+ if (read_bytes >= 1 && my_linebuf[read_bytes - 1] == '\n')
+ {
+ chomp = true;
+ my_linebuf[read_bytes - 1] = '\0';
+ }
+
+ /* See whether this line terminates the here document. */
+ if (strcmp (my_linebuf, delimiter) == 0)
+ break;
+
+ /* Add back the trailing newline to my_linebuf. */
+ if (chomp)
+ my_linebuf[read_bytes - 1] = '\n';
+
+ /* Ensure room for read_bytes + 1 bytes. */
+ if (bufpos + read_bytes >= bufmax)
+ {
+ do
+ bufmax = 2 * bufmax + 10;
+ while (bufpos + read_bytes >= bufmax);
+ buffer = xrealloc (buffer, bufmax);
+ }
+ /* Append this line to the accumulator. */
+ strcpy (buffer + bufpos, my_linebuf);
+ bufpos += read_bytes;
+ }
+
+ /* Done accumulating the here document. */
+ return xstrdup (buffer);
+}
+
+/* Skips pod sections. */
+static void
+skip_pod ()
+{
+ line_number += eaten_here;
+ eaten_here = 0;
+ linepos = 0;
+
+ for (;;)
+ {
+ linesize = getline (&linebuf, &linebuf_size, fp);
+
+ if (linesize < 0)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return;
+ }
+
+ ++line_number;
+
+ if (strncmp ("=cut", linebuf, 4) == 0)
+ {
+ /* Force reading of a new line on next call to phase1_getc(). */
+ linepos = linesize;
+ return;
+ }
+ }
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* 2. Replace each comment that is not inside a string literal or regular
+ expression with a newline character. We need to remember the comment
+ for later, because it may be attached to a keyword string. */
+
+static int
+phase2_getc ()
+{
+ static char *buffer;
+ static size_t bufmax;
+ size_t buflen;
+ int lineno;
+ int c;
+ char *utf8_string;
+
+ c = phase1_getc ();
+ if (c == '#')
+ {
+ buflen = 0;
+ lineno = line_number;
+ /* Skip leading whitespace. */
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c != ' ' && c != '\t' && c != '\r' && c != '\f')
+ {
+ phase1_ungetc (c);
+ break;
+ }
+ }
+ /* Accumulate the comment. */
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ /* Convert it to UTF-8. */
+ utf8_string =
+ from_current_source_encoding (buffer, lc_comment, logical_file_name,
+ lineno);
+ /* Save it until we encounter the corresponding string. */
+ savable_comment_add (utf8_string);
+ last_comment_line = lineno;
+ }
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ phase1_ungetc (c);
+}
+
+/* Whitespace recognition. */
+
+#define case_whitespace \
+ case ' ': case '\t': case '\r': case '\n': case '\f'
+
+static inline bool
+is_whitespace (int c)
+{
+ return (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f');
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_fat_comma, /* => */
+ token_type_dereference, /* -> */
+ token_type_semicolon, /* ; */
+ token_type_lbrace, /* { */
+ token_type_rbrace, /* } */
+ token_type_lbracket, /* [ */
+ token_type_rbracket, /* ] */
+ token_type_string, /* quote-like */
+ token_type_number, /* starting with a digit o dot */
+ token_type_named_op, /* if, unless, while, ... */
+ token_type_variable, /* $... */
+ token_type_object, /* A dereferenced variable, maybe a blessed
+ object. */
+ token_type_symbol, /* symbol, number */
+ token_type_regex_op, /* s, tr, y, m. */
+ token_type_dot, /* . */
+ token_type_other, /* regexp, misc. operator */
+ /* The following are not really token types, but variants used by
+ the parser. */
+ token_type_keyword_symbol /* keyword symbol */
+};
+typedef enum token_type_ty token_type_ty;
+
+/* Subtypes for strings, important for interpolation. */
+enum string_type_ty
+{
+ string_type_verbatim, /* "<<'EOF'", "m'...'", "s'...''...'",
+ "tr/.../.../", "y/.../.../". */
+ string_type_q, /* "'..'", "q/.../". */
+ string_type_qq, /* '"..."', "`...`", "qq/.../", "qx/.../",
+ "<file*glob>". */
+ string_type_qr /* Not supported. */
+};
+
+/* Subtypes for symbols, important for dollar interpretation. */
+enum symbol_type_ty
+{
+ symbol_type_none, /* Nothing special. */
+ symbol_type_sub, /* 'sub'. */
+ symbol_type_function /* Function name after 'sub'. */
+};
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ token_type_ty last_type;
+ int sub_type; /* for token_type_string, token_type_symbol */
+ char *string; /* for: in encoding:
+ token_type_named_op ASCII
+ token_type_string UTF-8
+ token_type_symbol ASCII
+ token_type_variable global_source_encoding
+ token_type_object global_source_encoding
+ */
+ refcounted_string_list_ty *comment; /* for token_type_string */
+ int line_number;
+};
+
+#if DEBUG_PERL
+static const char *
+token2string (const token_ty *token)
+{
+ switch (token->type)
+ {
+ case token_type_eof:
+ return "token_type_eof";
+ case token_type_lparen:
+ return "token_type_lparen";
+ case token_type_rparen:
+ return "token_type_rparen";
+ case token_type_comma:
+ return "token_type_comma";
+ case token_type_fat_comma:
+ return "token_type_fat_comma";
+ case token_type_dereference:
+ return "token_type_dereference";
+ case token_type_semicolon:
+ return "token_type_semicolon";
+ case token_type_lbrace:
+ return "token_type_lbrace";
+ case token_type_rbrace:
+ return "token_type_rbrace";
+ case token_type_lbracket:
+ return "token_type_lbracket";
+ case token_type_rbracket:
+ return "token_type_rbracket";
+ case token_type_string:
+ return "token_type_string";
+ case token_type_number:
+ return "token type number";
+ case token_type_named_op:
+ return "token_type_named_op";
+ case token_type_variable:
+ return "token_type_variable";
+ case token_type_object:
+ return "token_type_object";
+ case token_type_symbol:
+ return "token_type_symbol";
+ case token_type_regex_op:
+ return "token_type_regex_op";
+ case token_type_dot:
+ return "token_type_dot";
+ case token_type_other:
+ return "token_type_other";
+ default:
+ return "unknown";
+ }
+}
+#endif
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ switch (tp->type)
+ {
+ case token_type_named_op:
+ case token_type_string:
+ case token_type_symbol:
+ case token_type_variable:
+ case token_type_object:
+ free (tp->string);
+ break;
+ default:
+ break;
+ }
+ if (tp->type == token_type_string)
+ drop_reference (tp->comment);
+ free (tp);
+}
+
+/* Pass 1 of extracting quotes: Find the end of the string, regardless
+ of the semantics of the construct. Return the complete string,
+ including the starting and the trailing delimiter, with backslashes
+ removed where appropriate. */
+static char *
+extract_quotelike_pass1 (int delim)
+{
+ /* This function is called recursively. No way to allocate stuff
+ statically. Also alloca() is inappropriate due to limited stack
+ size on some platforms. So we use malloc(). */
+ int bufmax = 10;
+ char *buffer = XNMALLOC (bufmax, char);
+ int bufpos = 0;
+ bool nested = true;
+ int counter_delim;
+
+ buffer[bufpos++] = delim;
+
+ /* Find the closing delimiter. */
+ switch (delim)
+ {
+ case '(':
+ counter_delim = ')';
+ break;
+ case '{':
+ counter_delim = '}';
+ break;
+ case '[':
+ counter_delim = ']';
+ break;
+ case '<':
+ counter_delim = '>';
+ break;
+ default: /* "..." or '...' or |...| etc. */
+ nested = false;
+ counter_delim = delim;
+ break;
+ }
+
+ for (;;)
+ {
+ int c = phase1_getc ();
+
+ /* This round can produce 1 or 2 bytes. Ensure room for 2 bytes. */
+ if (bufpos + 2 > bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+
+ if (c == counter_delim || c == EOF)
+ {
+ buffer[bufpos++] = counter_delim; /* will be stripped off later */
+ buffer[bufpos++] = '\0';
+#if DEBUG_PERL
+ fprintf (stderr, "PASS1: %s\n", buffer);
+#endif
+ return buffer;
+ }
+
+ if (nested && c == delim)
+ {
+ char *inner = extract_quotelike_pass1 (delim);
+ size_t len = strlen (inner);
+
+ /* Ensure room for len + 1 bytes. */
+ if (bufpos + len >= bufmax)
+ {
+ do
+ bufmax = 2 * bufmax + 10;
+ while (bufpos + len >= bufmax);
+ buffer = xrealloc (buffer, bufmax);
+ }
+ strcpy (buffer + bufpos, inner);
+ free (inner);
+ bufpos += len;
+ }
+ else if (c == '\\')
+ {
+ c = phase1_getc ();
+ if (c == '\\')
+ {
+ buffer[bufpos++] = '\\';
+ buffer[bufpos++] = '\\';
+ }
+ else if (c == delim || c == counter_delim)
+ {
+ /* This is pass2 in Perl. */
+ buffer[bufpos++] = c;
+ }
+ else
+ {
+ buffer[bufpos++] = '\\';
+ phase1_ungetc (c);
+ }
+ }
+ else
+ {
+ buffer[bufpos++] = c;
+ }
+ }
+}
+
+/* Like extract_quotelike_pass1, but return the complete string in UTF-8
+ encoding. */
+static char *
+extract_quotelike_pass1_utf8 (int delim)
+{
+ char *string = extract_quotelike_pass1 (delim);
+ char *utf8_string =
+ from_current_source_encoding (string, lc_string, logical_file_name,
+ line_number);
+ if (utf8_string != string)
+ free (string);
+ return utf8_string;
+}
+
+
+/* ========= Reading of tokens and commands. Extracting strings. ========= */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* Forward declaration of local functions. */
+static void interpolate_keywords (message_list_ty *mlp, const char *string,
+ int lineno);
+static token_ty *x_perl_lex (message_list_ty *mlp);
+static void x_perl_unlex (token_ty *tp);
+static bool extract_balanced (message_list_ty *mlp,
+ token_type_ty delim, bool eat_delim,
+ bool comma_delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ int arg, struct arglist_parser *argparser);
+
+
+/* Extract an unsigned hexadecimal number from STRING, considering at
+ most LEN bytes and place the result in *RESULT. Returns a pointer
+ to the first character past the hexadecimal number. */
+static const char *
+extract_hex (const char *string, size_t len, unsigned int *result)
+{
+ size_t i;
+
+ *result = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ char c = string[i];
+ int number;
+
+ if (c >= 'A' && c <= 'F')
+ number = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ number = c - 'a' + 10;
+ else if (c >= '0' && c <= '9')
+ number = c - '0';
+ else
+ break;
+
+ *result <<= 4;
+ *result |= number;
+ }
+
+ return string + i;
+}
+
+/* Extract an unsigned octal number from STRING, considering at
+ most LEN bytes and place the result in *RESULT. Returns a pointer
+ to the first character past the octal number. */
+static const char *
+extract_oct (const char *string, size_t len, unsigned int *result)
+{
+ size_t i;
+
+ *result = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ char c = string[i];
+ int number;
+
+ if (c >= '0' && c <= '7')
+ number = c - '0';
+ else
+ break;
+
+ *result <<= 3;
+ *result |= number;
+ }
+
+ return string + i;
+}
+
+/* Extract the various quotelike constructs except for <<EOF. See the
+ section "Gory details of parsing quoted constructs" in perlop.pod.
+ Return the resulting token in *tp; tp->type == token_type_string. */
+static void
+extract_quotelike (token_ty *tp, int delim)
+{
+ char *string = extract_quotelike_pass1_utf8 (delim);
+ size_t len = strlen (string);
+
+ tp->type = token_type_string;
+ /* Take the string without the delimiters at the start and at the end. */
+ if (!(len >= 2))
+ abort ();
+ string[len - 1] = '\0';
+ tp->string = xstrdup (string + 1);
+ free (string);
+ tp->comment = add_reference (savable_comment);
+}
+
+/* Extract the quotelike constructs with double delimiters, like
+ s/[SEARCH]/[REPLACE]/. This function does not eat up trailing
+ modifiers (left to the caller).
+ Return the resulting token in *tp; tp->type == token_type_regex_op. */
+static void
+extract_triple_quotelike (message_list_ty *mlp, token_ty *tp, int delim,
+ bool interpolate)
+{
+ char *string;
+
+ tp->type = token_type_regex_op;
+
+ string = extract_quotelike_pass1_utf8 (delim);
+ if (interpolate)
+ interpolate_keywords (mlp, string, line_number);
+ free (string);
+
+ if (delim == '(' || delim == '<' || delim == '{' || delim == '[')
+ {
+ /* The delimiter for the second string can be different, e.g.
+ s{SEARCH}{REPLACE} or s{SEARCH}/REPLACE/. See "man perlrequick". */
+ delim = phase1_getc ();
+ while (is_whitespace (delim))
+ {
+ /* The hash-sign is not a valid delimiter after whitespace, ergo
+ use phase2_getc() and not phase1_getc() now. */
+ delim = phase2_getc ();
+ }
+ }
+ string = extract_quotelike_pass1_utf8 (delim);
+ if (interpolate)
+ interpolate_keywords (mlp, string, line_number);
+ free (string);
+}
+
+/* Perform pass 3 of quotelike extraction (interpolation).
+ *tp is a token of type token_type_string.
+ This function replaces tp->string.
+ This function does not access tp->comment. */
+/* FIXME: Currently may writes null-bytes into the string. */
+static void
+extract_quotelike_pass3 (token_ty *tp, int error_level)
+{
+ static char *buffer;
+ static int bufmax = 0;
+ int bufpos = 0;
+ const char *crs;
+ bool uppercase;
+ bool lowercase;
+ bool quotemeta;
+
+#if DEBUG_PERL
+ switch (tp->sub_type)
+ {
+ case string_type_verbatim:
+ fprintf (stderr, "Interpolating string_type_verbatim:\n");
+ break;
+ case string_type_q:
+ fprintf (stderr, "Interpolating string_type_q:\n");
+ break;
+ case string_type_qq:
+ fprintf (stderr, "Interpolating string_type_qq:\n");
+ break;
+ case string_type_qr:
+ fprintf (stderr, "Interpolating string_type_qr:\n");
+ break;
+ }
+ fprintf (stderr, "%s\n", tp->string);
+ if (tp->sub_type == string_type_verbatim)
+ fprintf (stderr, "---> %s\n", tp->string);
+#endif
+
+ if (tp->sub_type == string_type_verbatim)
+ return;
+
+ /* Loop over tp->string, accumulating the expansion in buffer. */
+ crs = tp->string;
+ uppercase = false;
+ lowercase = false;
+ quotemeta = false;
+ while (*crs)
+ {
+ bool backslashed;
+
+ /* Ensure room for 7 bytes, 6 (multi-)bytes plus a leading backslash
+ if \Q modifier is present. */
+ if (bufpos + 7 > bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+
+ if (tp->sub_type == string_type_q)
+ {
+ switch (*crs)
+ {
+ case '\\':
+ if (crs[1] == '\\')
+ {
+ crs += 2;
+ buffer[bufpos++] = '\\';
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ buffer[bufpos++] = *crs++;
+ break;
+ }
+ continue;
+ }
+
+ /* We only get here for double-quoted strings or regular expressions.
+ Unescape escape sequences. */
+ if (*crs == '\\')
+ {
+ switch (crs[1])
+ {
+ case 't':
+ crs += 2;
+ buffer[bufpos++] = '\t';
+ continue;
+ case 'n':
+ crs += 2;
+ buffer[bufpos++] = '\n';
+ continue;
+ case 'r':
+ crs += 2;
+ buffer[bufpos++] = '\r';
+ continue;
+ case 'f':
+ crs += 2;
+ buffer[bufpos++] = '\f';
+ continue;
+ case 'b':
+ crs += 2;
+ buffer[bufpos++] = '\b';
+ continue;
+ case 'a':
+ crs += 2;
+ buffer[bufpos++] = '\a';
+ continue;
+ case 'e':
+ crs += 2;
+ buffer[bufpos++] = 0x1b;
+ continue;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ unsigned int oct_number;
+ int length;
+
+ crs = extract_oct (crs + 1, 3, &oct_number);
+
+ /* FIXME: If one of the variables UPPERCASE or LOWERCASE is
+ true, the character should be converted to its uppercase
+ resp. lowercase equivalent. I don't know if the necessary
+ facilities are already included in gettext. For US-Ascii
+ the conversion can be already be done, however. */
+ if (uppercase && oct_number >= 'a' && oct_number <= 'z')
+ {
+ oct_number = oct_number - 'a' + 'A';
+ }
+ else if (lowercase && oct_number >= 'A' && oct_number <= 'Z')
+ {
+ oct_number = oct_number - 'A' + 'a';
+ }
+
+
+ /* Yes, octal escape sequences in the range 0x100..0x1ff are
+ valid. */
+ length = u8_uctomb ((unsigned char *) (buffer + bufpos),
+ oct_number, 2);
+ if (length > 0)
+ bufpos += length;
+ }
+ continue;
+ case 'x':
+ {
+ unsigned int hex_number = 0;
+ int length;
+
+ crs += 2;
+ if (*crs == '{')
+ {
+ const char *end = strchr (crs, '}');
+ if (end == NULL)
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: missing right brace on \\x{HEXNUMBER}"), real_file_name, line_number);
+ error_with_progname = true;
+ ++crs;
+ continue;
+ }
+ else
+ {
+ ++crs;
+ (void) extract_hex (crs, end - crs, &hex_number);
+ crs = end + 1;
+ }
+ }
+ else
+ {
+ crs = extract_hex (crs, 2, &hex_number);
+ }
+
+ /* FIXME: If one of the variables UPPERCASE or LOWERCASE is
+ true, the character should be converted to its uppercase
+ resp. lowercase equivalent. I don't know if the necessary
+ facilities are already included in gettext. For US-Ascii
+ the conversion can be already be done, however. */
+ if (uppercase && hex_number >= 'a' && hex_number <= 'z')
+ {
+ hex_number = hex_number - 'a' + 'A';
+ }
+ else if (lowercase && hex_number >= 'A' && hex_number <= 'Z')
+ {
+ hex_number = hex_number - 'A' + 'a';
+ }
+
+ length = u8_uctomb ((unsigned char *) (buffer + bufpos),
+ hex_number, 6);
+
+ if (length > 0)
+ bufpos += length;
+ }
+ continue;
+ case 'c':
+ /* Perl's notion of control characters. */
+ crs += 2;
+ if (*crs)
+ {
+ int the_char = (unsigned char) *crs;
+ if (the_char >= 'a' && the_char <= 'z')
+ the_char = the_char - 'a' + 'A';
+ buffer[bufpos++] = the_char ^ 0x40;
+ }
+ continue;
+ case 'N':
+ crs += 2;
+ if (*crs == '{')
+ {
+ const char *end = strchr (crs + 1, '}');
+ if (end != NULL)
+ {
+ char *name;
+ unsigned int unicode;
+
+ name = XNMALLOC (end - (crs + 1) + 1, char);
+ memcpy (name, crs + 1, end - (crs + 1));
+ name[end - (crs + 1)] = '\0';
+
+ unicode = unicode_name_character (name);
+ if (unicode != UNINAME_INVALID)
+ {
+ /* FIXME: Convert to upper/lowercase if the
+ corresponding flag is set to true. */
+ int length =
+ u8_uctomb ((unsigned char *) (buffer + bufpos),
+ unicode, 6);
+ if (length > 0)
+ bufpos += length;
+ }
+
+ free (name);
+
+ crs = end + 1;
+ }
+ }
+ continue;
+ }
+ }
+
+ /* No escape sequence, go on. */
+ if (*crs == '\\')
+ {
+ ++crs;
+ switch (*crs)
+ {
+ case 'E':
+ uppercase = false;
+ lowercase = false;
+ quotemeta = false;
+ ++crs;
+ continue;
+ case 'L':
+ uppercase = false;
+ lowercase = true;
+ ++crs;
+ continue;
+ case 'U':
+ uppercase = true;
+ lowercase = false;
+ ++crs;
+ continue;
+ case 'Q':
+ quotemeta = true;
+ ++crs;
+ continue;
+ case 'l':
+ ++crs;
+ if (*crs >= 'A' && *crs <= 'Z')
+ {
+ buffer[bufpos++] = *crs - 'A' + 'a';
+ }
+ else if ((unsigned char) *crs >= 0x80)
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: invalid interpolation (\"\\l\") of 8bit character \"%c\""),
+ real_file_name, line_number, *crs);
+ error_with_progname = true;
+ }
+ else
+ {
+ buffer[bufpos++] = *crs;
+ }
+ ++crs;
+ continue;
+ case 'u':
+ ++crs;
+ if (*crs >= 'a' && *crs <= 'z')
+ {
+ buffer[bufpos++] = *crs - 'a' + 'A';
+ }
+ else if ((unsigned char) *crs >= 0x80)
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: invalid interpolation (\"\\u\") of 8bit character \"%c\""),
+ real_file_name, line_number, *crs);
+ error_with_progname = true;
+ }
+ else
+ {
+ buffer[bufpos++] = *crs;
+ }
+ ++crs;
+ continue;
+ case '\\':
+ buffer[bufpos++] = *crs;
+ ++crs;
+ continue;
+ default:
+ backslashed = true;
+ break;
+ }
+ }
+ else
+ backslashed = false;
+
+ if (quotemeta
+ && !((*crs >= 'A' && *crs <= 'Z') || (*crs >= 'A' && *crs <= 'z')
+ || (*crs >= '0' && *crs <= '9') || *crs == '_'))
+ {
+ buffer[bufpos++] = '\\';
+ backslashed = true;
+ }
+
+ if (!backslashed && !extract_all && (*crs == '$' || *crs == '@'))
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: invalid variable interpolation at \"%c\""),
+ real_file_name, line_number, *crs);
+ error_with_progname = true;
+ ++crs;
+ }
+ else if (lowercase)
+ {
+ if (*crs >= 'A' && *crs <= 'Z')
+ buffer[bufpos++] = *crs - 'A' + 'a';
+ else if ((unsigned char) *crs >= 0x80)
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: invalid interpolation (\"\\L\") of 8bit character \"%c\""),
+ real_file_name, line_number, *crs);
+ error_with_progname = true;
+ buffer[bufpos++] = *crs;
+ }
+ else
+ buffer[bufpos++] = *crs;
+ ++crs;
+ }
+ else if (uppercase)
+ {
+ if (*crs >= 'a' && *crs <= 'z')
+ buffer[bufpos++] = *crs - 'a' + 'A';
+ else if ((unsigned char) *crs >= 0x80)
+ {
+ error_with_progname = false;
+ error (error_level, 0, _("\
+%s:%d: invalid interpolation (\"\\U\") of 8bit character \"%c\""),
+ real_file_name, line_number, *crs);
+ error_with_progname = true;
+ buffer[bufpos++] = *crs;
+ }
+ else
+ buffer[bufpos++] = *crs;
+ ++crs;
+ }
+ else
+ {
+ buffer[bufpos++] = *crs++;
+ }
+ }
+
+ /* Ensure room for 1 more byte. */
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+
+ buffer[bufpos++] = '\0';
+
+#if DEBUG_PERL
+ fprintf (stderr, "---> %s\n", buffer);
+#endif
+
+ /* Replace tp->string. */
+ free (tp->string);
+ tp->string = xstrdup (buffer);
+}
+
+/* Parse a variable. This is done in several steps:
+ 1) Consume all leading occurencies of '$', '@', '%', and '*'.
+ 2) Determine the name of the variable from the following input.
+ 3) Parse possible following hash keys or array indexes.
+ */
+static void
+extract_variable (message_list_ty *mlp, token_ty *tp, int first)
+{
+ static char *buffer;
+ static int bufmax = 0;
+ int bufpos = 0;
+ int c = first;
+ size_t varbody_length = 0;
+ bool maybe_hash_deref = false;
+ bool maybe_hash_value = false;
+
+ tp->type = token_type_variable;
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extracting variable type '%c'\n",
+ real_file_name, line_number, first);
+#endif
+
+ /*
+ * 1) Consume dollars and so on (not euros ...). Unconditionally
+ * accepting the hash sign (#) will maybe lead to inaccurate
+ * results. FIXME!
+ */
+ while (c == '$' || c == '*' || c == '#' || c == '@' || c == '%')
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ }
+
+ if (c == EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+
+ /* Hash references are treated in a special way, when looking for
+ our keywords. */
+ if (buffer[0] == '$')
+ {
+ if (bufpos == 1)
+ maybe_hash_value = true;
+ else if (bufpos == 2 && buffer[1] == '$')
+ {
+ if (!(c == '{'
+ || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')
+ || c == '_' || c == ':' || c == '\'' || c >= 0x80))
+ {
+ /* Special variable $$ for pid. */
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = '\0';
+ tp->string = xstrdup (buffer);
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: is PID ($$)\n",
+ real_file_name, line_number);
+#endif
+
+ phase1_ungetc (c);
+ return;
+ }
+
+ maybe_hash_deref = true;
+ bufpos = 1;
+ }
+ }
+
+ /*
+ * 2) Get the name of the variable. The first character is practically
+ * arbitrary. Punctuation and numbers automagically put a variable
+ * in the global namespace but that subtle difference is not interesting
+ * for us.
+ */
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ if (c == '{')
+ {
+ /* Yuck, we cannot accept ${gettext} as a keyword... Except for
+ * debugging purposes it is also harmless, that we suppress the
+ * real name of the variable.
+ */
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: braced {variable_name}\n",
+ real_file_name, line_number);
+#endif
+
+ if (extract_balanced (mlp, token_type_rbrace, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL)))
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+ buffer[bufpos++] = c;
+ }
+ else
+ {
+ while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')
+ || c == '_' || c == ':' || c == '\'' || c >= 0x80)
+ {
+ ++varbody_length;
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ }
+ phase1_ungetc (c);
+ }
+
+ /* Probably some strange Perl variable like $`. */
+ if (varbody_length == 0)
+ {
+ c = phase1_getc ();
+ if (c == EOF || is_whitespace (c))
+ phase1_ungetc (c); /* Loser. */
+ else
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ }
+
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = '\0';
+
+ tp->string = xstrdup (buffer);
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: complete variable name: %s\n",
+ real_file_name, line_number, tp->string);
+#endif
+
+ /*
+ * 3) If the following looks strange to you, this is valid Perl syntax:
+ *
+ * $var = $$hashref # We can place a
+ * # comment here and then ...
+ * {key_into_hashref};
+ *
+ * POD sections are not allowed but we leave complaints about
+ * that to the compiler/interpreter.
+ */
+ /* We only extract strings from the first hash key (if present). */
+
+ if (maybe_hash_deref || maybe_hash_value)
+ {
+ bool is_dereference = false;
+ int c;
+
+ do
+ c = phase2_getc ();
+ while (is_whitespace (c));
+
+ if (c == '-')
+ {
+ int c2 = phase1_getc ();
+
+ if (c2 == '>')
+ {
+ is_dereference = true;
+
+ do
+ c = phase2_getc ();
+ while (is_whitespace (c));
+ }
+ else if (c2 != '\n')
+ {
+ /* Discarding the newline is harmless here. The only
+ special character recognized after a minus is greater-than
+ for dereference. However, the sequence "-\n>" that we
+ treat incorrectly here, is a syntax error. */
+ phase1_ungetc (c2);
+ }
+ }
+
+ if (maybe_hash_value && is_dereference)
+ {
+ tp->type = token_type_object;
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: first keys preceded by \"->\"\n",
+ real_file_name, line_number);
+#endif
+ }
+ else if (maybe_hash_value)
+ {
+ /* Fake it into a hash. */
+ tp->string[0] = '%';
+ }
+
+ /* Do NOT change that into else if (see above). */
+ if ((maybe_hash_value || maybe_hash_deref) && c == '{')
+ {
+ void *keyword_value;
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: first keys preceded by '{'\n",
+ real_file_name, line_number);
+#endif
+
+ if (hash_find_entry (&keywords, tp->string, strlen (tp->string),
+ &keyword_value) == 0)
+ {
+ /* TODO: Shouldn't we use the shapes of the keyword, instead
+ of hardwiring argnum1 = 1 ?
+ const struct callshapes *shapes =
+ (const struct callshapes *) keyword_value;
+ */
+ struct callshapes shapes;
+ shapes.keyword = tp->string; /* XXX storage duration? */
+ shapes.keyword_len = strlen (tp->string);
+ shapes.nshapes = 1;
+ shapes.shapes[0].argnum1 = 1;
+ shapes.shapes[0].argnum2 = 0;
+ shapes.shapes[0].argnumc = 0;
+ shapes.shapes[0].argnum1_glib_context = false;
+ shapes.shapes[0].argnum2_glib_context = false;
+ shapes.shapes[0].argtotal = 0;
+ string_list_init (&shapes.shapes[0].xcomments);
+
+ {
+ /* Extract a possible string from the key. Before proceeding
+ we check whether the open curly is followed by a symbol and
+ then by a right curly. */
+ flag_context_list_iterator_ty context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ tp->string, strlen (tp->string)));
+ token_ty *t1 = x_perl_lex (mlp);
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extracting string key\n",
+ real_file_name, line_number);
+#endif
+
+ if (t1->type == token_type_symbol
+ || t1->type == token_type_named_op)
+ {
+ token_ty *t2 = x_perl_lex (mlp);
+ if (t2->type == token_type_rbrace)
+ {
+ flag_context_ty context;
+ lex_pos_ty pos;
+
+ context =
+ inherited_context (null_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ pos.line_number = line_number;
+ pos.file_name = logical_file_name;
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ remember_a_message (mlp, NULL, xstrdup (t1->string),
+ context, &pos, NULL, savable_comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ free_token (t2);
+ free_token (t1);
+ }
+ else
+ {
+ x_perl_unlex (t2);
+ }
+ }
+ else
+ {
+ x_perl_unlex (t1);
+ if (extract_balanced (mlp, token_type_rbrace, true, false,
+ null_context, context_iter,
+ 1, arglist_parser_alloc (mlp, &shapes)))
+ return;
+ }
+ }
+ }
+ else
+ {
+ phase2_ungetc (c);
+ }
+ }
+ else
+ {
+ phase2_ungetc (c);
+ }
+ }
+
+ /* Now consume "->", "[...]", and "{...}". */
+ for (;;)
+ {
+ int c = phase2_getc ();
+ int c2;
+
+ switch (c)
+ {
+ case '{':
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extracting balanced '{' after varname\n",
+ real_file_name, line_number);
+#endif
+ extract_balanced (mlp, token_type_rbrace, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL));
+ break;
+
+ case '[':
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extracting balanced '[' after varname\n",
+ real_file_name, line_number);
+#endif
+ extract_balanced (mlp, token_type_rbracket, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL));
+ break;
+
+ case '-':
+ c2 = phase1_getc ();
+ if (c2 == '>')
+ {
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: another \"->\" after varname\n",
+ real_file_name, line_number);
+#endif
+ break;
+ }
+ else if (c2 != '\n')
+ {
+ /* Discarding the newline is harmless here. The only
+ special character recognized after a minus is greater-than
+ for dereference. However, the sequence "-\n>" that we
+ treat incorrectly here, is a syntax error. */
+ phase1_ungetc (c2);
+ }
+ /* FALLTHROUGH */
+
+ default:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: variable finished\n",
+ real_file_name, line_number);
+#endif
+ phase2_ungetc (c);
+ return;
+ }
+ }
+}
+
+/* Actually a simplified version of extract_variable(). It searches for
+ variables inside a double-quoted string that may interpolate to
+ some keyword hash (reference). The string is UTF-8 encoded. */
+static void
+interpolate_keywords (message_list_ty *mlp, const char *string, int lineno)
+{
+ static char *buffer;
+ static int bufmax = 0;
+ int bufpos = 0;
+ flag_context_ty context;
+ int c;
+ bool maybe_hash_deref = false;
+ enum parser_state
+ {
+ initial,
+ one_dollar,
+ two_dollars,
+ identifier,
+ minus,
+ wait_lbrace,
+ wait_quote,
+ dquote,
+ squote,
+ barekey,
+ wait_rbrace
+ } state;
+ token_ty token;
+
+ lex_pos_ty pos;
+
+ /* States are:
+ *
+ * initial: initial
+ * one_dollar: dollar sign seen in state INITIAL
+ * two_dollars: another dollar-sign has been seen in state ONE_DOLLAR
+ * identifier: a valid identifier character has been seen in state
+ * ONE_DOLLAR or TWO_DOLLARS
+ * minus: a minus-sign has been seen in state IDENTIFIER
+ * wait_lbrace: a greater-than has been seen in state MINUS
+ * wait_quote: a left brace has been seen in state IDENTIFIER or in
+ * state WAIT_LBRACE
+ * dquote: a double-quote has been seen in state WAIT_QUOTE
+ * squote: a single-quote has been seen in state WAIT_QUOTE
+ * barekey: an bareword character has been seen in state WAIT_QUOTE
+ * wait_rbrace: closing quote has been seen in state DQUOTE or SQUOTE
+ *
+ * In the states initial...identifier the context is null_context; in the
+ * states minus...wait_rbrace the context is the one suitable for the first
+ * argument of the last seen identifier.
+ */
+ state = initial;
+ context = null_context;
+
+ token.type = token_type_string;
+ token.sub_type = string_type_qq;
+ token.line_number = line_number;
+ /* No need for token.comment = add_reference (savable_comment); here.
+ We can let token.comment uninitialized here, and use savable_comment
+ directly, because this function only parses the given string and does
+ not call phase2_getc. */
+ pos.file_name = logical_file_name;
+ pos.line_number = lineno;
+
+ while ((c = (unsigned char) *string++) != '\0')
+ {
+ void *keyword_value;
+
+ if (state == initial)
+ bufpos = 0;
+
+ if (c == '\n')
+ lineno++;
+
+ if (bufpos + 1 >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+
+ switch (state)
+ {
+ case initial:
+ switch (c)
+ {
+ case '\\':
+ c = (unsigned char) *string++;
+ if (c == '\0')
+ return;
+ break;
+ case '$':
+ buffer[bufpos++] = '$';
+ maybe_hash_deref = false;
+ state = one_dollar;
+ break;
+ default:
+ break;
+ }
+ break;
+ case one_dollar:
+ switch (c)
+ {
+ case '$':
+ /*
+ * This is enough to make us believe later that we dereference
+ * a hash reference.
+ */
+ maybe_hash_deref = true;
+ state = two_dollars;
+ break;
+ default:
+ if (c == '_' || c == ':' || c == '\'' || c >= 0x80
+ || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9'))
+ {
+ buffer[bufpos++] = c;
+ state = identifier;
+ }
+ else
+ state = initial;
+ break;
+ }
+ break;
+ case two_dollars:
+ if (c == '_' || c == ':' || c == '\'' || c >= 0x80
+ || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9'))
+ {
+ buffer[bufpos++] = c;
+ state = identifier;
+ }
+ else
+ state = initial;
+ break;
+ case identifier:
+ switch (c)
+ {
+ case '-':
+ if (hash_find_entry (&keywords, buffer, bufpos, &keyword_value)
+ == 0)
+ {
+ flag_context_list_iterator_ty context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ buffer, bufpos));
+ context =
+ inherited_context (null_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ state = minus;
+ }
+ else
+ state = initial;
+ break;
+ case '{':
+ if (!maybe_hash_deref)
+ buffer[0] = '%';
+ if (hash_find_entry (&keywords, buffer, bufpos, &keyword_value)
+ == 0)
+ {
+ flag_context_list_iterator_ty context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ buffer, bufpos));
+ context =
+ inherited_context (null_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ state = wait_quote;
+ }
+ else
+ state = initial;
+ break;
+ default:
+ if (c == '_' || c == ':' || c == '\'' || c >= 0x80
+ || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9'))
+ {
+ buffer[bufpos++] = c;
+ }
+ else
+ state = initial;
+ break;
+ }
+ break;
+ case minus:
+ switch (c)
+ {
+ case '>':
+ state = wait_lbrace;
+ break;
+ default:
+ context = null_context;
+ state = initial;
+ break;
+ }
+ break;
+ case wait_lbrace:
+ switch (c)
+ {
+ case '{':
+ state = wait_quote;
+ break;
+ default:
+ context = null_context;
+ state = initial;
+ break;
+ }
+ break;
+ case wait_quote:
+ switch (c)
+ {
+ case_whitespace:
+ break;
+ case '\'':
+ pos.line_number = lineno;
+ bufpos = 0;
+ state = squote;
+ break;
+ case '"':
+ pos.line_number = lineno;
+ bufpos = 0;
+ state = dquote;
+ break;
+ default:
+ if (c == '_' || (c >= '0' && c <= '9') || c >= 0x80
+ || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ {
+ pos.line_number = lineno;
+ bufpos = 0;
+ buffer[bufpos++] = c;
+ state = barekey;
+ }
+ else
+ {
+ context = null_context;
+ state = initial;
+ }
+ break;
+ }
+ break;
+ case dquote:
+ switch (c)
+ {
+ case '"':
+ /* The resulting string has to be interpolated twice. */
+ buffer[bufpos] = '\0';
+ token.string = xstrdup (buffer);
+ extract_quotelike_pass3 (&token, EXIT_FAILURE);
+ /* The string can only shrink with interpolation (because
+ we ignore \Q). */
+ if (!(strlen (token.string) <= bufpos))
+ abort ();
+ strcpy (buffer, token.string);
+ free (token.string);
+ state = wait_rbrace;
+ break;
+ case '\\':
+ if (string[0] == '\"')
+ {
+ buffer[bufpos++] = string++[0];
+ }
+ else if (string[0])
+ {
+ buffer[bufpos++] = '\\';
+ buffer[bufpos++] = string++[0];
+ }
+ else
+ {
+ context = null_context;
+ state = initial;
+ }
+ break;
+ default:
+ buffer[bufpos++] = c;
+ break;
+ }
+ break;
+ case squote:
+ switch (c)
+ {
+ case '\'':
+ state = wait_rbrace;
+ break;
+ case '\\':
+ if (string[0] == '\'')
+ {
+ buffer[bufpos++] = string++[0];
+ }
+ else if (string[0])
+ {
+ buffer[bufpos++] = '\\';
+ buffer[bufpos++] = string++[0];
+ }
+ else
+ {
+ context = null_context;
+ state = initial;
+ }
+ break;
+ default:
+ buffer[bufpos++] = c;
+ break;
+ }
+ break;
+ case barekey:
+ if (c == '_' || (c >= '0' && c <= '9') || c >= 0x80
+ || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ {
+ buffer[bufpos++] = c;
+ break;
+ }
+ else if (is_whitespace (c))
+ {
+ state = wait_rbrace;
+ break;
+ }
+ else if (c != '}')
+ {
+ context = null_context;
+ state = initial;
+ break;
+ }
+ /* Must be right brace. */
+ /* FALLTHROUGH */
+ case wait_rbrace:
+ switch (c)
+ {
+ case_whitespace:
+ break;
+ case '}':
+ buffer[bufpos] = '\0';
+ token.string = xstrdup (buffer);
+ extract_quotelike_pass3 (&token, EXIT_FAILURE);
+ xgettext_current_source_encoding = po_charset_utf8;
+ remember_a_message (mlp, NULL, token.string, context, &pos,
+ NULL, savable_comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ /* FALLTHROUGH */
+ default:
+ context = null_context;
+ state = initial;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+/* There is an ambiguity about '/' and '?': They can start an operator
+ (division operator '/' or '/=' or the conditional operator '?'), or they can
+ start a regular expression. The distinction is important because inside
+ regular expressions, '#' loses its special meaning. This function helps
+ making the decision (a heuristic). See the documentation for details. */
+static bool
+prefer_regexp_over_division (token_type_ty type)
+{
+ bool retval = true;
+
+ switch (type)
+ {
+ case token_type_eof:
+ retval = true;
+ break;
+ case token_type_lparen:
+ retval = true;
+ break;
+ case token_type_rparen:
+ retval = false;
+ break;
+ case token_type_comma:
+ retval = true;
+ break;
+ case token_type_fat_comma:
+ retval = true;
+ break;
+ case token_type_dereference:
+ retval = true;
+ break;
+ case token_type_semicolon:
+ retval = true;
+ break;
+ case token_type_lbrace:
+ retval = true;
+ break;
+ case token_type_rbrace:
+ retval = false;
+ break;
+ case token_type_lbracket:
+ retval = true;
+ break;
+ case token_type_rbracket:
+ retval = false;
+ break;
+ case token_type_string:
+ retval = false;
+ break;
+ case token_type_number:
+ retval = false;
+ break;
+ case token_type_named_op:
+ retval = true;
+ break;
+ case token_type_variable:
+ retval = false;
+ break;
+ case token_type_object:
+ retval = false;
+ break;
+ case token_type_symbol:
+ case token_type_keyword_symbol:
+ retval = true;
+ break;
+ case token_type_regex_op:
+ retval = false;
+ break;
+ case token_type_dot:
+ retval = true;
+ break;
+ case token_type_other:
+ retval = true;
+ break;
+ }
+
+#if DEBUG_PERL
+ token_ty ty;
+ ty.type = type;
+ fprintf (stderr, "Prefer regexp over division after %s: %s\n",
+ token2string (&ty), retval ? "true" : "false");
+#endif
+
+ return retval;
+}
+
+/* Last token type seen in the stream. Important for the interpretation
+ of slash and question mark. */
+static token_type_ty last_token_type;
+
+/* Combine characters into tokens. Discard whitespace. */
+
+static void
+x_perl_prelex (message_list_ty *mlp, token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+
+ for (;;)
+ {
+ c = phase2_getc ();
+ tp->line_number = line_number;
+ tp->last_type = last_token_type;
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case '\t':
+ case ' ':
+ /* Ignore whitespace. */
+ continue;
+
+ case '%':
+ case '@':
+ case '*':
+ case '$':
+ if (!extract_all)
+ {
+ extract_variable (mlp, tp, c);
+ return;
+ }
+ break;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case '.':
+ {
+ int c2 = phase1_getc ();
+ phase1_ungetc (c2);
+ if (c2 == '.')
+ {
+ tp->type = token_type_other;
+ return;
+ }
+ else if (!(c2 >= '0' && c2 <= '9'))
+ {
+ tp->type = token_type_dot;
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Symbol, or part of a number. */
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+
+ default:
+ phase1_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+
+ if (strcmp (buffer, "__END__") == 0
+ || strcmp (buffer, "__DATA__") == 0)
+ {
+ end_of_file = true;
+ tp->type = token_type_eof;
+ return;
+ }
+ else if (strcmp (buffer, "and") == 0
+ || strcmp (buffer, "cmp") == 0
+ || strcmp (buffer, "eq") == 0
+ || strcmp (buffer, "if") == 0
+ || strcmp (buffer, "ge") == 0
+ || strcmp (buffer, "gt") == 0
+ || strcmp (buffer, "le") == 0
+ || strcmp (buffer, "lt") == 0
+ || strcmp (buffer, "ne") == 0
+ || strcmp (buffer, "not") == 0
+ || strcmp (buffer, "or") == 0
+ || strcmp (buffer, "unless") == 0
+ || strcmp (buffer, "while") == 0
+ || strcmp (buffer, "xor") == 0)
+ {
+ tp->type = token_type_named_op;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+ else if (strcmp (buffer, "s") == 0
+ || strcmp (buffer, "y") == 0
+ || strcmp (buffer, "tr") == 0)
+ {
+ int delim = phase1_getc ();
+
+ while (is_whitespace (delim))
+ delim = phase2_getc ();
+
+ if (delim == EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+ if ((delim >= '0' && delim <= '9')
+ || (delim >= 'A' && delim <= 'Z')
+ || (delim >= 'a' && delim <= 'z'))
+ {
+ /* False positive. */
+ phase2_ungetc (delim);
+ tp->type = token_type_symbol;
+ tp->sub_type = symbol_type_none;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+ extract_triple_quotelike (mlp, tp, delim,
+ buffer[0] == 's' && delim != '\'');
+
+ /* Eat the following modifiers. */
+ do
+ c = phase1_getc ();
+ while (c >= 'a' && c <= 'z');
+ phase1_ungetc (c);
+ return;
+ }
+ else if (strcmp (buffer, "m") == 0)
+ {
+ int delim = phase1_getc ();
+
+ while (is_whitespace (delim))
+ delim = phase2_getc ();
+
+ if (delim == EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+ if ((delim >= '0' && delim <= '9')
+ || (delim >= 'A' && delim <= 'Z')
+ || (delim >= 'a' && delim <= 'z'))
+ {
+ /* False positive. */
+ phase2_ungetc (delim);
+ tp->type = token_type_symbol;
+ tp->sub_type = symbol_type_none;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+ extract_quotelike (tp, delim);
+ if (delim != '\'')
+ interpolate_keywords (mlp, tp->string, line_number);
+ free (tp->string);
+ drop_reference (tp->comment);
+ tp->type = token_type_regex_op;
+
+ /* Eat the following modifiers. */
+ do
+ c = phase1_getc ();
+ while (c >= 'a' && c <= 'z');
+ phase1_ungetc (c);
+ return;
+ }
+ else if (strcmp (buffer, "qq") == 0
+ || strcmp (buffer, "q") == 0
+ || strcmp (buffer, "qx") == 0
+ || strcmp (buffer, "qw") == 0
+ || strcmp (buffer, "qr") == 0)
+ {
+ /* The qw (...) construct is not really a string but we
+ can treat in the same manner and then pretend it is
+ a symbol. Rationale: Saying "qw (foo bar)" is the
+ same as "my @list = ('foo', 'bar'); @list;". */
+
+ int delim = phase1_getc ();
+
+ while (is_whitespace (delim))
+ delim = phase2_getc ();
+
+ if (delim == EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+
+ if ((delim >= '0' && delim <= '9')
+ || (delim >= 'A' && delim <= 'Z')
+ || (delim >= 'a' && delim <= 'z'))
+ {
+ /* False positive. */
+ phase2_ungetc (delim);
+ tp->type = token_type_symbol;
+ tp->sub_type = symbol_type_none;
+ tp->string = xstrdup (buffer);
+ return;
+ }
+
+ extract_quotelike (tp, delim);
+
+ switch (buffer[1])
+ {
+ case 'q':
+ case 'x':
+ tp->type = token_type_string;
+ tp->sub_type = string_type_qq;
+ interpolate_keywords (mlp, tp->string, line_number);
+ break;
+ case 'r':
+ drop_reference (tp->comment);
+ tp->type = token_type_regex_op;
+ break;
+ case 'w':
+ drop_reference (tp->comment);
+ tp->type = token_type_symbol;
+ tp->sub_type = symbol_type_none;
+ break;
+ case '\0':
+ tp->type = token_type_string;
+ tp->sub_type = string_type_q;
+ break;
+ default:
+ abort ();
+ }
+ return;
+ }
+ else if ((buffer[0] >= '0' && buffer[0] <= '9') || buffer[0] == '.')
+ {
+ tp->type = token_type_number;
+ return;
+ }
+ tp->type = token_type_symbol;
+ tp->sub_type = (strcmp (buffer, "sub") == 0
+ ? symbol_type_sub
+ : symbol_type_none);
+ tp->string = xstrdup (buffer);
+ return;
+
+ case '"':
+ extract_quotelike (tp, c);
+ tp->sub_type = string_type_qq;
+ interpolate_keywords (mlp, tp->string, line_number);
+ return;
+
+ case '`':
+ extract_quotelike (tp, c);
+ tp->sub_type = string_type_qq;
+ interpolate_keywords (mlp, tp->string, line_number);
+ return;
+
+ case '\'':
+ extract_quotelike (tp, c);
+ tp->sub_type = string_type_q;
+ return;
+
+ case '(':
+ c = phase2_getc ();
+ if (c == ')')
+ /* Ignore empty list. */
+ continue;
+ else
+ phase2_ungetc (c);
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case '{':
+ tp->type = token_type_lbrace;
+ return;
+
+ case '}':
+ tp->type = token_type_rbrace;
+ return;
+
+ case '[':
+ tp->type = token_type_lbracket;
+ return;
+
+ case ']':
+ tp->type = token_type_rbracket;
+ return;
+
+ case ';':
+ tp->type = token_type_semicolon;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '=':
+ /* Check for fat comma. */
+ c = phase1_getc ();
+ if (c == '>')
+ {
+ tp->type = token_type_fat_comma;
+ return;
+ }
+ else if (linepos == 2
+ && (last_token_type == token_type_semicolon
+ || last_token_type == token_type_rbrace)
+ && ((c >= 'A' && c <='Z')
+ || (c >= 'a' && c <= 'z')))
+ {
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: start pod section\n",
+ real_file_name, line_number);
+#endif
+ skip_pod ();
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: end pod section\n",
+ real_file_name, line_number);
+#endif
+ continue;
+ }
+ phase1_ungetc (c);
+ tp->type = token_type_other;
+ return;
+
+ case '<':
+ /* Check for <<EOF and friends. */
+ c = phase1_getc ();
+ if (c == '<')
+ {
+ c = phase1_getc ();
+ if (c == '\'')
+ {
+ char *string;
+ extract_quotelike (tp, c);
+ string = get_here_document (tp->string);
+ free (tp->string);
+ tp->string = string;
+ tp->type = token_type_string;
+ tp->sub_type = string_type_verbatim;
+ tp->line_number = line_number + 1;
+ return;
+ }
+ else if (c == '"')
+ {
+ char *string;
+ extract_quotelike (tp, c);
+ string = get_here_document (tp->string);
+ free (tp->string);
+ tp->string = string;
+ tp->type = token_type_string;
+ tp->sub_type = string_type_qq;
+ tp->line_number = line_number + 1;
+ interpolate_keywords (mlp, tp->string, tp->line_number);
+ return;
+ }
+ else if ((c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || c == '_')
+ {
+ bufpos = 0;
+ while ((c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')
+ || c == '_' || c >= 0x80)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ }
+ if (c == EOF)
+ {
+ tp->type = token_type_eof;
+ return;
+ }
+ else
+ {
+ char *string;
+ phase1_ungetc (c);
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = '\0';
+ string = get_here_document (buffer);
+ tp->string = string;
+ tp->type = token_type_string;
+ tp->sub_type = string_type_qq;
+ tp->comment = add_reference (savable_comment);
+ tp->line_number = line_number + 1;
+ interpolate_keywords (mlp, tp->string, tp->line_number);
+ return;
+ }
+ }
+ else
+ {
+ tp->type = token_type_other;
+ return;
+ }
+ }
+ else
+ {
+ phase1_ungetc (c);
+ tp->type = token_type_other;
+ }
+ return; /* End of case '>'. */
+
+ case '-':
+ /* Check for dereferencing operator. */
+ c = phase1_getc ();
+ if (c == '>')
+ {
+ tp->type = token_type_dereference;
+ return;
+ }
+ else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
+ {
+ /* One of the -X (filetest) functions. We play safe
+ and accept all alphabetical characters here. */
+ tp->type = token_type_other;
+ return;
+ }
+ phase1_ungetc (c);
+ tp->type = token_type_other;
+ return;
+
+ case '/':
+ case '?':
+ if (prefer_regexp_over_division (tp->last_type))
+ {
+ extract_quotelike (tp, c);
+ interpolate_keywords (mlp, tp->string, line_number);
+ free (tp->string);
+ drop_reference (tp->comment);
+ tp->type = token_type_regex_op;
+ /* Eat the following modifiers. */
+ do
+ c = phase1_getc ();
+ while (c >= 'a' && c <= 'z');
+ phase1_ungetc (c);
+ return;
+ }
+ /* Recognize operator '//'. */
+ if (c == '/')
+ {
+ c = phase1_getc ();
+ if (c != '/')
+ phase1_ungetc (c);
+ }
+ /* FALLTHROUGH */
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, except for the '//' operator,
+ as we only need to recognize gettext invocations. Don't
+ bother. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+
+/* A token stack used as a lookahead buffer. */
+
+typedef struct token_stack_ty token_stack_ty;
+struct token_stack_ty
+{
+ token_ty **items;
+ size_t nitems;
+ size_t nitems_max;
+};
+
+static struct token_stack_ty token_stack;
+
+#if DEBUG_PERL
+/* Dumps all resources allocated by stack STACK. */
+static int
+token_stack_dump (token_stack_ty *stack)
+{
+ size_t i;
+
+ fprintf (stderr, "BEGIN STACK DUMP\n");
+ for (i = 0; i < stack->nitems; i++)
+ {
+ token_ty *token = stack->items[i];
+ fprintf (stderr, " [%s]\n", token2string (token));
+ switch (token->type)
+ {
+ case token_type_named_op:
+ case token_type_string:
+ case token_type_symbol:
+ case token_type_variable:
+ fprintf (stderr, " string: %s\n", token->string);
+ break;
+ case token_type_object:
+ fprintf (stderr, " string: %s->\n", token->string);
+ default:
+ break;
+ }
+ }
+ fprintf (stderr, "END STACK DUMP\n");
+ return 0;
+}
+#endif
+
+/* Pushes the token TOKEN onto the stack STACK. */
+static inline void
+token_stack_push (token_stack_ty *stack, token_ty *token)
+{
+ if (stack->nitems >= stack->nitems_max)
+ {
+ size_t nbytes;
+
+ stack->nitems_max = 2 * stack->nitems_max + 4;
+ nbytes = stack->nitems_max * sizeof (token_ty *);
+ stack->items = xrealloc (stack->items, nbytes);
+ }
+ stack->items[stack->nitems++] = token;
+}
+
+/* Pops the most recently pushed token from the stack STACK and returns it.
+ Returns NULL if the stack is empty. */
+static inline token_ty *
+token_stack_pop (token_stack_ty *stack)
+{
+ if (stack->nitems > 0)
+ return stack->items[--(stack->nitems)];
+ else
+ return NULL;
+}
+
+/* Return the top of the stack without removing it from the stack, or
+ NULL if the stack is empty. */
+static inline token_ty *
+token_stack_peek (const token_stack_ty *stack)
+{
+ if (stack->nitems > 0)
+ return stack->items[stack->nitems - 1];
+ else
+ return NULL;
+}
+
+/* Frees all resources allocated by stack STACK. */
+static inline void
+token_stack_free (token_stack_ty *stack)
+{
+ size_t i;
+
+ for (i = 0; i < stack->nitems; i++)
+ free_token (stack->items[i]);
+ free (stack->items);
+}
+
+
+static token_ty *
+x_perl_lex (message_list_ty *mlp)
+{
+#if DEBUG_PERL
+ int dummy = token_stack_dump (&token_stack);
+#endif
+ token_ty *tp = token_stack_pop (&token_stack);
+
+ if (!tp)
+ {
+ tp = XMALLOC (token_ty);
+ x_perl_prelex (mlp, tp);
+ tp->last_type = last_token_type;
+ last_token_type = tp->type;
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: x_perl_prelex returned %s\n",
+ real_file_name, line_number, token2string (tp));
+#endif
+
+ /* The interpretation of a slash or question mark after a function call
+ depends on the prototype of that function. If the function expects
+ at least one argument, a regular expression is preferred, otherwise
+ an operator. With our limited means, we can only guess here. If
+ the function is a builtin that takes no arguments, we prefer an
+ operator by silently turning the last symbol into a variable instead
+ of a symbol.
+
+ Method calls without parentheses are not ambiguous. After them, an
+ operator must follow. Due to some ideosyncrasies in this parser
+ they are treated in two different manners. If the call is
+ chained ($foo->bar->baz) the token left of the symbol is a
+ dereference operator. If it is not chained ($foo->bar) the
+ dereference operator is consumed with the extracted variable. The
+ latter case is handled below. */
+ if (tp->type == token_type_symbol)
+ {
+ if (tp->last_type == token_type_dereference)
+ {
+ /* Class method call or chained method call (with at least
+ two arrow operators). */
+ last_token_type = token_type_variable;
+ }
+ else if (tp->last_type == token_type_object)
+ {
+ /* Instance method, not chained. */
+ last_token_type = token_type_variable;
+ }
+ else if (strcmp (tp->string, "wantarray") == 0
+ || strcmp (tp->string, "fork") == 0
+ || strcmp (tp->string, "getlogin") == 0
+ || strcmp (tp->string, "getppid") == 0
+ || strcmp (tp->string, "getpwent") == 0
+ || strcmp (tp->string, "getgrent") == 0
+ || strcmp (tp->string, "gethostent") == 0
+ || strcmp (tp->string, "getnetent") == 0
+ || strcmp (tp->string, "getprotoent") == 0
+ || strcmp (tp->string, "getservent") == 0
+ || strcmp (tp->string, "setpwent") == 0
+ || strcmp (tp->string, "setgrent") == 0
+ || strcmp (tp->string, "endpwent") == 0
+ || strcmp (tp->string, "endgrent") == 0
+ || strcmp (tp->string, "endhostent") == 0
+ || strcmp (tp->string, "endnetent") == 0
+ || strcmp (tp->string, "endprotoent") == 0
+ || strcmp (tp->string, "endservent") == 0
+ || strcmp (tp->string, "time") == 0
+ || strcmp (tp->string, "times") == 0
+ || strcmp (tp->string, "wait") == 0
+ || strcmp (tp->string, "wantarray") == 0)
+ {
+ /* A Perl built-in function that does not accept arguments. */
+ last_token_type = token_type_variable;
+ }
+ }
+ }
+#if DEBUG_PERL
+ else
+ {
+ fprintf (stderr, "%s:%d: %s recycled from stack\n",
+ real_file_name, line_number, token2string (tp));
+ }
+#endif
+
+ /* A symbol followed by a fat comma is really a single-quoted string.
+ Function definitions or forward declarations also need a special
+ handling because the dollars and at signs inside the parentheses
+ must not be interpreted as the beginning of a variable ')'. */
+ if (tp->type == token_type_symbol || tp->type == token_type_named_op)
+ {
+ token_ty *next = token_stack_peek (&token_stack);
+
+ if (!next)
+ {
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: pre-fetching next token\n",
+ real_file_name, line_number);
+#endif
+ next = x_perl_lex (mlp);
+ x_perl_unlex (next);
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: unshifted next token\n",
+ real_file_name, line_number);
+#endif
+ }
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: next token is %s\n",
+ real_file_name, line_number, token2string (next));
+#endif
+
+ if (next->type == token_type_fat_comma)
+ {
+ tp->type = token_type_string;
+ tp->sub_type = string_type_q;
+ tp->comment = add_reference (savable_comment);
+#if DEBUG_PERL
+ fprintf (stderr,
+ "%s:%d: token %s mutated to token_type_string\n",
+ real_file_name, line_number, token2string (tp));
+#endif
+ }
+ else if (tp->type == token_type_symbol && tp->sub_type == symbol_type_sub
+ && next->type == token_type_symbol)
+ {
+ /* Start of a function declaration or definition. Mark this
+ symbol as a function name, so that we can later eat up
+ possible prototype information. */
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: subroutine declaration/definition '%s'\n",
+ real_file_name, line_number, next->string);
+#endif
+ next->sub_type = symbol_type_function;
+ }
+ else if (tp->type == token_type_symbol
+ && (tp->sub_type == symbol_type_sub
+ || tp->sub_type == symbol_type_function)
+ && next->type == token_type_lparen)
+ {
+ /* For simplicity we simply consume everything up to the
+ closing parenthesis. Actually only a limited set of
+ characters is allowed inside parentheses but we leave
+ complaints to the interpreter and are prepared for
+ future extensions to the Perl syntax. */
+ int c;
+
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: consuming prototype information\n",
+ real_file_name, line_number);
+#endif
+
+ do
+ {
+ c = phase1_getc ();
+#if DEBUG_PERL
+ fprintf (stderr, " consuming character '%c'\n", c);
+#endif
+ }
+ while (c != EOF && c != ')');
+ phase1_ungetc (c);
+ }
+ }
+
+ return tp;
+}
+
+static void
+x_perl_unlex (token_ty *tp)
+{
+ token_stack_push (&token_stack, tp);
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+/* Assuming TP is a string token, this function accumulates all subsequent
+ . string2 . string3 ... to the string. (String concatenation.) */
+
+static char *
+collect_message (message_list_ty *mlp, token_ty *tp, int error_level)
+{
+ char *string;
+ size_t len;
+
+ extract_quotelike_pass3 (tp, error_level);
+ string = xstrdup (tp->string);
+ len = strlen (tp->string) + 1;
+
+ for (;;)
+ {
+ int c;
+
+ do
+ c = phase2_getc ();
+ while (is_whitespace (c));
+
+ if (c != '.')
+ {
+ phase2_ungetc (c);
+ return string;
+ }
+
+ do
+ c = phase2_getc ();
+ while (is_whitespace (c));
+
+ phase2_ungetc (c);
+
+ if (c == '"' || c == '\'' || c == '`'
+ || ((c == '/' || c == '?')
+ && prefer_regexp_over_division (tp->last_type))
+ || c == 'q')
+ {
+ token_ty *qstring = x_perl_lex (mlp);
+ if (qstring->type != token_type_string)
+ {
+ /* assert (qstring->type == token_type_symbol) */
+ x_perl_unlex (qstring);
+ return string;
+ }
+
+ extract_quotelike_pass3 (qstring, error_level);
+ len += strlen (qstring->string);
+ string = xrealloc (string, len);
+ strcat (string, qstring->string);
+ free_token (qstring);
+ }
+ }
+}
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid Perl program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form.
+
+ In Perl, parentheses around function arguments can be omitted.
+
+ The general rules are:
+ 1) Functions declared with a prototype take exactly the specified number
+ of arguments.
+ sub one_arg ($) { ... }
+ sub two_args ($$) { ... }
+ 2) When a function name is immediately followed by an opening parenthesis,
+ the argument list ends at the corresponding closing parenthesis.
+
+ If rule 1 and rule 2 are contradictory, i.e. when the program calls a
+ function with an explicit argument list and the wrong number of arguments,
+ the program is invalid:
+ sub two_args ($$) { ... }
+ foo two_args (x), y - invalid due to rules 1 and 2
+
+ Ambiguities are resolved as follows:
+ 3) Some built-ins, such as 'abs', 'sqrt', 'sin', 'cos', ..., and functions
+ declared with a prototype of exactly one argument take exactly one
+ argument:
+ foo sin x, y ==> foo (sin (x), y)
+ sub one_arg ($) { ... }
+ foo one_arg x, y, z ==> foo (one_arg (x), y, z)
+ 4) Other identifiers, if not immediately followed by an opening
+ parenthesis, consume the entire remaining argument list:
+ foo bar x, y ==> foo (bar (x, y))
+ sub two_args ($$) { ... }
+ foo two_args x, y ==> foo (two_args (x, y))
+
+ Other series of comma separated expressions without a function name at
+ the beginning are comma expressions:
+ sub two_args ($$) { ... }
+ foo two_args x, (y, z) ==> foo (two_args (x, (y, z)))
+ Note that the evaluation of comma expressions returns a list of values
+ when in list context (e.g. inside the argument list of a function without
+ prototype) but only one value when inside the argument list of a function
+ with a prototype:
+ sub print3 ($$$) { print @_ }
+ print3 5, (6, 7), 8 ==> 578
+ print 5, (6, 7), 8 ==> 5678
+
+ Where rule 3 or 4 contradict rule 1 or 2, the program is invalid:
+ sin (x, y) - invalid due to rules 2 and 3
+ sub one_arg ($) { ... }
+ one_arg (x, y) - invalid due to rules 2 and 3
+ sub two_args ($$) { ... }
+ foo two_args x, y, z - invalid due to rules 1 and 4
+ */
+
+/* Extract messages until the next balanced closing parenthesis.
+ Extracted messages are added to MLP.
+
+ DELIM can be either token_type_rbrace, token_type_rbracket,
+ token_type_rparen. Additionally, if COMMA_DELIM is true, parsing
+ stops at the next comma outside parentheses.
+
+ ARG is the current argument list position, starts with 1.
+ ARGPARSER is the corresponding argument list parser.
+
+ Returns true for EOF, false otherwise. */
+
+static bool
+extract_balanced (message_list_ty *mlp,
+ token_type_ty delim, bool eat_delim, bool comma_delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ int arg, struct arglist_parser *argparser)
+{
+ /* Whether to implicitly assume the next tokens are arguments even without
+ a '('. */
+ bool next_is_argument = false;
+ /* Parameters of the keyword just seen. Defined only when next_is_argument
+ is true. */
+ const struct callshapes *next_shapes = NULL;
+ struct arglist_parser *next_argparser = NULL;
+
+ /* Whether to not consider strings until the next comma. */
+ bool skip_until_comma = false;
+
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+#if DEBUG_PERL
+ static int nesting_level = 0;
+
+ ++nesting_level;
+#endif
+
+ for (;;)
+ {
+ /* The current token. */
+ token_ty *tp;
+
+ tp = x_perl_lex (mlp);
+
+ if (delim == tp->type)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extract_balanced finished (%d)\n",
+ logical_file_name, tp->line_number, --nesting_level);
+#endif
+ if (eat_delim)
+ free_token (tp);
+ else
+ /* Preserve the delimiter for the caller. */
+ x_perl_unlex (tp);
+ return false;
+ }
+
+ if (comma_delim && tp->type == token_type_comma)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: extract_balanced finished at comma (%d)\n",
+ logical_file_name, tp->line_number, --nesting_level);
+#endif
+ x_perl_unlex (tp);
+ return false;
+ }
+
+ if (next_is_argument && tp->type != token_type_lparen)
+ {
+ /* An argument list starts, even though there is no '('. */
+ bool next_comma_delim;
+
+ x_perl_unlex (tp);
+
+ if (next_shapes != NULL)
+ /* We know something about the function being called. Assume
+ that it consumes only one argument if no argument number or
+ total > 1 is specified. */
+ {
+ size_t i;
+
+ next_comma_delim = true;
+ for (i = 0; i < next_shapes->nshapes; i++)
+ {
+ const struct callshape *shape = &next_shapes->shapes[i];
+
+ if (shape->argnum1 > 1
+ || shape->argnum2 > 1
+ || shape->argnumc > 1
+ || shape->argtotal > 1)
+ next_comma_delim = false;
+ }
+ }
+ else
+ /* We know nothing about the function being called. It could be
+ a function prototyped to take only one argument, or on the other
+ hand it could be prototyped to take more than one argument or an
+ arbitrary argument list or it could be unprototyped. Due to
+ the way the parser works, assuming the first case gives the
+ best results. */
+ next_comma_delim = true;
+
+ if (extract_balanced (mlp, delim, false, next_comma_delim,
+ inner_context, next_context_iter,
+ 1, next_argparser))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+
+ next_is_argument = false;
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ continue;
+ }
+
+ switch (tp->type)
+ {
+ case token_type_symbol:
+ case token_type_keyword_symbol:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type symbol (%d) \"%s\"\n",
+ logical_file_name, tp->line_number, nesting_level,
+ tp->string);
+#endif
+
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, tp->string, strlen (tp->string),
+ &keyword_value) == 0)
+ {
+ const struct callshapes *shapes =
+ (const struct callshapes *) keyword_value;
+
+ next_shapes = shapes;
+ next_argparser = arglist_parser_alloc (mlp, shapes);
+ }
+ else
+ {
+ next_shapes = NULL;
+ next_argparser = arglist_parser_alloc (mlp, NULL);
+ }
+ }
+ next_is_argument = true;
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ tp->string, strlen (tp->string)));
+ break;
+
+ case token_type_variable:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type variable (%d) \"%s\"\n",
+ logical_file_name, tp->line_number, nesting_level,
+ tp->string);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_object:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type object (%d) \"%s->\"\n",
+ logical_file_name, tp->line_number, nesting_level,
+ tp->string);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_lparen:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type left parenthesis (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ if (next_is_argument)
+ {
+ /* Parse the argument list of a function call. */
+ if (extract_balanced (mlp, token_type_rparen, true, false,
+ inner_context, next_context_iter,
+ 1, next_argparser))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ return true;
+ }
+ next_is_argument = false;
+ next_argparser = NULL;
+ }
+ else
+ {
+ /* Parse a parenthesized expression or comma expression. */
+ if (extract_balanced (mlp, token_type_rparen, true, false,
+ inner_context, next_context_iter,
+ arg, arglist_parser_clone (argparser)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ free_token (tp);
+ return true;
+ }
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ }
+ skip_until_comma = true;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_rparen:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type right parenthesis (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ skip_until_comma = true;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_comma:
+ case token_type_fat_comma:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type comma (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ if (arglist_parser_decidedp (argparser, arg))
+ {
+ /* We have missed the argument. */
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ argparser = arglist_parser_alloc (mlp, NULL);
+ arg = 0;
+ }
+ arg++;
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: arg: %d\n",
+ real_file_name, tp->line_number, arg);
+#endif
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ skip_until_comma = false;
+ next_context_iter = passthrough_context_list_iterator;
+ break;
+
+ case token_type_string:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type string (%d): \"%s\"\n",
+ logical_file_name, tp->line_number, nesting_level,
+ tp->string);
+#endif
+
+ if (extract_all)
+ {
+ char *string = collect_message (mlp, tp, EXIT_SUCCESS);
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = tp->line_number;
+ xgettext_current_source_encoding = po_charset_utf8;
+ remember_a_message (mlp, NULL, string, inner_context, &pos,
+ NULL, tp->comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ }
+ else if (!skip_until_comma)
+ {
+ /* Need to collect the complete string, with error checking,
+ only if the argument ARG is used in ARGPARSER. */
+ bool must_collect = false;
+ {
+ size_t nalternatives = argparser->nalternatives;
+ size_t i;
+
+ for (i = 0; i < nalternatives; i++)
+ {
+ struct partial_call *cp = &argparser->alternative[i];
+
+ if (arg == cp->argnumc
+ || arg == cp->argnum1 || arg == cp->argnum2)
+ must_collect = true;
+ }
+ }
+
+ if (must_collect)
+ {
+ char *string = collect_message (mlp, tp, EXIT_FAILURE);
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_remember (argparser, arg,
+ string, inner_context,
+ logical_file_name, tp->line_number,
+ tp->comment);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ }
+ }
+
+ if (arglist_parser_decidedp (argparser, arg))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ argparser = arglist_parser_alloc (mlp, NULL);
+ }
+
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_number:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type number (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_eof:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type EOF (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ free_token (tp);
+ return true;
+
+ case token_type_lbrace:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type lbrace (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ if (extract_balanced (mlp, token_type_rbrace, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ free_token (tp);
+ return true;
+ }
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_rbrace:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type rbrace (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_lbracket:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type lbracket (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ if (extract_balanced (mlp, token_type_rbracket, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ free_token (tp);
+ return true;
+ }
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_rbracket:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type rbracket (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_semicolon:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type semicolon (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+
+ /* The ultimate sign. */
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+ argparser = arglist_parser_alloc (mlp, NULL);
+
+ /* FIXME: Instead of resetting outer_context here, it may be better
+ to recurse in the next_is_argument handling above, waiting for
+ the next semicolon or other statement terminator. */
+ outer_context = null_context;
+ context_iter = null_context_list_iterator;
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = passthrough_context_list_iterator;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ break;
+
+ case token_type_dereference:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type dereference (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_dot:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type dot (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_named_op:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type named operator (%d): %s\n",
+ logical_file_name, tp->line_number, nesting_level,
+ tp->string);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_regex_op:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type regex operator (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ case token_type_other:
+#if DEBUG_PERL
+ fprintf (stderr, "%s:%d: type other (%d)\n",
+ logical_file_name, tp->line_number, nesting_level);
+#endif
+ next_is_argument = false;
+ if (next_argparser != NULL)
+ free (next_argparser);
+ next_argparser = NULL;
+ next_context_iter = null_context_list_iterator;
+ break;
+
+ default:
+ fprintf (stderr, "%s:%d: unknown token type %d\n",
+ real_file_name, tp->line_number, tp->type);
+ abort ();
+ }
+
+ free_token (tp);
+ }
+}
+
+void
+extract_perl (FILE *f, const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 0;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ token_stack.items = NULL;
+ token_stack.nitems = 0;
+ token_stack.nitems_max = 0;
+ linesize = 0;
+ linepos = 0;
+ eaten_here = 0;
+ end_of_file = false;
+
+ /* Safe assumption. */
+ last_token_type = token_type_semicolon;
+
+ /* Eat tokens until eof is seen. When extract_balanced returns
+ due to an unbalanced closing brace, just restart it. */
+ while (!extract_balanced (mlp, token_type_rbrace, true, false,
+ null_context, null_context_list_iterator,
+ 1, arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ free (logical_file_name);
+ logical_file_name = NULL;
+ line_number = 0;
+ last_token_type = token_type_semicolon;
+ token_stack_free (&token_stack);
+ eaten_here = 0;
+ end_of_file = true;
+}
diff --git a/gettext-tools/src/x-perl.h b/gettext-tools/src/x-perl.h
new file mode 100644
index 0000000..8e140e9
--- /dev/null
+++ b/gettext-tools/src/x-perl.h
@@ -0,0 +1,55 @@
+/* xgettext Perl backend.
+ Copyright (C) 2002-2003, 2006, 2010 Free Software Foundation, Inc.
+ Written by Guido Flohr <guido@imperia.net>, 2002-2003
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_PERL \
+ { "pl", "perl" }, \
+ { "PL", "perl" }, \
+ { "pm", "perl" }, \
+ { "perl", "perl" }, \
+ { "cgi", "perl" }, \
+
+#define SCANNERS_PERL \
+ { "perl", extract_perl, \
+ &flag_table_perl, &formatstring_perl, &formatstring_perl_brace, NULL }, \
+
+/* Scan a Perl file and add its translatable strings to mdlp. */
+extern void extract_perl (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_perl_keyword (const char *keyword);
+extern void x_perl_extract_all (void);
+
+extern void init_flag_table_perl (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-php.c b/gettext-tools/src/x-php.c
new file mode 100644
index 0000000..74b8ece
--- /dev/null
+++ b/gettext-tools/src/x-php.c
@@ -0,0 +1,1605 @@
+/* xgettext PHP backend.
+ Copyright (C) 2001-2003, 2005-2010 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-php.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The PHP syntax is defined in phpdoc/manual/langref.html.
+ See also php-4.1.0/Zend/zend_language_scanner.l
+ and php-4.1.0/Zend/zend_language_parser.y.
+ Note that variable and function names can contain bytes in the range
+ 0x7f..0xff; see
+ http://www.php.net/manual/en/language.variables.php
+ http://www.php.net/manual/en/language.functions.php */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_php_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_php_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_php_keyword ("_");
+ x_php_keyword ("gettext");
+ x_php_keyword ("dgettext:2");
+ x_php_keyword ("dcgettext:2");
+ /* The following were added in PHP 4.2.0. */
+ x_php_keyword ("ngettext:1,2");
+ x_php_keyword ("dngettext:2,3");
+ x_php_keyword ("dcngettext:2,3");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_php ()
+{
+ xgettext_record_flag ("_:1:pass-php-format");
+ xgettext_record_flag ("gettext:1:pass-php-format");
+ xgettext_record_flag ("dgettext:2:pass-php-format");
+ xgettext_record_flag ("dcgettext:2:pass-php-format");
+ xgettext_record_flag ("ngettext:1:pass-php-format");
+ xgettext_record_flag ("ngettext:2:pass-php-format");
+ xgettext_record_flag ("dngettext:2:pass-php-format");
+ xgettext_record_flag ("dngettext:3:pass-php-format");
+ xgettext_record_flag ("dcngettext:2:pass-php-format");
+ xgettext_record_flag ("dcngettext:3:pass-php-format");
+ xgettext_record_flag ("sprintf:1:php-format");
+ xgettext_record_flag ("printf:1:php-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 1. line_number handling. */
+
+static unsigned char phase1_pushback[2];
+static int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ c = phase1_pushback[--phase1_pushback_length];
+ else
+ {
+ c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+ }
+
+ if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Supports 2 characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* 2. Ignore HTML sections. They are equivalent to PHP echo commands and
+ therefore don't contain translatable strings. */
+
+static void
+skip_html ()
+{
+ for (;;)
+ {
+ int c = phase1_getc ();
+
+ if (c == EOF)
+ return;
+
+ if (c == '<')
+ {
+ int c2 = phase1_getc ();
+
+ if (c2 == EOF)
+ break;
+
+ if (c2 == '?')
+ {
+ /* <?php is the normal way to enter PHP mode. <? and <?= are
+ recognized by PHP depending on a configuration setting. */
+ int c3 = phase1_getc ();
+
+ if (c3 != '=')
+ phase1_ungetc (c3);
+
+ return;
+ }
+
+ if (c2 == '%')
+ {
+ /* <% and <%= are recognized by PHP depending on a configuration
+ setting. */
+ int c3 = phase1_getc ();
+
+ if (c3 != '=')
+ phase1_ungetc (c3);
+
+ return;
+ }
+
+ if (c2 == '<')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+
+ /* < script language = php >
+ < script language = "php" >
+ < script language = 'php' >
+ are always recognized. */
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 != 's' && c2 != 'S')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'c' && c2 != 'C')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'r' && c2 != 'R')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'i' && c2 != 'I')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'p' && c2 != 'P')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 't' && c2 != 'T')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (!(c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r'))
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ do
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
+ if (c2 != 'l' && c2 != 'L')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'a' && c2 != 'A')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'n' && c2 != 'N')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'g' && c2 != 'G')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'u' && c2 != 'U')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'a' && c2 != 'A')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'g' && c2 != 'G')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'e' && c2 != 'E')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 != '=')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 == '"')
+ {
+ c2 = phase1_getc ();
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'h')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != '"')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ }
+ else if (c2 == '\'')
+ {
+ c2 = phase1_getc ();
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'h')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != '\'')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ }
+ else
+ {
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'h')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ c2 = phase1_getc ();
+ if (c2 != 'p')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ }
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 != '>')
+ {
+ phase1_ungetc (c2);
+ continue;
+ }
+ return;
+ }
+ }
+}
+
+#if 0
+
+static unsigned char phase2_pushback[1];
+static int phase2_pushback_length;
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ c = phase1_getc ();
+ switch (c)
+ {
+ case '?':
+ case '%':
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '>')
+ {
+ /* ?> and %> terminate PHP mode and switch back to HTML mode. */
+ skip_html ();
+ return ' ';
+ }
+ phase1_ungetc (c2);
+ }
+ break;
+
+ case '<':
+ {
+ int c2 = phase1_getc ();
+
+ /* < / script > terminates PHP mode and switches back to HTML mode. */
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 == '/')
+ {
+ do
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
+ if (c2 == 's' || c2 == 'S')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'c' || c2 == 'C')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'r' || c2 == 'R')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'i' || c2 == 'I')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'p' || c2 == 'P')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 't' || c2 == 'T')
+ {
+ do
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t'
+ || c2 == '\n' || c2 == '\r');
+ if (c2 == '>')
+ {
+ skip_html ();
+ return ' ';
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ phase1_ungetc (c2);
+ }
+ break;
+ }
+
+ return c;
+}
+
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+#endif
+
+
+/* Accumulating comments. */
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* 3. Replace each comment that is not inside a string literal with a
+ space character. We need to remember the comment for later, because
+ it may be attached to a keyword string. */
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+static unsigned char phase3_pushback[1];
+static int phase3_pushback_length;
+
+static int
+phase3_getc ()
+{
+ int lineno;
+ int c;
+
+ if (phase3_pushback_length)
+ return phase3_pushback[--phase3_pushback_length];
+
+ c = phase1_getc ();
+
+ if (c == '#')
+ {
+ /* sh comment. */
+ bool last_was_qmark = false;
+
+ comment_start ();
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ {
+ comment_line_end (0);
+ break;
+ }
+ if (last_was_qmark && c == '>')
+ {
+ comment_line_end (1);
+ skip_html ();
+ break;
+ }
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ last_was_qmark = (c == '?' || c == '%');
+ }
+ last_comment_line = lineno;
+ return '\n';
+ }
+ else if (c == '/')
+ {
+ c = phase1_getc ();
+
+ switch (c)
+ {
+ default:
+ phase1_ungetc (c);
+ return '/';
+
+ case '*':
+ {
+ /* C comment. */
+ bool last_was_star;
+
+ comment_start ();
+ lineno = line_number;
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (buflen == 0 && (c == ' ' || c == '\t'))
+ continue;
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ lineno = line_number;
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = lineno;
+ return ' ';
+ }
+
+ case '/':
+ {
+ /* C++ comment. */
+ bool last_was_qmark = false;
+
+ comment_start ();
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ {
+ comment_line_end (0);
+ break;
+ }
+ if (last_was_qmark && c == '>')
+ {
+ comment_line_end (1);
+ skip_html ();
+ break;
+ }
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ last_was_qmark = (c == '?' || c == '%');
+ }
+ last_comment_line = lineno;
+ return '\n';
+ }
+ }
+ }
+ else
+ return c;
+}
+
+#ifdef unused
+static void
+phase3_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = c;
+ }
+}
+#endif
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_lbracket, /* [ */
+ token_type_rbracket, /* ] */
+ token_type_dot, /* . */
+ token_type_operator1, /* * / % ++ -- */
+ token_type_operator2, /* + - ! ~ @ */
+ token_type_string_literal, /* "abc" */
+ token_type_symbol, /* symbol, number */
+ token_type_other /* misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ int line_number;
+};
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string_literal)
+ drop_reference (tp->comment);
+}
+
+
+/* 4. Combine characters into tokens. Discard whitespace. */
+
+static token_ty phase4_pushback[3];
+static int phase4_pushback_length;
+
+static void
+phase4_get (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+
+ if (phase4_pushback_length)
+ {
+ *tp = phase4_pushback[--phase4_pushback_length];
+ return;
+ }
+ tp->string = NULL;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase3_getc ();
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\t':
+ case '\r':
+ /* Ignore whitespace. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 127: case 128: case 129: case 130: case 131: case 132: case 133:
+ case 134: case 135: case 136: case 137: case 138: case 139: case 140:
+ case 141: case 142: case 143: case 144: case 145: case 146: case 147:
+ case 148: case 149: case 150: case 151: case 152: case 153: case 154:
+ case 155: case 156: case 157: case 158: case 159: case 160: case 161:
+ case 162: case 163: case 164: case 165: case 166: case 167: case 168:
+ case 169: case 170: case 171: case 172: case 173: case 174: case 175:
+ case 176: case 177: case 178: case 179: case 180: case 181: case 182:
+ case 183: case 184: case 185: case 186: case 187: case 188: case 189:
+ case 190: case 191: case 192: case 193: case 194: case 195: case 196:
+ case 197: case 198: case 199: case 200: case 201: case 202: case 203:
+ case 204: case 205: case 206: case 207: case 208: case 209: case 210:
+ case 211: case 212: case 213: case 214: case 215: case 216: case 217:
+ case 218: case 219: case 220: case 221: case 222: case 223: case 224:
+ case 225: case 226: case 227: case 228: case 229: case 230: case 231:
+ case 232: case 233: case 234: case 235: case 236: case 237: case 238:
+ case 239: case 240: case 241: case 242: case 243: case 244: case 245:
+ case 246: case 247: case 248: case 249: case 250: case 251: case 252:
+ case 253: case 254: case 255:
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 127: case 128: case 129: case 130: case 131: case 132:
+ case 133: case 134: case 135: case 136: case 137: case 138:
+ case 139: case 140: case 141: case 142: case 143: case 144:
+ case 145: case 146: case 147: case 148: case 149: case 150:
+ case 151: case 152: case 153: case 154: case 155: case 156:
+ case 157: case 158: case 159: case 160: case 161: case 162:
+ case 163: case 164: case 165: case 166: case 167: case 168:
+ case 169: case 170: case 171: case 172: case 173: case 174:
+ case 175: case 176: case 177: case 178: case 179: case 180:
+ case 181: case 182: case 183: case 184: case 185: case 186:
+ case 187: case 188: case 189: case 190: case 191: case 192:
+ case 193: case 194: case 195: case 196: case 197: case 198:
+ case 199: case 200: case 201: case 202: case 203: case 204:
+ case 205: case 206: case 207: case 208: case 209: case 210:
+ case 211: case 212: case 213: case 214: case 215: case 216:
+ case 217: case 218: case 219: case 220: case 221: case 222:
+ case 223: case 224: case 225: case 226: case 227: case 228:
+ case 229: case 230: case 231: case 232: case 233: case 234:
+ case 235: case 236: case 237: case 238: case 239: case 240:
+ case 241: case 242: case 243: case 244: case 245: case 246:
+ case 247: case 248: case 249: case 250: case 251: case 252:
+ case 253: case 254: case 255:
+ continue;
+
+ default:
+ phase1_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ return;
+
+ case '\'':
+ /* Single-quoted string literal. */
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF || c == '\'')
+ break;
+ if (c == '\\')
+ {
+ c = phase1_getc ();
+ if (c != '\\' && c != '\'')
+ {
+ phase1_ungetc (c);
+ c = '\\';
+ }
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ tp->type = token_type_string_literal;
+ tp->string = xstrdup (buffer);
+ tp->comment = add_reference (savable_comment);
+ return;
+
+ case '"':
+ /* Double-quoted string literal. */
+ tp->type = token_type_string_literal;
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF || c == '"')
+ break;
+ if (c == '$')
+ {
+ c = phase1_getc ();
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || c == '_' || c == '{' || c >= 0x7f)
+ {
+ /* String with variables. */
+ tp->type = token_type_other;
+ continue;
+ }
+ phase1_ungetc (c);
+ c = '$';
+ }
+ if (c == '{')
+ {
+ c = phase1_getc ();
+ if (c == '$')
+ {
+ /* String with expressions. */
+ tp->type = token_type_other;
+ continue;
+ }
+ phase1_ungetc (c);
+ c = '{';
+ }
+ if (c == '\\')
+ {
+ int n, j;
+
+ c = phase1_getc ();
+ switch (c)
+ {
+ case '"':
+ case '\\':
+ case '$':
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ n = 0;
+ for (j = 0; j < 3; ++j)
+ {
+ n = n * 8 + c - '0';
+ c = phase1_getc ();
+ switch (c)
+ {
+ default:
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ continue;
+ }
+ break;
+ }
+ phase1_ungetc (c);
+ c = n;
+ break;
+
+ case 'x':
+ n = 0;
+ for (j = 0; j < 2; ++j)
+ {
+ c = phase1_getc ();
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = n * 16 + c - '0';
+ break;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ n = n * 16 + 10 + c - 'A';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ n = n * 16 + 10 + c - 'a';
+ break;
+ default:
+ phase1_ungetc (c);
+ c = 0;
+ break;
+ }
+ if (c == 0)
+ break;
+ }
+ if (j == 0)
+ {
+ phase1_ungetc ('x');
+ c = '\\';
+ }
+ else
+ c = n;
+ break;
+
+ case 'n':
+ c = '\n';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+
+ default:
+ phase1_ungetc (c);
+ c = '\\';
+ break;
+ }
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ if (tp->type == token_type_string_literal)
+ {
+ tp->string = xstrdup (buffer);
+ tp->comment = add_reference (savable_comment);
+ }
+ return;
+
+ case '?':
+ case '%':
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '>')
+ {
+ /* ?> and %> terminate PHP mode and switch back to HTML
+ mode. */
+ skip_html ();
+ tp->type = token_type_other;
+ }
+ else
+ {
+ phase1_ungetc (c2);
+ tp->type = (c == '%' ? token_type_operator1 : token_type_other);
+ }
+ return;
+ }
+
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '[':
+ tp->type = token_type_lbracket;
+ return;
+
+ case ']':
+ tp->type = token_type_rbracket;
+ return;
+
+ case '.':
+ tp->type = token_type_dot;
+ return;
+
+ case '*':
+ case '/':
+ tp->type = token_type_operator1;
+ return;
+
+ case '+':
+ case '-':
+ {
+ int c2 = phase1_getc ();
+ if (c2 == c)
+ /* ++ or -- */
+ tp->type = token_type_operator1;
+ else
+ /* + or - */
+ {
+ phase1_ungetc (c2);
+ tp->type = token_type_operator2;
+ }
+ return;
+ }
+
+ case '!':
+ case '~':
+ case '@':
+ tp->type = token_type_operator2;
+ return;
+
+ case '<':
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '<')
+ {
+ int c3 = phase1_getc ();
+ if (c3 == '<')
+ {
+ int label_start = 0;
+
+ /* Start of here and now document.
+ Parse whitespace, then label, then newline. */
+ do
+ c = phase3_getc ();
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+
+ bufpos = 0;
+ do
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase3_getc ();
+ }
+ while (c != EOF && c != '\n' && c != '\r');
+ /* buffer[0..bufpos-1] now contains the label
+ (including single or double quotes). */
+
+ if (*buffer == '\'' || *buffer == '"')
+ {
+ label_start++;
+ bufpos--;
+ }
+
+ /* Now skip the here document. */
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c == '\n' || c == '\r')
+ {
+ int bufidx = label_start;
+
+ while (bufidx < bufpos)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c != buffer[bufidx])
+ {
+ phase1_ungetc (c);
+ break;
+ }
+ bufidx++;
+ }
+ if (bufidx == bufpos)
+ {
+ c = phase1_getc ();
+ if (c != ';')
+ phase1_ungetc (c);
+ c = phase1_getc ();
+ if (c == '\n' || c == '\r')
+ break;
+ }
+ }
+ }
+
+ /* FIXME: Ideally we should turn the here document into a
+ string literal if it didn't contain $ substitution. And
+ we should also respect backslash escape sequences like
+ in double-quoted strings. */
+ tp->type = token_type_other;
+ return;
+ }
+ phase1_ungetc (c3);
+ }
+
+ /* < / script > terminates PHP mode and switches back to HTML
+ mode. */
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
+ c2 = phase1_getc ();
+ if (c2 == '/')
+ {
+ do
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
+ if (c2 == 's' || c2 == 'S')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'c' || c2 == 'C')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'r' || c2 == 'R')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'i' || c2 == 'I')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 'p' || c2 == 'P')
+ {
+ c2 = phase1_getc ();
+ if (c2 == 't' || c2 == 'T')
+ {
+ do
+ c2 = phase1_getc ();
+ while (c2 == ' ' || c2 == '\t'
+ || c2 == '\n' || c2 == '\r');
+ if (c2 == '>')
+ {
+ skip_html ();
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+ }
+ else
+ phase1_ungetc (c2);
+
+ tp->type = token_type_other;
+ return;
+ }
+
+ case '`':
+ /* Execution operator. */
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports 3 tokens of pushback. */
+static void
+phase4_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase4_pushback_length == SIZEOF (phase4_pushback))
+ abort ();
+ phase4_pushback[phase4_pushback_length++] = *tp;
+ }
+}
+
+
+/* 5. Compile-time optimization of string literal concatenation.
+ Combine "string1" . ... . "stringN" to the concatenated string if
+ - the token before this expression is none of
+ '+' '-' '.' '*' '/' '%' '!' '~' '++' '--' ')' '@'
+ (because then the first string could be part of an expression with
+ the same or higher precedence as '.', such as an additive,
+ multiplicative, negation, preincrement, or cast expression),
+ - the token after this expression is none of
+ '*' '/' '%' '++' '--'
+ (because then the last string could be part of an expression with
+ higher precedence as '.', such as a multiplicative or postincrement
+ expression). */
+
+static token_type_ty phase5_last;
+
+static void
+x_php_lex (token_ty *tp)
+{
+ phase4_get (tp);
+ if (tp->type == token_type_string_literal
+ && !(phase5_last == token_type_dot
+ || phase5_last == token_type_operator1
+ || phase5_last == token_type_operator2
+ || phase5_last == token_type_rparen))
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase4_get (&token2);
+ if (token2.type == token_type_dot)
+ {
+ token_ty token3;
+
+ phase4_get (&token3);
+ if (token3.type == token_type_string_literal)
+ {
+ token_ty token_after;
+
+ phase4_get (&token_after);
+ if (token_after.type != token_type_operator1)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ phase4_unget (&token_after);
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase4_unget (&token_after);
+ }
+ phase4_unget (&token3);
+ }
+ phase4_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+ phase5_last = tp->type;
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis or bracket.
+ Extracted messages are added to MLP.
+ DELIM can be either token_type_rparen or token_type_rbracket, or
+ token_type_eof to accept both.
+ Return true upon eof, false upon closing parenthesis or bracket. */
+static bool
+extract_balanced (message_list_ty *mlp,
+ token_type_ty delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_php_lex (&token);
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_balanced (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ if (delim == token_type_rparen || delim == token_type_eof)
+ {
+ arglist_parser_done (argparser, arg);
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbracket:
+ if (extract_balanced (mlp, token_type_rbracket,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rbracket:
+ if (delim == token_type_rbracket || delim == token_type_eof)
+ {
+ arglist_parser_done (argparser, arg);
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string_literal:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ token.comment);
+ drop_reference (token.comment);
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_dot:
+ case token_type_operator1:
+ case token_type_operator2:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ arglist_parser_done (argparser, arg);
+ return true;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_php (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ phase5_last = token_type_eof;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Initial mode is HTML mode, not PHP mode. */
+ skip_html ();
+
+ /* Eat tokens until eof is seen. When extract_balanced returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_balanced (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-php.h b/gettext-tools/src/x-php.h
new file mode 100644
index 0000000..79442b4
--- /dev/null
+++ b/gettext-tools/src/x-php.h
@@ -0,0 +1,53 @@
+/* xgettext PHP backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_PHP \
+ { "php", "PHP" }, \
+ { "php3", "PHP" }, \
+ { "php4", "PHP" }, \
+
+#define SCANNERS_PHP \
+ { "PHP", extract_php, \
+ &flag_table_php, &formatstring_php, NULL, NULL }, \
+
+/* Scan a PHP file and add its translatable strings to mdlp. */
+extern void extract_php (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_php_keyword (const char *keyword);
+extern void x_php_extract_all (void);
+
+extern void init_flag_table_php (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-po.c b/gettext-tools/src/x-po.c
new file mode 100644
index 0000000..89824e2
--- /dev/null
+++ b/gettext-tools/src/x-po.c
@@ -0,0 +1,240 @@
+/* xgettext PO and JavaProperties backends.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include "x-po.h"
+#include "x-properties.h"
+#include "x-stringtable.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "xalloc.h"
+#include "read-catalog.h"
+#include "read-po.h"
+#include "read-properties.h"
+#include "read-stringtable.h"
+#include "po-lex.h"
+#include "gettext.h"
+
+/* A convenience macro. I don't like writing gettext() every time. */
+#define _(str) gettext (str)
+
+
+/* The charset found in the header entry. */
+static char *header_charset;
+
+/* Define a subclass extract_catalog_reader_ty of default_catalog_reader_ty. */
+
+static void
+extract_add_message (default_catalog_reader_ty *this,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ /* See whether we shall exclude this message. */
+ if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL)
+ goto discard;
+
+ /* If the msgid is the empty string, it is the old header. Throw it
+ away, we have constructed a new one. Only remember its charset.
+ But if no new one was constructed, keep the old header. This is useful
+ because the old header may contain a charset= directive. */
+ if (msgctxt == NULL && *msgid == '\0' && !xgettext_omit_header)
+ {
+ {
+ const char *charsetstr = strstr (msgstr, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len;
+ char *charset;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+ charset = XNMALLOC (len + 1, char);
+ memcpy (charset, charsetstr, len);
+ charset[len] = '\0';
+
+ if (header_charset != NULL)
+ free (header_charset);
+ header_charset = charset;
+ }
+ }
+
+ discard:
+ if (msgctxt != NULL)
+ free (msgctxt);
+ free (msgid);
+ if (msgid_plural != NULL)
+ free (msgid_plural);
+ free (msgstr);
+ if (prev_msgctxt != NULL)
+ free (prev_msgctxt);
+ if (prev_msgid != NULL)
+ free (prev_msgid);
+ if (prev_msgid_plural != NULL)
+ free (prev_msgid_plural);
+ return;
+ }
+
+ /* Invoke superclass method. */
+ default_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+
+/* So that the one parser can be used for multiple programs, and also
+ use good data hiding and encapsulation practices, an object
+ oriented approach has been taken. An object instance is allocated,
+ and all actions resulting from the parse will be through
+ invocations of method functions of that object. */
+
+static default_catalog_reader_class_ty extract_methods =
+{
+ {
+ sizeof (default_catalog_reader_ty),
+ default_constructor,
+ default_destructor,
+ default_parse_brief,
+ default_parse_debrief,
+ default_directive_domain,
+ default_directive_message,
+ default_comment,
+ default_comment_dot,
+ default_comment_filepos,
+ default_comment_special
+ },
+ default_set_domain, /* set_domain */
+ extract_add_message, /* add_message */
+ NULL /* frob_new_message */
+};
+
+
+static void
+extract (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ catalog_input_format_ty input_syntax,
+ msgdomain_list_ty *mdlp)
+{
+ default_catalog_reader_ty *pop;
+
+ header_charset = NULL;
+
+ pop = default_catalog_reader_alloc (&extract_methods);
+ pop->handle_comments = true;
+ pop->allow_domain_directives = false;
+ pop->allow_duplicates = false;
+ pop->allow_duplicates_if_same_msgstr = true;
+ pop->file_name = real_filename;
+ pop->mdlp = NULL;
+ pop->mlp = mdlp->item[0]->messages;
+ catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename,
+ logical_filename, input_syntax);
+ catalog_reader_free ((abstract_catalog_reader_ty *) pop);
+
+ if (header_charset != NULL)
+ {
+ if (!xgettext_omit_header)
+ {
+ /* Put the old charset into the freshly constructed header entry. */
+ message_ty *mp =
+ message_list_search (mdlp->item[0]->messages, NULL, "");
+
+ if (mp != NULL && !mp->obsolete)
+ {
+ const char *header = mp->msgstr;
+
+ if (header != NULL)
+ {
+ const char *charsetstr = strstr (header, "charset=");
+
+ if (charsetstr != NULL)
+ {
+ size_t len, len1, len2, len3;
+ char *new_header;
+
+ charsetstr += strlen ("charset=");
+ len = strcspn (charsetstr, " \t\n");
+
+ len1 = charsetstr - header;
+ len2 = strlen (header_charset);
+ len3 = (header + strlen (header)) - (charsetstr + len);
+ new_header = XNMALLOC (len1 + len2 + len3 + 1, char);
+ memcpy (new_header, header, len1);
+ memcpy (new_header + len1, header_charset, len2);
+ memcpy (new_header + len1 + len2, charsetstr + len, len3 + 1);
+ mp->msgstr = new_header;
+ mp->msgstr_len = len1 + len2 + len3 + 1;
+ }
+ }
+ }
+ }
+
+ free (header_charset);
+ }
+}
+
+
+void
+extract_po (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ extract (fp, real_filename, logical_filename, &input_format_po, mdlp);
+}
+
+
+void
+extract_properties (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ extract (fp, real_filename, logical_filename, &input_format_properties,
+ mdlp);
+}
+
+
+void
+extract_stringtable (FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ extract (fp, real_filename, logical_filename, &input_format_stringtable,
+ mdlp);
+}
diff --git a/gettext-tools/src/x-po.h b/gettext-tools/src/x-po.h
new file mode 100644
index 0000000..0d350d8
--- /dev/null
+++ b/gettext-tools/src/x-po.h
@@ -0,0 +1,46 @@
+/* xgettext PO backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_PO \
+ { "po", "PO" }, \
+ { "pot", "PO" }, \
+
+#define SCANNERS_PO \
+ { "PO", extract_po, NULL, NULL, NULL, NULL }, \
+
+/* Scan a PO file and add its translatable strings to mdlp. */
+extern void extract_po (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-properties.h b/gettext-tools/src/x-properties.h
new file mode 100644
index 0000000..fd16ab3
--- /dev/null
+++ b/gettext-tools/src/x-properties.h
@@ -0,0 +1,45 @@
+/* xgettext JavaProperties backend.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_PROPERTIES \
+ { "properties", "JavaProperties" }, \
+
+#define SCANNERS_PROPERTIES \
+ { "JavaProperties", extract_properties, NULL, NULL, NULL, NULL }, \
+
+/* Scan a JavaProperties file and add its translatable strings to mdlp. */
+extern void extract_properties (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-python.c b/gettext-tools/src/x-python.c
new file mode 100644
index 0000000..d781ef2
--- /dev/null
+++ b/gettext-tools/src/x-python.c
@@ -0,0 +1,1779 @@
+/* xgettext Python backend.
+ Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-python.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "basename.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "xalloc.h"
+#include "c-strstr.h"
+#include "c-ctype.h"
+#include "po-charset.h"
+#include "uniname.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The Python syntax is defined in the Python Reference Manual
+ /usr/share/doc/packages/python/html/ref/index.html.
+ See also Python-2.0/Parser/tokenizer.c, Python-2.0/Python/compile.c,
+ Python-2.0/Objects/unicodeobject.c. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_python_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_python_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_python_keyword ("gettext");
+ x_python_keyword ("ugettext");
+ x_python_keyword ("dgettext:2");
+ x_python_keyword ("ngettext:1,2");
+ x_python_keyword ("ungettext:1,2");
+ x_python_keyword ("dngettext:2,3");
+ x_python_keyword ("_");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_python ()
+{
+ xgettext_record_flag ("gettext:1:pass-python-format");
+ xgettext_record_flag ("ugettext:1:pass-python-format");
+ xgettext_record_flag ("dgettext:2:pass-python-format");
+ xgettext_record_flag ("ngettext:1:pass-python-format");
+ xgettext_record_flag ("ngettext:2:pass-python-format");
+ xgettext_record_flag ("ungettext:1:pass-python-format");
+ xgettext_record_flag ("ungettext:2:pass-python-format");
+ xgettext_record_flag ("dngettext:2:pass-python-format");
+ xgettext_record_flag ("dngettext:3:pass-python-format");
+ xgettext_record_flag ("_:1:pass-python-format");
+ /* xgettext_record_flag ("%:1:python-format"); // % is an infix operator! */
+
+ xgettext_record_flag ("gettext:1:pass-python-brace-format");
+ xgettext_record_flag ("ugettext:1:pass-python-brace-format");
+ xgettext_record_flag ("dgettext:2:pass-python-brace-format");
+ xgettext_record_flag ("ngettext:1:pass-python-brace-format");
+ xgettext_record_flag ("ngettext:2:pass-python-brace-format");
+ xgettext_record_flag ("ungettext:1:pass-python-brace-format");
+ xgettext_record_flag ("ungettext:2:pass-python-brace-format");
+ xgettext_record_flag ("dngettext:2:pass-python-brace-format");
+ xgettext_record_flag ("dngettext:3:pass-python-brace-format");
+ xgettext_record_flag ("_:1:pass-python-brace-format");
+ /* xgettext_record_flag ("format:1:python-brace-format"); */
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 0. Terminate line by \n, regardless whether the external
+ representation of a line terminator is CR (Mac), and CR/LF
+ (DOS/Windows), as Python treats them equally. */
+static int
+phase0_getc ()
+{
+ int c;
+
+ c = getc (fp);
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\r')
+ {
+ int c1 = getc (fp);
+
+ if (c1 != EOF && c1 != '\n')
+ ungetc (c1, fp);
+
+ /* Seen line terminator CR or CR/LF. */
+ return '\n';
+ }
+
+ return c;
+}
+
+/* Supports only one pushback character, and not '\n'. */
+static inline void
+phase0_ungetc (int c)
+{
+ if (c != EOF)
+ ungetc (c, fp);
+}
+
+
+/* 1. line_number handling. */
+
+/* Maximum used, roughly a safer MB_LEN_MAX. */
+#define MAX_PHASE1_PUSHBACK 16
+static unsigned char phase1_pushback[MAX_PHASE1_PUSHBACK];
+static int phase1_pushback_length;
+
+/* Read the next single byte from the input file. */
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ c = phase1_pushback[--phase1_pushback_length];
+ else
+ c = phase0_getc ();
+
+ if (c == '\n')
+ ++line_number;
+
+ return c;
+}
+
+/* Supports MAX_PHASE1_PUSHBACK characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* Phase 2: Conversion to Unicode.
+ This is done early because PEP 0263 specifies that conversion to Unicode
+ conceptually occurs before tokenization. A test case where it matters
+ is with encodings like BIG5: when a double-byte character ending in 0x5C
+ is followed by '\' or 'u0021', the tokenizer must not treat the second
+ half of the double-byte character as a backslash. */
+
+/* End-of-file indicator for functions returning an UCS-4 character. */
+#define UEOF -1
+
+static lexical_context_ty lexical_context;
+
+static int phase2_pushback[max (9, UNINAME_MAX + 3)];
+static int phase2_pushback_length;
+
+/* Read the next Unicode UCS-4 character from the input file. */
+static int
+phase2_getc ()
+{
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (xgettext_current_source_encoding == po_charset_ascii)
+ {
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ if (!c_isascii (c))
+ {
+ multiline_error (xstrdup (""),
+ xasprintf ("%s\n%s\n",
+ non_ascii_error_message (lexical_context,
+ real_file_name,
+ line_number),
+ _("\
+Please specify the source encoding through --from-code or through a comment\n\
+as specified in http://www.python.org/peps/pep-0263.html.\n")));
+ exit (EXIT_FAILURE);
+ }
+ return c;
+ }
+ else if (xgettext_current_source_encoding != po_charset_utf8)
+ {
+#if HAVE_ICONV
+ /* Use iconv on an increasing number of bytes. Read only as many bytes
+ through phase1_getc as needed. This is needed to give reasonable
+ interactive behaviour when fp is connected to an interactive tty. */
+ unsigned char buf[MAX_PHASE1_PUSHBACK];
+ size_t bufcount;
+ int c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = (unsigned char) c;
+ bufcount = 1;
+
+ for (;;)
+ {
+ unsigned char scratchbuf[6];
+ const char *inptr = (const char *) &buf[0];
+ size_t insize = bufcount;
+ char *outptr = (char *) &scratchbuf[0];
+ size_t outsize = sizeof (scratchbuf);
+
+ size_t res = iconv (xgettext_current_source_iconv,
+ (ICONV_CONST char **) &inptr, &insize,
+ &outptr, &outsize);
+ /* We expect that a character has been produced if and only if
+ some input bytes have been consumed. */
+ if ((insize < bufcount) != (outsize < sizeof (scratchbuf)))
+ abort ();
+ if (outsize == sizeof (scratchbuf))
+ {
+ /* No character has been produced. Must be an error. */
+ if (res != (size_t)(-1))
+ abort ();
+
+ if (errno == EILSEQ)
+ {
+ /* An invalid multibyte sequence was encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the correct source encoding through --from-code or through a\n\
+comment as specified in http://www.python.org/peps/pep-0263.html.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ else if (errno == EINVAL)
+ {
+ /* An incomplete multibyte character. */
+ int c;
+
+ if (bufcount == MAX_PHASE1_PUSHBACK)
+ {
+ /* An overlong incomplete multibyte sequence was
+ encountered. */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Long incomplete multibyte sequence.\n\
+Please specify the correct source encoding through --from-code or through a\n\
+comment as specified in http://www.python.org/peps/pep-0263.html.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+
+ /* Read one more byte and retry iconv. */
+ c = phase1_getc ();
+ if (c == EOF)
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of file.\n\
+Please specify the correct source encoding through --from-code or through a\n\
+comment as specified in http://www.python.org/peps/pep-0263.html.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ if (c == '\n')
+ {
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Incomplete multibyte sequence at end of line.\n\
+Please specify the correct source encoding through --from-code or through a\n\
+comment as specified in http://www.python.org/peps/pep-0263.html.\n"),
+ real_file_name, line_number - 1));
+ exit (EXIT_FAILURE);
+ }
+ buf[bufcount++] = (unsigned char) c;
+ }
+ else
+ error (EXIT_FAILURE, errno, _("%s:%d: iconv failure"),
+ real_file_name, line_number);
+ }
+ else
+ {
+ size_t outbytes = sizeof (scratchbuf) - outsize;
+ size_t bytes = bufcount - insize;
+ ucs4_t uc;
+
+ /* We expect that one character has been produced. */
+ if (bytes == 0)
+ abort ();
+ if (outbytes == 0)
+ abort ();
+ /* Push back the unused bytes. */
+ while (insize > 0)
+ phase1_ungetc (buf[--insize]);
+ /* Convert the character from UTF-8 to UCS-4. */
+ if (u8_mbtoucr (&uc, scratchbuf, outbytes) < (int) outbytes)
+ {
+ /* scratchbuf contains an out-of-range Unicode character
+ (> 0x10ffff). */
+ multiline_error (xstrdup (""),
+ xasprintf (_("\
+%s:%d: Invalid multibyte sequence.\n\
+Please specify the source encoding through --from-code or through a comment\n\
+as specified in http://www.python.org/peps/pep-0263.html.\n"),
+ real_file_name, line_number));
+ exit (EXIT_FAILURE);
+ }
+ return uc;
+ }
+ }
+#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. */
+ abort ();
+#endif
+ }
+ else
+ {
+ /* Read an UTF-8 encoded character. */
+ unsigned char buf[6];
+ unsigned int count;
+ int c;
+ ucs4_t uc;
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[0] = c;
+ count = 1;
+
+ if (buf[0] >= 0xc0)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[1] = c;
+ count = 2;
+ }
+
+ if (buf[0] >= 0xe0
+ && ((buf[1] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[2] = c;
+ count = 3;
+ }
+
+ if (buf[0] >= 0xf0
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[3] = c;
+ count = 4;
+ }
+
+ if (buf[0] >= 0xf8
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[4] = c;
+ count = 5;
+ }
+
+ if (buf[0] >= 0xfc
+ && ((buf[1] ^ 0x80) < 0x40)
+ && ((buf[2] ^ 0x80) < 0x40)
+ && ((buf[3] ^ 0x80) < 0x40)
+ && ((buf[4] ^ 0x80) < 0x40))
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ return UEOF;
+ buf[5] = c;
+ count = 6;
+ }
+
+ u8_mbtouc (&uc, buf, count);
+ return uc;
+ }
+}
+
+/* Supports max (9, UNINAME_MAX + 3) pushback characters. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != UEOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* A string buffer type that allows appending Unicode characters.
+ Returns the entire string in UTF-8 encoding. */
+
+struct unicode_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;
+};
+
+/* Initialize a 'struct unicode_string_buffer' to empty. */
+static inline void
+init_unicode_string_buffer (struct unicode_string_buffer *bp)
+{
+ bp->utf8_buffer = NULL;
+ bp->utf8_buflen = 0;
+ bp->utf8_allocated = 0;
+}
+
+/* Auxiliary function: Ensure count more bytes are available in bp->utf8. */
+static inline void
+unicode_string_buffer_append_unicode_grow (struct unicode_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
+unicode_string_buffer_append_unicode (struct unicode_string_buffer *bp,
+ unsigned int 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 ();
+
+ unicode_string_buffer_append_unicode_grow (bp, count);
+ memcpy (bp->utf8_buffer + bp->utf8_buflen, utf8buf, count);
+ bp->utf8_buflen += count;
+}
+
+/* Return the string buffer's contents. */
+static char *
+unicode_string_buffer_result (struct unicode_string_buffer *bp)
+{
+ /* NUL-terminate it. */
+ unicode_string_buffer_append_unicode_grow (bp, 1);
+ bp->utf8_buffer[bp->utf8_buflen] = '\0';
+ /* Return it. */
+ return bp->utf8_buffer;
+}
+
+/* Free the memory pointed to by a 'struct unicode_string_buffer'. */
+static inline void
+free_unicode_string_buffer (struct unicode_string_buffer *bp)
+{
+ free (bp->utf8_buffer);
+}
+
+
+/* ======================== Accumulating comments. ======================== */
+
+
+/* Accumulating a single comment line. */
+
+static struct unicode_string_buffer comment_buffer;
+
+static inline void
+comment_start ()
+{
+ lexical_context = lc_comment;
+ comment_buffer.utf8_buflen = 0;
+}
+
+static inline bool
+comment_at_start ()
+{
+ return (comment_buffer.utf8_buflen == 0);
+}
+
+static inline void
+comment_add (int c)
+{
+ unicode_string_buffer_append_unicode (&comment_buffer, c);
+}
+
+static inline const char *
+comment_line_end ()
+{
+ char *buffer = unicode_string_buffer_result (&comment_buffer);
+ size_t buflen = strlen (buffer);
+
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ lexical_context = lc_outside;
+ return buffer;
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ======================== Recognizing comments. ======================== */
+
+
+/* Recognizing the "coding" comment.
+ As specified in PEP 0263, it takes the form
+ "coding" [":"|"="] {alphanumeric or "-" or "_" or "*"}*
+ or
+ "set" "fileencoding" "=" {alphanumeric or "-" or "_" or "*"}*
+ and is located in a comment in a line that
+ - is either the first or second line,
+ - is not a continuation line,
+ - in the first form, contains no other tokens except this comment. */
+
+/* Canonicalized encoding name for the current input file. */
+static const char *xgettext_current_file_source_encoding;
+
+#if HAVE_ICONV
+/* Converter from xgettext_current_file_source_encoding to UTF-8 (except from
+ ASCII or UTF-8, when this conversion is a no-op). */
+static iconv_t xgettext_current_file_source_iconv;
+#endif
+
+static inline void
+set_current_file_source_encoding (const char *canon_encoding)
+{
+ xgettext_current_file_source_encoding = canon_encoding;
+
+ if (xgettext_current_file_source_encoding != po_charset_ascii
+ && xgettext_current_file_source_encoding != po_charset_utf8)
+ {
+#if HAVE_ICONV
+ iconv_t cd;
+
+ /* Avoid glibc-2.1 bug with EUC-KR. */
+# if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
+ && !defined _LIBICONV_VERSION
+ if (strcmp (xgettext_current_file_source_encoding, "EUC-KR") == 0)
+ cd = (iconv_t)(-1);
+ else
+# endif
+ cd = iconv_open (po_charset_utf8, xgettext_current_file_source_encoding);
+ if (cd == (iconv_t)(-1))
+ error_at_line (EXIT_FAILURE, 0, logical_file_name, line_number - 1, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
+and iconv() does not support this conversion."),
+ xgettext_current_file_source_encoding, po_charset_utf8,
+ basename (program_name));
+ xgettext_current_file_source_iconv = cd;
+#else
+ error_at_line (EXIT_FAILURE, 0, logical_file_name, line_number - 1, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \
+This version was built without iconv()."),
+ xgettext_global_source_encoding, po_charset_utf8,
+ basename (program_name));
+#endif
+ }
+
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_source_iconv = xgettext_current_file_source_iconv;
+#endif
+}
+
+static inline void
+try_to_extract_coding (const char *comment)
+{
+ const char *p = c_strstr (comment, "coding");
+
+ if (p != NULL)
+ {
+ p += 6;
+ if (*p == ':' || *p == '=')
+ {
+ p++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ {
+ const char *encoding_start = p;
+
+ while (c_isalnum (*p) || *p == '-' || *p == '_' || *p == '.')
+ p++;
+ {
+ const char *encoding_end = p;
+
+ if (encoding_end > encoding_start)
+ {
+ /* Extract the encoding string. */
+ size_t encoding_len = encoding_end - encoding_start;
+ char *encoding = XNMALLOC (encoding_len + 1, char);
+
+ memcpy (encoding, encoding_start, encoding_len);
+ encoding[encoding_len] = '\0';
+
+ {
+ /* Canonicalize it. */
+ const char *canon_encoding = po_charset_canonicalize (encoding);
+ if (canon_encoding == NULL)
+ {
+ error_at_line (0, 0,
+ logical_file_name, line_number - 1, _("\
+Unknown encoding \"%s\". Proceeding with ASCII instead."),
+ encoding);
+ canon_encoding = po_charset_ascii;
+ }
+
+ /* Activate it. */
+ set_current_file_source_encoding (canon_encoding);
+ }
+
+ free (encoding);
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Tracking whether the current line is a continuation line or contains a
+ non-blank character. */
+static bool continuation_or_nonblank_line = false;
+
+
+/* Phase 3: Outside strings, replace backslash-newline with nothing and a
+ comment with nothing. */
+
+static int
+phase3_getc ()
+{
+ int c;
+
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == '\\')
+ {
+ c = phase2_getc ();
+ if (c != '\n')
+ {
+ phase2_ungetc (c);
+ /* This shouldn't happen usually, because "A backslash is
+ illegal elsewhere on a line outside a string literal." */
+ return '\\';
+ }
+ /* Eat backslash-newline. */
+ continuation_or_nonblank_line = true;
+ }
+ else if (c == '#')
+ {
+ /* Eat a comment. */
+ const char *comment;
+
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == UEOF || c == '\n')
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(comment_at_start () && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment = comment_line_end ();
+ if (line_number - 1 <= 2 && !continuation_or_nonblank_line)
+ try_to_extract_coding (comment);
+ continuation_or_nonblank_line = false;
+ return c;
+ }
+ else
+ {
+ if (c == '\n')
+ continuation_or_nonblank_line = false;
+ else if (!(c == ' ' || c == '\t' || c == '\f'))
+ continuation_or_nonblank_line = true;
+ return c;
+ }
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase3_ungetc (int c)
+{
+ phase2_ungetc (c);
+}
+
+
+/* ========================= Accumulating strings. ======================== */
+
+/* Return value of phase7_getuc when EOF is reached. */
+#define P7_EOF (-1)
+#define P7_STRING_END (-2)
+
+/* Convert an UTF-16 or UTF-32 code point to a return value that can be
+ distinguished from a single-byte return value. */
+#define UNICODE(code) (0x100 + (code))
+
+/* Test a return value of phase7_getuc whether it designates an UTF-16 or
+ UTF-32 code point. */
+#define IS_UNICODE(p7_result) ((p7_result) >= 0x100)
+
+/* Extract the UTF-16 or UTF-32 code of a return value that satisfies
+ IS_UNICODE. */
+#define UNICODE_VALUE(p7_result) ((p7_result) - 0x100)
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_lbracket, /* [ */
+ token_type_rbracket, /* ] */
+ token_type_string, /* "abc", 'abc', """abc""", '''abc''' */
+ token_type_symbol, /* symbol, number */
+ token_type_plus, /* + */
+ token_type_other /* misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string */
+ int line_number;
+};
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string)
+ drop_reference (tp->comment);
+}
+
+
+/* There are two different input syntaxes for strings, "abc" and r"abc",
+ and two different input syntaxes for Unicode strings, u"abc" and ur"abc".
+ Which escape sequences are understood, i.e. what is interpreted specially
+ after backslash?
+ "abc" \<nl> \\ \' \" \a\b\f\n\r\t\v \ooo \xnn
+ r"abc"
+ u"abc" \<nl> \\ \' \" \a\b\f\n\r\t\v \ooo \xnn \unnnn \Unnnnnnnn \N{...}
+ ur"abc" \unnnn
+ The \unnnn values are UTF-16 values; a single \Unnnnnnnn can expand to two
+ \unnnn items. The \ooo and \xnn values are in the current source encoding
+ for byte strings, and Unicode code points for Unicode strings.
+ */
+
+static int
+phase7_getuc (int quote_char,
+ bool triple, bool interpret_ansic, bool interpret_unicode,
+ unsigned int *backslash_counter)
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 2, because phase 3 elides comments. */
+ c = phase2_getc ();
+
+ if (c == UEOF)
+ return P7_EOF;
+
+ if (c == quote_char && (interpret_ansic || (*backslash_counter & 1) == 0))
+ {
+ if (triple)
+ {
+ int c1 = phase2_getc ();
+ if (c1 == quote_char)
+ {
+ int c2 = phase2_getc ();
+ if (c2 == quote_char)
+ return P7_STRING_END;
+ phase2_ungetc (c2);
+ }
+ phase2_ungetc (c1);
+ return UNICODE (c);
+ }
+ else
+ return P7_STRING_END;
+ }
+
+ if (c == '\n')
+ {
+ if (triple)
+ {
+ *backslash_counter = 0;
+ return UNICODE ('\n');
+ }
+ /* In r"..." and ur"..." strings, newline is only allowed
+ immediately after an odd number of backslashes (although the
+ backslashes are not interpreted!). */
+ if (!(interpret_ansic || (*backslash_counter & 1) == 0))
+ {
+ *backslash_counter = 0;
+ return UNICODE ('\n');
+ }
+ phase2_ungetc (c);
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated string"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ return P7_STRING_END;
+ }
+
+ if (c != '\\')
+ {
+ *backslash_counter = 0;
+ return UNICODE (c);
+ }
+
+ /* Backslash handling. */
+
+ if (!interpret_ansic && !interpret_unicode)
+ {
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+
+ /* Dispatch according to the character following the backslash. */
+ c = phase2_getc ();
+ if (c == UEOF)
+ {
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+
+ if (interpret_ansic)
+ switch (c)
+ {
+ case '\n':
+ continue;
+ case '\\':
+ ++*backslash_counter;
+ return UNICODE (c);
+ case '\'': case '"':
+ *backslash_counter = 0;
+ return UNICODE (c);
+ case 'a':
+ *backslash_counter = 0;
+ return UNICODE ('\a');
+ case 'b':
+ *backslash_counter = 0;
+ return UNICODE ('\b');
+ case 'f':
+ *backslash_counter = 0;
+ return UNICODE ('\f');
+ case 'n':
+ *backslash_counter = 0;
+ return UNICODE ('\n');
+ case 'r':
+ *backslash_counter = 0;
+ return UNICODE ('\r');
+ case 't':
+ *backslash_counter = 0;
+ return UNICODE ('\t');
+ case 'v':
+ *backslash_counter = 0;
+ return UNICODE ('\v');
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = phase2_getc ();
+ if (c != UEOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = phase2_getc ();
+ if (c != UEOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ phase2_ungetc (c);
+ }
+ }
+ else
+ phase2_ungetc (c);
+ }
+ *backslash_counter = 0;
+ if (interpret_unicode)
+ return UNICODE (n);
+ else
+ return (unsigned char) n;
+ }
+ case 'x':
+ {
+ int c1 = phase2_getc ();
+ int n1;
+
+ if (c1 >= '0' && c1 <= '9')
+ n1 = c1 - '0';
+ else if (c1 >= 'A' && c1 <= 'F')
+ n1 = c1 - 'A' + 10;
+ else if (c1 >= 'a' && c1 <= 'f')
+ n1 = c1 - 'a' + 10;
+ else
+ n1 = -1;
+
+ if (n1 >= 0)
+ {
+ int c2 = phase2_getc ();
+ int n2;
+
+ if (c2 >= '0' && c2 <= '9')
+ n2 = c2 - '0';
+ else if (c2 >= 'A' && c2 <= 'F')
+ n2 = c2 - 'A' + 10;
+ else if (c2 >= 'a' && c2 <= 'f')
+ n2 = c2 - 'a' + 10;
+ else
+ n2 = -1;
+
+ if (n2 >= 0)
+ {
+ int n = (n1 << 4) + n2;
+ *backslash_counter = 0;
+ if (interpret_unicode)
+ return UNICODE (n);
+ else
+ return (unsigned char) n;
+ }
+
+ phase2_ungetc (c2);
+ }
+ phase2_ungetc (c1);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+ }
+
+ if (interpret_unicode)
+ {
+ if (c == 'u')
+ {
+ unsigned char buf[4];
+ unsigned int n = 0;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ int c1 = phase2_getc ();
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ {
+ phase2_ungetc (c1);
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+
+ buf[i] = c1;
+ }
+ *backslash_counter = 0;
+ return UNICODE (n);
+ }
+
+ if (interpret_ansic)
+ {
+ if (c == 'U')
+ {
+ unsigned char buf[8];
+ unsigned int n = 0;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ int c1 = phase2_getc ();
+
+ if (c1 >= '0' && c1 <= '9')
+ n = (n << 4) + (c1 - '0');
+ else if (c1 >= 'A' && c1 <= 'F')
+ n = (n << 4) + (c1 - 'A' + 10);
+ else if (c1 >= 'a' && c1 <= 'f')
+ n = (n << 4) + (c1 - 'a' + 10);
+ else
+ {
+ phase2_ungetc (c1);
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+
+ buf[i] = c1;
+ }
+ if (n < 0x110000)
+ {
+ *backslash_counter = 0;
+ return UNICODE (n);
+ }
+
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: invalid Unicode character"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+
+ if (c == 'N')
+ {
+ int c1 = phase2_getc ();
+ if (c1 == '{')
+ {
+ unsigned char buf[UNINAME_MAX + 1];
+ int i;
+ unsigned int n;
+
+ for (i = 0; i < UNINAME_MAX; i++)
+ {
+ int c2 = phase2_getc ();
+ if (!(c2 >= ' ' && c2 <= '~'))
+ {
+ phase2_ungetc (c2);
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ phase2_ungetc (c1);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+ if (c2 == '}')
+ break;
+ buf[i] = c2;
+ }
+ buf[i] = '\0';
+
+ n = unicode_name_character ((char *) buf);
+ if (n != UNINAME_INVALID)
+ {
+ *backslash_counter = 0;
+ return UNICODE (n);
+ }
+
+ phase2_ungetc ('}');
+ while (--i >= 0)
+ phase2_ungetc (buf[i]);
+ }
+ phase2_ungetc (c1);
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+ }
+ }
+
+ phase2_ungetc (c);
+ ++*backslash_counter;
+ return UNICODE ('\\');
+ }
+}
+
+
+/* Combine characters into tokens. Discard whitespace except newlines at
+ the end of logical lines. */
+
+/* Number of pending open parentheses/braces/brackets. */
+static int open_pbb;
+
+static token_ty phase5_pushback[2];
+static int phase5_pushback_length;
+
+static void
+phase5_get (token_ty *tp)
+{
+ int c;
+
+ if (phase5_pushback_length)
+ {
+ *tp = phase5_pushback[--phase5_pushback_length];
+ return;
+ }
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase3_getc ();
+
+ switch (c)
+ {
+ case UEOF:
+ tp->type = token_type_eof;
+ return;
+
+ case ' ':
+ case '\t':
+ case '\f':
+ /* Ignore whitespace and comments. */
+ continue;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* Ignore newline if and only if it is used for implicit line
+ joining. */
+ if (open_pbb > 0)
+ continue;
+ tp->type = token_type_other;
+ return;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case '.':
+ {
+ int c1 = phase3_getc ();
+ phase3_ungetc (c1);
+ if (!(c1 >= '0' && c1 <= '9'))
+ {
+
+ tp->type = token_type_other;
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q':
+ case 'S': case 'T': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q':
+ case 's': case 't': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ symbol:
+ /* Symbol, or part of a number. */
+ {
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase3_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ default:
+ phase3_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ return;
+ }
+
+ /* Strings. */
+ {
+ struct mixed_string_buffer *bp;
+ int quote_char;
+ bool interpret_ansic;
+ bool interpret_unicode;
+ bool triple;
+ unsigned int backslash_counter;
+
+ case 'R': case 'r':
+ {
+ int c1 = phase2_getc ();
+ if (c1 == '"' || c1 == '\'')
+ {
+ quote_char = c1;
+ interpret_ansic = false;
+ interpret_unicode = false;
+ goto string;
+ }
+ phase2_ungetc (c1);
+ goto symbol;
+ }
+
+ case 'U': case 'u':
+ {
+ int c1 = phase2_getc ();
+ if (c1 == '"' || c1 == '\'')
+ {
+ quote_char = c1;
+ interpret_ansic = true;
+ interpret_unicode = true;
+ goto string;
+ }
+ if (c1 == 'R' || c1 == 'r')
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '"' || c2 == '\'')
+ {
+ quote_char = c2;
+ interpret_ansic = false;
+ interpret_unicode = true;
+ goto string;
+ }
+ phase2_ungetc (c2);
+ }
+ phase2_ungetc (c1);
+ goto symbol;
+ }
+
+ case '"': case '\'':
+ quote_char = c;
+ interpret_ansic = true;
+ interpret_unicode = false;
+ string:
+ triple = false;
+ lexical_context = lc_string;
+ {
+ int c1 = phase2_getc ();
+ if (c1 == quote_char)
+ {
+ int c2 = phase2_getc ();
+ if (c2 == quote_char)
+ triple = true;
+ else
+ {
+ phase2_ungetc (c2);
+ phase2_ungetc (c1);
+ }
+ }
+ else
+ phase2_ungetc (c1);
+ }
+ backslash_counter = 0;
+ /* Start accumulating the string. */
+ bp = mixed_string_buffer_alloc (lexical_context,
+ logical_file_name,
+ line_number);
+ for (;;)
+ {
+ int uc = phase7_getuc (quote_char, triple, interpret_ansic,
+ interpret_unicode, &backslash_counter);
+
+ /* Keep line_number in sync. */
+ bp->line_number = line_number;
+
+ if (uc == P7_EOF || uc == P7_STRING_END)
+ break;
+
+ if (IS_UNICODE (uc))
+ {
+ assert (UNICODE_VALUE (uc) >= 0
+ && UNICODE_VALUE (uc) < 0x110000);
+ mixed_string_buffer_append_unicode (bp,
+ UNICODE_VALUE (uc));
+ }
+ else
+ mixed_string_buffer_append_char (bp, uc);
+ }
+ tp->string = mixed_string_buffer_done (bp);
+ tp->comment = add_reference (savable_comment);
+ lexical_context = lc_outside;
+ tp->type = token_type_string;
+ return;
+ }
+
+ case '(':
+ open_pbb++;
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ if (open_pbb > 0)
+ open_pbb--;
+ tp->type = token_type_rparen;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ case '[': case '{':
+ open_pbb++;
+ tp->type = (c == '[' ? token_type_lbracket : token_type_other);
+ return;
+
+ case ']': case '}':
+ if (open_pbb > 0)
+ open_pbb--;
+ tp->type = (c == ']' ? token_type_rbracket : token_type_other);
+ return;
+
+ case '+':
+ tp->type = token_type_plus;
+ return;
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports only one pushback token. */
+static void
+phase5_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = *tp;
+ }
+}
+
+
+/* Combine adjacent strings to form a single string. Note that the end
+ of a logical line appears as a token of its own, therefore strings that
+ belong to different logical lines will not be concatenated. */
+
+static void
+x_python_lex (token_ty *tp)
+{
+ phase5_get (tp);
+ if (tp->type == token_type_string)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2, *tp2 = NULL;
+ token_ty token3;
+
+ phase5_get (&token2);
+ switch (token2.type)
+ {
+ case token_type_plus:
+ {
+ phase5_get (&token3);
+ if (token3.type == token_type_string)
+ {
+ free_token (&token2);
+ tp2 = &token3;
+ }
+ else
+ phase5_unget (&token3);
+ }
+ break;
+ case token_type_string:
+ tp2 = &token2;
+ break;
+ default:
+ break;
+ }
+
+ if (tp2)
+ {
+ char *addend = tp2->string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ free_token (tp2);
+ continue;
+ }
+ phase5_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid C or C++ program, and leave the complaints about
+ the grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+
+/* Extract messages until the next balanced closing parenthesis or bracket.
+ Extracted messages are added to MLP.
+ DELIM can be either token_type_rparen or token_type_rbracket, or
+ token_type_eof to accept both.
+ Return true upon eof, false upon closing parenthesis or bracket. */
+static bool
+extract_balanced (message_list_ty *mlp,
+ token_type_ty delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_python_lex (&token);
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_balanced (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ if (delim == token_type_rparen || delim == token_type_eof)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_lbracket:
+ if (extract_balanced (mlp, token_type_rbracket,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rbracket:
+ if (delim == token_type_rbracket || delim == token_type_eof)
+ {
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return false;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ xgettext_current_source_encoding = po_charset_utf8;
+ if (extract_all)
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ else
+ arglist_parser_remember (argparser, arg, token.string,
+ inner_context,
+ pos.file_name, pos.line_number,
+ token.comment);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ xgettext_current_source_encoding = po_charset_utf8;
+ arglist_parser_done (argparser, arg);
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+ return true;
+
+ case token_type_plus:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_python (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ lexical_context = lc_outside;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ xgettext_current_file_source_encoding = xgettext_global_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_file_source_iconv = xgettext_global_source_iconv;
+#endif
+
+ xgettext_current_source_encoding = xgettext_current_file_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_source_iconv = xgettext_current_file_source_iconv;
+#endif
+
+ continuation_or_nonblank_line = false;
+
+ open_pbb = 0;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_balanced returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_balanced (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-python.h b/gettext-tools/src/x-python.h
new file mode 100644
index 0000000..f7861fb
--- /dev/null
+++ b/gettext-tools/src/x-python.h
@@ -0,0 +1,51 @@
+/* xgettext Python backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_PYTHON \
+ { "py", "Python" }, \
+
+#define SCANNERS_PYTHON \
+ { "Python", extract_python, \
+ &flag_table_python, &formatstring_python, &formatstring_python_brace, NULL }, \
+
+/* Scan a Python file and add its translatable strings to mdlp. */
+extern void extract_python (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_python_keyword (const char *keyword);
+extern void x_python_extract_all (void);
+
+extern void init_flag_table_python (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-rst.c b/gettext-tools/src/x-rst.c
new file mode 100644
index 0000000..8b5a26a
--- /dev/null
+++ b/gettext-tools/src/x-rst.c
@@ -0,0 +1,236 @@
+/* xgettext RST backend.
+ Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-rst.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "c-ctype.h"
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+/* RST stands for Resource String Table.
+
+ An RST file consists of several string definitions. A string definition
+ starts at the beginning of a line and looks like this:
+ ModuleName.ConstName=StringExpression
+ A StringExpression consists of string pieces of the form 'xyz',
+ single characters of the form #nnn (decimal integer), and +
+ at the end of the line to designate continuation on the next line.
+ String definitions can be separated by blank lines or comment lines
+ beginning with '#'.
+
+ This backend attempts to be functionally equivalent to the 'rstconv'
+ program, part of the Free Pascal run time library, written by
+ Sebastian Guenther. Except that the locations are output as
+ "ModuleName.ConstName", not "ModuleName:ConstName".
+ */
+
+void
+extract_rst (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ static char *buffer;
+ static int bufmax;
+ message_list_ty *mlp = mdlp->item[0]->messages;
+ int line_number;
+
+ line_number = 1;
+ for (;;)
+ {
+ int c;
+ int bufpos;
+ char *location;
+ char *msgid;
+ lex_pos_ty pos;
+
+ c = getc (f);
+ if (c == EOF)
+ break;
+
+ /* Ignore blank line. */
+ if (c == '\n')
+ {
+ line_number++;
+ continue;
+ }
+
+ /* Ignore comment line. */
+ if (c == '#')
+ {
+ do
+ c = getc (f);
+ while (c != EOF && c != '\n');
+ if (c == EOF)
+ break;
+ line_number++;
+ continue;
+ }
+
+ /* Read ModuleName.ConstName. */
+ bufpos = 0;
+ for (;;)
+ {
+ if (c == EOF || c == '\n')
+ {
+ error_with_progname = false;
+ error (EXIT_FAILURE, 0, _("%s:%d: invalid string definition"),
+ logical_filename, line_number);
+ error_with_progname = true;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ if (c == '=')
+ break;
+ buffer[bufpos++] = c;
+ c = getc (f);
+ if (c == EOF && ferror (f))
+ goto bomb;
+ }
+ buffer[bufpos] = '\0';
+ location = xstrdup (buffer);
+
+ /* Read StringExpression. */
+ bufpos = 0;
+ for (;;)
+ {
+ c = getc (f);
+ if (c == EOF)
+ break;
+ else if (c == '\n')
+ {
+ line_number++;
+ break;
+ }
+ else if (c == '\'')
+ {
+ for (;;)
+ {
+ c = getc (f);
+ /* Embedded single quotes like 'abc''def' don't occur.
+ See fpc-1.0.4/compiler/cresstr.pas. */
+ if (c == EOF || c == '\n' || c == '\'')
+ break;
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (c == EOF)
+ break;
+ else if (c == '\n')
+ {
+ line_number++;
+ break;
+ }
+ }
+ else if (c == '#')
+ {
+ int n;
+ c = getc (f);
+ if (c == EOF && ferror (f))
+ goto bomb;
+ if (c == EOF || !c_isdigit (c))
+ {
+ error_with_progname = false;
+ error (EXIT_FAILURE, 0, _("%s:%d: missing number after #"),
+ logical_filename, line_number);
+ error_with_progname = true;
+ }
+ n = (c - '0');
+ for (;;)
+ {
+ c = getc (f);
+ if (c == EOF || !c_isdigit (c))
+ break;
+ n = n * 10 + (c - '0');
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = (unsigned char) n;
+ if (c == EOF)
+ break;
+ ungetc (c, f);
+ }
+ else if (c == '+')
+ {
+ c = getc (f);
+ if (c == EOF)
+ break;
+ if (c == '\n')
+ line_number++;
+ else
+ ungetc (c, f);
+ }
+ else
+ {
+ error_with_progname = false;
+ error (EXIT_FAILURE, 0, _("%s:%d: invalid string expression"),
+ logical_filename, line_number);
+ error_with_progname = true;
+ }
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ msgid = xstrdup (buffer);
+
+ pos.file_name = location;
+ pos.line_number = (size_t)(-1);
+
+ remember_a_message (mlp, NULL, msgid, null_context, &pos, NULL, NULL);
+
+ /* Here c is the last read character: EOF or '\n'. */
+ if (c == EOF)
+ break;
+ }
+
+ if (ferror (f))
+ {
+ bomb:
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_filename);
+ }
+}
diff --git a/gettext-tools/src/x-rst.h b/gettext-tools/src/x-rst.h
new file mode 100644
index 0000000..6507d0d
--- /dev/null
+++ b/gettext-tools/src/x-rst.h
@@ -0,0 +1,46 @@
+/* xgettext RST backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_RST \
+ { "rst", "RST" }, \
+
+#define SCANNERS_RST \
+ { "RST", extract_rst, \
+ NULL, &formatstring_pascal, NULL, NULL }, \
+
+/* Scan an RST file and add its translatable strings to mdlp. */
+extern void extract_rst (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-scheme.c b/gettext-tools/src/x-scheme.c
new file mode 100644
index 0000000..e8cfe84
--- /dev/null
+++ b/gettext-tools/src/x-scheme.c
@@ -0,0 +1,1339 @@
+/* xgettext Scheme backend.
+ Copyright (C) 2004-2009, 2011 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <bruno@clisp.org>, 2004-2005.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-scheme.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+
+/* The Scheme syntax is described in R5RS. It is implemented in
+ guile-2.0.0/libguile/read.c.
+ Since we are interested only in strings and in forms similar to
+ (gettext msgid ...)
+ or (ngettext msgid msgid_plural ...)
+ we make the following simplifications:
+
+ - Assume the keywords and strings are in an ASCII compatible encoding.
+ This means we can read the input file one byte at a time, instead of
+ one character at a time. No need to worry about multibyte characters:
+ If they occur as part of identifiers, they most probably act as
+ constituent characters, and the byte based approach will do the same.
+
+ - Assume the read-hash-procedures is in the default state.
+ Non-standard reader extensions are mostly used to read data, not programs.
+
+ The remaining syntax rules are:
+
+ - The syntax code assigned to each character, and how tokens are built
+ up from characters (single escape, multiple escape etc.).
+
+ - Comment syntax: ';' and '#! ... !#' and '#| ... |#' (may be nested).
+
+ - String syntax: "..." with single escapes.
+
+ - Read macros and dispatch macro character '#'. Needed to be able to
+ tell which is the n-th argument of a function call.
+
+ */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_scheme_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_scheme_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Lisp symbol.
+ Extract the symbol name part. */
+ colon = strchr (name, ':');
+ if (colon != NULL && colon < end)
+ {
+ name = colon + 1;
+ if (name < end && *name == ':')
+ name++;
+ colon = strchr (name, ':');
+ if (colon != NULL && colon < end)
+ return;
+ }
+
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_scheme_keyword ("gettext"); /* libguile/i18n.c */
+ x_scheme_keyword ("ngettext:1,2"); /* libguile/i18n.c */
+ x_scheme_keyword ("gettext-noop");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_scheme ()
+{
+ xgettext_record_flag ("gettext:1:pass-scheme-format");
+ xgettext_record_flag ("ngettext:1:pass-scheme-format");
+ xgettext_record_flag ("ngettext:2:pass-scheme-format");
+ xgettext_record_flag ("gettext-noop:1:pass-scheme-format");
+ xgettext_record_flag ("format:2:scheme-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* A token consists of a sequence of characters. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ char *chars; /* the token's constituents */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (char *) xrealloc (tp->chars, tp->allocated * sizeof (char));
+ }
+}
+
+/* Read the next token. 'first' is the first character, which has already
+ been read. */
+static void
+read_token (struct token *tp, int first)
+{
+ init_token (tp);
+
+ grow_token (tp);
+ tp->chars[tp->charcount++] = first;
+
+ for (;;)
+ {
+ int c = do_getc ();
+
+ if (c == EOF)
+ break;
+ if (c == ' ' || c == '\r' || c == '\f' || c == '\t' || c == '\n'
+ || c == '"' || c == '(' || c == ')' || c == ';')
+ {
+ do_ungetc (c);
+ break;
+ }
+ grow_token (tp);
+ tp->chars[tp->charcount++] = c;
+ }
+}
+
+/* Tests if a token represents an integer.
+ Taken from guile-1.6.4/libguile/numbers.c:scm_istr2int(). */
+static inline bool
+is_integer_syntax (const char *str, int len, int radix)
+{
+ const char *p = str;
+ const char *p_end = str + len;
+
+ /* The accepted syntax is
+ ['+'|'-'] DIGIT+
+ where DIGIT is a hexadecimal digit whose value is below radix. */
+
+ if (p == p_end)
+ return false;
+ if (*p == '+' || *p == '-')
+ {
+ p++;
+ if (p == p_end)
+ return false;
+ }
+ do
+ {
+ int c = *p++;
+
+ if (c >= '0' && c <= '9')
+ c = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ c = c - 'a' + 10;
+ else
+ return false;
+ if (c >= radix)
+ return false;
+ }
+ while (p < p_end);
+ return true;
+}
+
+/* Tests if a token represents a rational, floating-point or complex number.
+ If unconstrained is false, only real numbers are accepted; otherwise,
+ complex numbers are accepted as well.
+ Taken from guile-1.6.4/libguile/numbers.c:scm_istr2flo(). */
+static inline bool
+is_other_number_syntax (const char *str, int len, int radix, bool unconstrained)
+{
+ const char *p = str;
+ const char *p_end = str + len;
+ bool seen_sign;
+ bool seen_digits;
+
+ /* The accepted syntaxes are:
+ for a floating-point number:
+ ['+'|'-'] DIGIT+ [EXPONENT]
+ ['+'|'-'] DIGIT* '.' DIGIT+ [EXPONENT]
+ where EXPONENT ::= ['d'|'e'|'f'|'l'|'s'] DIGIT+
+ (Dot and exponent are allowed only if radix is 10.)
+ for a rational number:
+ ['+'|'-'] DIGIT+ '/' DIGIT+
+ for a complex number:
+ REAL-NUMBER {'+'|'-'} REAL-NUMBER-WITHOUT-SIGN 'i'
+ REAL-NUMBER {'+'|'-'} 'i'
+ {'+'|'-'} REAL-NUMBER-WITHOUT-SIGN 'i'
+ {'+'|'-'} 'i'
+ REAL-NUMBER '@' REAL-NUMBER
+ */
+ if (p == p_end)
+ return false;
+ /* Parse leading sign. */
+ seen_sign = false;
+ if (*p == '+' || *p == '-')
+ {
+ p++;
+ if (p == p_end)
+ return false;
+ seen_sign = true;
+ /* Recognize complex number syntax: {'+'|'-'} 'i' */
+ if (unconstrained && (*p == 'I' || *p == 'i') && p + 1 == p_end)
+ return true;
+ }
+ /* Parse digits before dot or exponent or slash. */
+ seen_digits = false;
+ do
+ {
+ int c = *p;
+
+ if (c >= '0' && c <= '9')
+ c = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ {
+ if (c >= 'D' && radix == 10) /* exponent? */
+ break;
+ c = c - 'A' + 10;
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ if (c >= 'd' && radix == 10) /* exponent? */
+ break;
+ c = c - 'a' + 10;
+ }
+ else
+ break;
+ if (c >= radix)
+ return false;
+ seen_digits = true;
+ p++;
+ }
+ while (p < p_end);
+ /* If p == p_end, we know that seen_digits = true, and the number is an
+ integer without exponent. */
+ if (p < p_end)
+ {
+ /* If we have no digits so far, we need a decimal point later. */
+ if (!seen_digits && !(*p == '.' && radix == 10))
+ return false;
+ /* Trailing '#' signs are equivalent to zeroes. */
+ while (p < p_end && *p == '#')
+ p++;
+ if (p < p_end)
+ {
+ if (*p == '/')
+ {
+ /* Parse digits after the slash. */
+ bool all_zeroes = true;
+ p++;
+ for (; p < p_end; p++)
+ {
+ int c = *p;
+
+ if (c >= '0' && c <= '9')
+ c = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ c = c - 'a' + 10;
+ else
+ break;
+ if (c >= radix)
+ return false;
+ if (c != 0)
+ all_zeroes = false;
+ }
+ /* A zero denominator is not allowed. */
+ if (all_zeroes)
+ return false;
+ /* Trailing '#' signs are equivalent to zeroes. */
+ while (p < p_end && *p == '#')
+ p++;
+ }
+ else
+ {
+ if (*p == '.')
+ {
+ /* Decimal point notation. */
+ if (radix != 10)
+ return false;
+ /* Parse digits after the decimal point. */
+ p++;
+ for (; p < p_end; p++)
+ {
+ int c = *p;
+
+ if (c >= '0' && c <= '9')
+ seen_digits = true;
+ else
+ break;
+ }
+ /* Digits are required before or after the decimal point. */
+ if (!seen_digits)
+ return false;
+ /* Trailing '#' signs are equivalent to zeroes. */
+ while (p < p_end && *p == '#')
+ p++;
+ }
+ if (p < p_end)
+ {
+ /* Parse exponent. */
+ switch (*p)
+ {
+ case 'D': case 'd':
+ case 'E': case 'e':
+ case 'F': case 'f':
+ case 'L': case 'l':
+ case 'S': case 's':
+ if (radix != 10)
+ return false;
+ p++;
+ if (p == p_end)
+ return false;
+ if (*p == '+' || *p == '-')
+ {
+ p++;
+ if (p == p_end)
+ return false;
+ }
+ if (!(*p >= '0' && *p <= '9'))
+ return false;
+ for (;;)
+ {
+ p++;
+ if (p == p_end)
+ break;
+ if (!(*p >= '0' && *p <= '9'))
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (p == p_end)
+ return true;
+ /* Recognize complex number syntax. */
+ if (unconstrained)
+ {
+ /* Recognize the syntax {'+'|'-'} REAL-NUMBER-WITHOUT-SIGN 'i' */
+ if (seen_sign && (*p == 'I' || *p == 'i') && p + 1 == p_end)
+ return true;
+ /* Recognize the syntaxes
+ REAL-NUMBER {'+'|'-'} REAL-NUMBER-WITHOUT-SIGN 'i'
+ REAL-NUMBER {'+'|'-'} 'i'
+ */
+ if (*p == '+' || *p == '-')
+ return (p_end[-1] == 'I' || p_end[-1] == 'i')
+ && (p + 1 == p_end - 1
+ || is_other_number_syntax (p, p_end - 1 - p, radix, false));
+ /* Recognize the syntax REAL-NUMBER '@' REAL-NUMBER */
+ if (*p == '@')
+ {
+ p++;
+ return is_other_number_syntax (p, p_end - p, radix, false);
+ }
+ }
+ return false;
+}
+
+/* Tests if a token represents a number.
+ Taken from guile-1.6.4/libguile/numbers.c:scm_istring2number(). */
+static bool
+is_number (const struct token *tp)
+{
+ const char *str = tp->chars;
+ int len = tp->charcount;
+ enum { unknown, exact, inexact } exactness = unknown;
+ bool seen_radix_prefix = false;
+ bool seen_exactness_prefix = false;
+
+ if (len == 1)
+ if (*str == '+' || *str == '-')
+ return false;
+ while (len >= 2 && *str == '#')
+ {
+ switch (str[1])
+ {
+ case 'B': case 'b':
+ if (seen_radix_prefix)
+ return false;
+ seen_radix_prefix = true;
+ break;
+ case 'O': case 'o':
+ if (seen_radix_prefix)
+ return false;
+ seen_radix_prefix = true;
+ break;
+ case 'D': case 'd':
+ if (seen_radix_prefix)
+ return false;
+ seen_radix_prefix = true;
+ break;
+ case 'X': case 'x':
+ if (seen_radix_prefix)
+ return false;
+ seen_radix_prefix = true;
+ break;
+ case 'E': case 'e':
+ if (seen_exactness_prefix)
+ return false;
+ exactness = exact;
+ seen_exactness_prefix = true;
+ break;
+ case 'I': case 'i':
+ if (seen_exactness_prefix)
+ return false;
+ exactness = inexact;
+ seen_exactness_prefix = true;
+ break;
+ default:
+ return false;
+ }
+ str += 2;
+ len -= 2;
+ }
+ if (exactness != inexact)
+ {
+ /* Try to parse an integer. */
+ if (is_integer_syntax (str, len, 10))
+ return true;
+ /* FIXME: Other Scheme implementations support exact rational numbers
+ or exact complex numbers. */
+ }
+ if (exactness != exact)
+ {
+ /* Try to parse a rational, floating-point or complex number. */
+ if (is_other_number_syntax (str, len, 10, true))
+ return true;
+ }
+ return false;
+}
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ========================== Reading of objects. ========================= */
+
+
+/* We are only interested in symbols (e.g. gettext or ngettext) and strings.
+ Other objects need not to be represented precisely. */
+enum object_type
+{
+ t_symbol, /* symbol */
+ t_string, /* string */
+ t_other, /* other kind of real object */
+ t_dot, /* '.' pseudo object */
+ t_close, /* ')' pseudo object */
+ t_eof /* EOF marker */
+};
+
+struct object
+{
+ enum object_type type;
+ struct token *token; /* for t_symbol and t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct object'. */
+static inline void
+free_object (struct object *op)
+{
+ if (op->type == t_symbol || op->type == t_string)
+ {
+ free_token (op->token);
+ free (op->token);
+ }
+}
+
+/* Convert a t_symbol/t_string token to a char*. */
+static char *
+string_of_object (const struct object *op)
+{
+ char *str;
+ int n;
+
+ if (!(op->type == t_symbol || op->type == t_string))
+ abort ();
+ n = op->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, op->token->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+/* Read the next object. */
+static void
+read_object (struct object *op, flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ int c = do_getc ();
+ bool seen_underscore_prefix = false;
+
+ switch (c)
+ {
+ case EOF:
+ op->type = t_eof;
+ return;
+
+ case ' ': case '\r': case '\f': case '\t':
+ continue;
+
+ case '\n':
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between
+ both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ continue;
+
+ case ';':
+ {
+ bool all_semicolons = true;
+
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = do_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ if (c != ';')
+ all_semicolons = false;
+ if (!all_semicolons)
+ {
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ }
+ comment_line_end (0);
+ continue;
+ }
+
+ case '(':
+ {
+ int arg = 0; /* Current argument number. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;; arg++)
+ {
+ struct object inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_object (&inner, inner_context);
+
+ /* Recognize end of list. */
+ if (inner.type == t_close)
+ {
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return;
+ }
+
+ /* Dots are not allowed in every position.
+ But be tolerant. */
+
+ /* EOF inside list is illegal.
+ But be tolerant. */
+ if (inner.type == t_eof)
+ break;
+
+ if (arg == 0)
+ {
+ /* This is the function position. */
+ if (inner.type == t_symbol)
+ {
+ char *symbol_name = string_of_object (&inner);
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords,
+ symbol_name, strlen (symbol_name),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ symbol_name, strlen (symbol_name)));
+
+ free (symbol_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (argparser != NULL && inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_object (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+ }
+
+ free_object (&inner);
+ }
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case ')':
+ /* Tell the caller about the end of list.
+ Unmatched closing parenthesis is illegal.
+ But be tolerant. */
+ op->type = t_close;
+ last_non_comment_line = line_number;
+ return;
+
+ case ',':
+ {
+ int c = do_getc ();
+ /* The ,@ handling inside lists is wrong anyway, because
+ ,@form expands to an unknown number of elements. */
+ if (c != EOF && c != '@')
+ do_ungetc (c);
+ }
+ /*FALLTHROUGH*/
+ case '\'':
+ case '`':
+ {
+ struct object inner;
+
+ read_object (&inner, null_context);
+
+ /* Dots and EOF are not allowed here. But be tolerant. */
+
+ free_object (&inner);
+
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '#':
+ /* Dispatch macro handling. */
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+
+ switch (c)
+ {
+ case '(': /* Vector */
+ do_ungetc (c);
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case 'T': case 't': /* Boolean true */
+ case 'F': case 'f': /* Boolean false */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+
+ case 'B': case 'b':
+ case 'O': case 'o':
+ case 'D': case 'd':
+ case 'X': case 'x':
+ case 'E': case 'e':
+ case 'I': case 'i':
+ {
+ struct token token;
+ do_ungetc (c);
+ read_token (&token, '#');
+ if (is_number (&token))
+ {
+ /* A number. */
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ else
+ {
+ if (token.charcount == 2
+ && (token.chars[1] == 'e' || token.chars[1] == 'i'))
+ {
+ c = do_getc ();
+ if (c != EOF)
+ do_ungetc (c);
+ if (c == '(')
+ /* Homogenous vector syntax, see arrays.scm. */
+ case 'a': /* Vectors of char */
+ case 'c': /* Vectors of complex */
+ /*case 'e':*/ /* Vectors of long */
+ case 'h': /* Vectors of short */
+ /*case 'i':*/ /* Vectors of double-float */
+ case 'l': /* Vectors of long long */
+ case 's': /* Vectors of single-float */
+ case 'u': /* Vectors of unsigned long */
+ case 'y': /* Vectors of byte */
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_token (&token);
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+ /* Unknown # object. But be tolerant. */
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+
+ case '!':
+ /* Block comment '#! ... !#'. See
+ <http://www.gnu.org/software/guile/manual/html_node/Block-Comments.html>. */
+ {
+ int c;
+
+ comment_start ();
+ c = do_getc ();
+ for (;;)
+ {
+ if (c == EOF)
+ break;
+ if (c == '!')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ if (c == '#')
+ {
+ comment_line_end (0);
+ break;
+ }
+ else
+ comment_add ('!');
+ }
+ else
+ {
+ /* We skip all leading white space. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ if (c == '\n')
+ {
+ comment_line_end (1);
+ comment_start ();
+ }
+ c = do_getc ();
+ }
+ }
+ if (c == EOF)
+ {
+ /* EOF not allowed here. But be tolerant. */
+ op->type = t_eof;
+ return;
+ }
+ last_comment_line = line_number;
+ continue;
+ }
+
+ case '|':
+ /* Block comment '#| ... |#'. See
+ <http://www.gnu.org/software/guile/manual/html_node/Block-Comments.html>
+ and <http://srfi.schemers.org/srfi-30/srfi-30.html>. */
+ {
+ int depth = 0;
+ int c;
+
+ comment_start ();
+ c = do_getc ();
+ for (;;)
+ {
+ if (c == EOF)
+ break;
+ if (c == '|')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ if (c == '#')
+ {
+ if (depth == 0)
+ {
+ comment_line_end (0);
+ break;
+ }
+ depth--;
+ comment_add ('|');
+ comment_add ('#');
+ c = do_getc ();
+ }
+ else
+ comment_add ('|');
+ }
+ else if (c == '#')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ comment_add ('#');
+ if (c == '|')
+ {
+ depth++;
+ comment_add ('|');
+ c = do_getc ();
+ }
+ }
+ else
+ {
+ /* We skip all leading white space. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ if (c == '\n')
+ {
+ comment_line_end (1);
+ comment_start ();
+ }
+ c = do_getc ();
+ }
+ }
+ if (c == EOF)
+ {
+ /* EOF not allowed here. But be tolerant. */
+ op->type = t_eof;
+ return;
+ }
+ last_comment_line = line_number;
+ continue;
+ }
+
+ case '*':
+ /* Bit vector. */
+ {
+ struct token token;
+ read_token (&token, c);
+ /* The token should consists only of '0' and '1', except
+ for the initial '*'. But be tolerant. */
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '{':
+ /* Symbol with multiple escapes: #{...}# */
+ {
+ op->token = XMALLOC (struct token);
+
+ init_token (op->token);
+
+ for (;;)
+ {
+ c = do_getc ();
+
+ if (c == EOF)
+ break;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ break;
+ }
+ else if (c == '}')
+ {
+ c = do_getc ();
+ if (c == '#')
+ break;
+ if (c != EOF)
+ do_ungetc (c);
+ c = '}';
+ }
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+
+ op->type = t_symbol;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '\\':
+ /* Character. */
+ {
+ struct token token;
+ c = do_getc ();
+ if (c != EOF)
+ {
+ read_token (&token, c);
+ free_token (&token);
+ }
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case ':': /* Keyword. */
+ case '&': /* Deprecated keyword, installed in optargs.scm. */
+ {
+ struct token token;
+ read_token (&token, '-');
+ free_token (&token);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ /* The following are installed through read-hash-extend. */
+
+ /* arrays.scm */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Multidimensional array syntax: #nx(...) where
+ n ::= DIGIT+
+ x ::= {'a'|'b'|'c'|'e'|'i'|'s'|'u'}
+ */
+ do
+ c = do_getc ();
+ while (c >= '0' && c <= '9');
+ /* c should be one of {'a'|'b'|'c'|'e'|'i'|'s'|'u'}.
+ But be tolerant. */
+ /*FALLTHROUGH*/
+ case '\'': /* boot-9.scm */
+ case '.': /* boot-9.scm */
+ case ',': /* srfi-10.scm */
+ {
+ struct object inner;
+ read_object (&inner, null_context);
+ /* Dots and EOF are not allowed here.
+ But be tolerant. */
+ free_object (&inner);
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ default:
+ /* Unknown. */
+ op->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+ /*NOTREACHED*/
+ abort ();
+ }
+
+ case '_':
+ /* GIMP script-fu extension: '_' before a string literal is
+ considered a gettext call on the string. */
+ {
+ int c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ {
+ op->type = t_other;
+ return;
+ }
+ if (c != '"')
+ {
+ do_ungetc (c);
+
+ /* If '_' is not followed by a string literal,
+ consider it a part of symbol. */
+ op->token = XMALLOC (struct token);
+ read_token (op->token, '_');
+ op->type = t_symbol;
+ last_non_comment_line = line_number;
+ return;
+ }
+ seen_underscore_prefix = true;
+ }
+ /*FALLTHROUGH*/
+
+ case '"':
+ {
+ op->token = XMALLOC (struct token);
+ init_token (op->token);
+ op->line_number_at_start = line_number;
+ for (;;)
+ {
+ int c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ if (c == '"')
+ break;
+ if (c == '\\')
+ {
+ c = do_getc ();
+ if (c == EOF)
+ /* Invalid input. Be tolerant, no error message. */
+ break;
+ switch (c)
+ {
+ case '\n':
+ continue;
+ case '0':
+ c = '\0';
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ default:
+ break;
+ }
+ }
+ grow_token (op->token);
+ op->token->chars[op->token->charcount++] = c;
+ }
+ op->type = t_string;
+
+ if (seen_underscore_prefix || extract_all)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = op->line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_object (op),
+ null_context, &pos, NULL, savable_comment);
+ }
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '+': case '-': case '.':
+ /* Read a number or symbol token. */
+ op->token = XMALLOC (struct token);
+ read_token (op->token, c);
+ if (op->token->charcount == 1 && op->token->chars[0] == '.')
+ {
+ free_token (op->token);
+ free (op->token);
+ op->type = t_dot;
+ }
+ else if (is_number (op->token))
+ {
+ /* A number. */
+ free_token (op->token);
+ free (op->token);
+ op->type = t_other;
+ }
+ else
+ {
+ /* A symbol. */
+ op->type = t_symbol;
+ }
+ last_non_comment_line = line_number;
+ return;
+
+ case ':':
+ default:
+ /* Read a symbol token. */
+ op->token = XMALLOC (struct token);
+ read_token (op->token, c);
+ op->type = t_symbol;
+ last_non_comment_line = line_number;
+ return;
+ }
+ }
+}
+
+
+void
+extract_scheme (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When read_object returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ do
+ {
+ struct object toplevel_object;
+
+ read_object (&toplevel_object, null_context);
+
+ if (toplevel_object.type == t_eof)
+ break;
+
+ free_object (&toplevel_object);
+ }
+ while (!feof (fp));
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-scheme.h b/gettext-tools/src/x-scheme.h
new file mode 100644
index 0000000..a159ab1
--- /dev/null
+++ b/gettext-tools/src/x-scheme.h
@@ -0,0 +1,54 @@
+/* xgettext Scheme backend.
+ Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2004.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_SCHEME \
+ { "scm", "Scheme" }, \
+
+#define SCANNERS_SCHEME \
+ { "Scheme", extract_scheme, \
+ &flag_table_scheme, &formatstring_scheme, NULL, NULL }, \
+
+/* Scan a Scheme file and add its translatable strings to mdlp. */
+extern void extract_scheme (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_scheme_extract_all (void);
+extern void x_scheme_keyword (const char *name);
+
+extern void init_flag_table_scheme (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-sh.c b/gettext-tools/src/x-sh.c
new file mode 100644
index 0000000..9a0614f
--- /dev/null
+++ b/gettext-tools/src/x-sh.c
@@ -0,0 +1,1360 @@
+/* xgettext sh backend.
+ Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-sh.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The sh syntax is defined in POSIX:2001, see
+ http://www.opengroup.org/onlinepubs/007904975/utilities/xcu_chap02.html
+ Summary of sh syntax:
+ - Input is broken into words, which are then subject to
+ - tilde expansion ~...
+ - command substitution `...`
+ - variable substitution $var
+ - arithmetic substitution $((...))
+ - field splitting at whitespace (IFS)
+ - wildcard pattern expansion *?
+ - quote removal
+ - Strings are enclosed in "..."; command substitution, variable
+ substitution and arithmetic substitution are performed here as well.
+ - '...' is a string without substitutions.
+ - The list of resulting words is split into commands by semicolon and
+ newline.
+ - '#' at the beginning of a word introduces a comment until end of line.
+ The parser is implemented in bash-2.05b/parse.y. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_sh_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_sh_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_sh_keyword ("gettext");
+ x_sh_keyword ("ngettext:1,2");
+ x_sh_keyword ("eval_gettext");
+ x_sh_keyword ("eval_ngettext:1,2");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_sh ()
+{
+ xgettext_record_flag ("gettext:1:pass-sh-format");
+ xgettext_record_flag ("ngettext:1:pass-sh-format");
+ xgettext_record_flag ("ngettext:2:pass-sh-format");
+ xgettext_record_flag ("eval_gettext:1:sh-format");
+ xgettext_record_flag ("eval_ngettext:1:sh-format");
+ xgettext_record_flag ("eval_ngettext:2:sh-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* Remove backslash followed by newline from the input stream. */
+
+static int phase1_pushback[1];
+static int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ {
+ c = phase1_pushback[--phase1_pushback_length];
+ if (c == '\n')
+ ++line_number;
+ return c;
+ }
+ for (;;)
+ {
+ c = do_getc ();
+ if (c != '\\')
+ return c;
+ c = do_getc ();
+ if (c != '\n')
+ {
+ if (c != EOF)
+ do_ungetc (c);
+ return '\\';
+ }
+ }
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ switch (c)
+ {
+ case EOF:
+ break;
+
+ case '\n':
+ --line_number;
+ /* FALLTHROUGH */
+
+ default:
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ break;
+ }
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* A token consists of a sequence of characters. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ char *chars; /* the token's constituents */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (char *) xrealloc (tp->chars, tp->allocated * sizeof (char));
+ }
+}
+
+/* Convert a struct token * to a char*. */
+static char *
+string_of_token (const struct token *tp)
+{
+ char *str;
+ int n;
+
+ n = tp->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, tp->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end ()
+{
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Debackslashification ========================== */
+
+/* This state tracks the effect of backquotes, double-quotes and single-quotes
+ on the parsing of backslashes. We make a single pass through the input
+ file, keeping the state up to date. This is much faster than accumulating
+ strings and processing them with explicit debackslashification, like the
+ shell does it. */
+
+/* The number of nested `...` or "`...`" constructs. Assumed to be <= 32. */
+static unsigned int nested_backquotes;
+
+/* A bit mask indicating which of the currently open `...` or "`...`"
+ constructs is with double-quotes: "`...`".
+ A bit value of 1 stands for "`...`", a bit value of 0 stands for `...`.
+ Bit position 0 designates the outermost backquotes nesting,
+ bit position 1 the second-outermost backquotes nesting,
+ ...
+ bit position (nested_backquotes-1) the innermost backquotes nesting. */
+static unsigned int open_doublequotes_mask;
+
+/* A bit indicating whether a double-quote is currently open inside the
+ innermost backquotes nesting. */
+static bool open_doublequote;
+
+/* A bit indicating whether a single-quote is currently open inside the
+ innermost backquotes nesting. */
+static bool open_singlequote;
+
+/* The expected terminator of the currently open single-quote.
+ Usually '\'', but can be '"' for i18n-quotes. */
+static char open_singlequote_terminator;
+
+
+/* Functions to update the state. */
+
+static inline void
+saw_opening_backquote ()
+{
+ if (open_singlequote)
+ abort ();
+ if (open_doublequote)
+ open_doublequotes_mask |= (unsigned int) 1 << nested_backquotes;
+ nested_backquotes++;
+ open_doublequote = false;
+}
+
+static inline void
+saw_closing_backquote ()
+{
+ nested_backquotes--;
+ open_doublequote = (open_doublequotes_mask >> nested_backquotes) & 1;
+ open_doublequotes_mask &= ((unsigned int) 1 << nested_backquotes) - 1;
+ open_singlequote = false; /* just for safety */
+}
+
+static inline void
+saw_opening_doublequote ()
+{
+ if (open_singlequote || open_doublequote)
+ abort ();
+ open_doublequote = true;
+}
+
+static inline void
+saw_closing_doublequote ()
+{
+ if (open_singlequote || !open_doublequote)
+ abort ();
+ open_doublequote = false;
+}
+
+static inline void
+saw_opening_singlequote ()
+{
+ if (open_doublequote || open_singlequote)
+ abort ();
+ open_singlequote = true;
+ open_singlequote_terminator = '\'';
+}
+
+static inline void
+saw_closing_singlequote ()
+{
+ if (open_doublequote || !open_singlequote)
+ abort ();
+ open_singlequote = false;
+}
+
+
+/* ========================== Reading of commands ========================== */
+
+/* We are only interested in constant strings. Other words need not to be
+ represented precisely. */
+enum word_type
+{
+ t_string, /* constant string */
+ t_other, /* other string */
+ t_separator, /* command separator: semicolon or newline */
+ t_redirect, /* redirection: one of < > >| << <<- >> <> <& >& */
+ t_backquote, /* closing '`' pseudo word */
+ t_paren, /* closing ')' pseudo word */
+ t_eof /* EOF marker */
+};
+
+struct word
+{
+ enum word_type type;
+ struct token *token; /* for t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct word'. */
+static inline void
+free_word (struct word *wp)
+{
+ if (wp->type == t_string)
+ {
+ free_token (wp->token);
+ free (wp->token);
+ }
+}
+
+/* Convert a t_string token to a char*. */
+static char *
+string_of_word (const struct word *wp)
+{
+ char *str;
+ int n;
+
+ if (!(wp->type == t_string))
+ abort ();
+ n = wp->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, wp->token->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+
+/* Whitespace recognition. */
+
+static inline bool
+is_whitespace (int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n');
+}
+
+/* Operator character recognition. */
+
+static inline bool
+is_operator_start (int c)
+{
+ return (c == '|' || c == '&' || c == ';' || c == '<' || c == '>'
+ || c == '(' || c == ')');
+}
+
+
+/* Denotation of a quoted character.
+ The distinction between quoted and unquoted character is important only for
+ the special, whitespace and operator characters; it is irrelevant for
+ alphanumeric characters, '\\' and many others. */
+#define QUOTED(c) (UCHAR_MAX + 1 + (c))
+/* Values in the 'unsigned char' range are implicitly unquoted. Among these,
+ the following are important:
+ '"' opening or closing double quote
+ '\'' opening or closing single quote
+ '$' the unknown result of a dollar expansion
+ '`' does not occur - replaced with OPENING_BACKQUOTE or
+ CLOSING_BACKQUOTE
+ */
+#define OPENING_BACKQUOTE (2 * (UCHAR_MAX + 1) + '`')
+#define CLOSING_BACKQUOTE (3 * (UCHAR_MAX + 1) + '`')
+
+/* 2 characters of pushback are supported.
+ 2 characters of pushback occur only when the first is an 'x'; in all
+ other cases only one character of pushback is needed. */
+static int phase2_pushback[2];
+static int phase2_pushback_length;
+
+/* Return the next character, with backslashes removed.
+ The result is QUOTED(c) for some unsigned char c, if the next character
+ is escaped sufficiently often to make it a regular constituent character,
+ or simply an 'unsigned char' if it has its special meaning (of special,
+ whitespace or operator charcter), or OPENING_BACKQUOTE, CLOSING_BACKQUOTE,
+ EOF.
+ It's the caller's responsibility to update the state. */
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ {
+ c = phase2_pushback[--phase2_pushback_length];
+ if (c == '\n')
+ ++line_number;
+ return c;
+ }
+
+ c = phase1_getc ();
+ if (c == EOF)
+ return c;
+ if (c == '\'')
+ return ((open_doublequote
+ || (open_singlequote && open_singlequote_terminator != c))
+ ? QUOTED (c)
+ : c);
+ if (open_singlequote)
+ {
+ if (c == open_singlequote_terminator)
+ return c;
+ }
+ else
+ {
+ if (c == '"' || c == '$')
+ return c;
+ if (c == '`')
+ return (nested_backquotes > 0 ? CLOSING_BACKQUOTE : OPENING_BACKQUOTE);
+ }
+ if (c == '\\')
+ {
+ /* Number of debackslashification passes that are active at the
+ current point. */
+ unsigned int debackslashify =
+ nested_backquotes + (open_singlequote ? 0 : 1);
+ /* Normal number of backslashes that yield a single backslash in the
+ final output. */
+ unsigned int expected_count =
+ (unsigned int) 1 << debackslashify;
+ /* Number of backslashes found. */
+ unsigned int count;
+
+ for (count = 1; count < expected_count; count++)
+ {
+ c = phase1_getc ();
+ if (c != '\\')
+ break;
+ }
+ if (count == expected_count)
+ return '\\';
+
+ /* The count of backslashes is > 0 and < expected_count, therefore the
+ result depends on c, the first character after the backslashes.
+ Note: The formulas below don't necessarily have a logic; they were
+ empirically determined such that 1. the xgettext-30 test succeeds,
+ 2. the behaviour for count == 0 would correspond to the one without
+ any baskslash. */
+ if (c == '\'')
+ {
+ if (!open_singlequote && count > (expected_count >> 1))
+ {
+ phase1_ungetc (c);
+ return '\\';
+ }
+ else
+ return ((open_doublequote
+ || (open_singlequote && open_singlequote_terminator != c))
+ ? QUOTED (c)
+ : c);
+ }
+ else if (c == '"')
+ {
+ /* Each debackslashification pass converts \\ to \ and \" to ";
+ passes corresponding to `...` drop a lone " whereas passes
+ corresponding to "`...`" leave it alone. Therefore, the
+ minimum number of backslashes needed to get one double-quote
+ in the end is open_doublequotes_mask + 1. */
+ if (open_singlequote)
+ {
+ if (count > open_doublequotes_mask)
+ {
+ phase1_ungetc (c);
+ return '\\';
+ }
+ else
+ return (open_singlequote_terminator != c ? QUOTED (c) : c);
+ }
+ else
+ {
+ if (count > open_doublequotes_mask)
+ return QUOTED (c);
+ else
+ /* Some of the count values <= open_doublequotes_mask are
+ actually invalid here, but we assume a syntactically
+ correct input file anyway. */
+ return c;
+ }
+ }
+ else if (c == '`')
+ {
+ /* FIXME: This code looks fishy. */
+ if (count == expected_count - 1)
+ return c;
+ else
+ /* Some of the count values < expected_count - 1 are
+ actually invalid here, but we assume a syntactically
+ correct input file anyway. */
+ if (nested_backquotes > 0 && !open_singlequote
+ && count >= (expected_count >> 2))
+ return OPENING_BACKQUOTE;
+ else
+ return CLOSING_BACKQUOTE;
+ }
+ else if (c == '$')
+ {
+ if (open_singlequote)
+ return QUOTED (c);
+ if (count >= (expected_count >> 1))
+ return QUOTED (c);
+ else
+ return c;
+ }
+ else
+ {
+ /* When not followed by a quoting character or backslash or dollar,
+ a backslash survives a debackslashification pass unmodified.
+ Therefore each debackslashification pass performs a
+ count := (count + 1) >> 1
+ operation. Therefore the minimum number of backslashes needed
+ to get one backslash in the end is (expected_count >> 1) + 1. */
+ if (open_doublequote || open_singlequote)
+ {
+ if (count > 0)
+ {
+ phase1_ungetc (c);
+ return '\\';
+ }
+ else
+ return QUOTED (c);
+ }
+ else
+ {
+ if (count > (expected_count >> 1))
+ {
+ phase1_ungetc (c);
+ return '\\';
+ }
+ else if (count > 0)
+ return QUOTED (c);
+ else
+ return c;
+ }
+ }
+ }
+
+ return (open_singlequote || open_doublequote ? QUOTED (c) : c);
+}
+
+/* Supports 2 characters of pushback. */
+static void
+phase2_ungetc (int c)
+{
+ switch (c)
+ {
+ case EOF:
+ break;
+
+ case '\n':
+ --line_number;
+ /* FALLTHROUGH */
+
+ default:
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ break;
+ }
+}
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* Forward declaration of local functions. */
+static enum word_type read_command_list (int looking_for,
+ flag_context_ty outer_context);
+
+
+
+/* Read the next word.
+ 'looking_for' denotes a parse terminator, either CLOSING_BACKQUOTE, ')'
+ or '\0'. */
+static void
+read_word (struct word *wp, int looking_for, flag_context_ty context)
+{
+ int c;
+ bool all_unquoted_digits;
+
+ do
+ {
+ c = phase2_getc ();
+ if (c == '#')
+ {
+ /* Skip a comment up to end of line. */
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF || c == '\n')
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end ();
+ }
+ if (c == '\n')
+ {
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between
+ both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ wp->type = t_separator;
+ return;
+ }
+ }
+ while (is_whitespace (c));
+
+ if (c == EOF)
+ {
+ wp->type = t_eof;
+ return;
+ }
+
+ if (c == '<' || c == '>')
+ {
+ /* Recognize the redirection operators < > >| << <<- >> <> <& >&
+ But <( and >) are handled below, not here. */
+ int c2 = phase2_getc ();
+ if (c2 != '(')
+ {
+ if ((c == '<' ? c2 == '<' : c2 == '|') || c2 == '>' || c2 == '&')
+ {
+ if (c == '<' && c2 == '<')
+ {
+ int c3 = phase2_getc ();
+ if (c3 != '-')
+ phase2_ungetc (c3);
+ }
+ }
+ else
+ phase2_ungetc (c2);
+ wp->type = t_redirect;
+ return;
+ }
+ else
+ phase2_ungetc (c2);
+ }
+
+ if (looking_for == CLOSING_BACKQUOTE && c == CLOSING_BACKQUOTE)
+ {
+ saw_closing_backquote ();
+ wp->type = t_backquote;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ if (looking_for == ')' && c == ')')
+ {
+ wp->type = t_paren;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ if (is_operator_start (c))
+ {
+ wp->type = (c == ';' ? t_separator : t_other);
+ return;
+ }
+
+ wp->type = t_string;
+ wp->token = XMALLOC (struct token);
+ init_token (wp->token);
+ wp->line_number_at_start = line_number;
+ all_unquoted_digits = true;
+
+ for (;; c = phase2_getc ())
+ {
+ if (c == EOF)
+ break;
+
+ if (all_unquoted_digits && (c == '<' || c == '>'))
+ {
+ /* Recognize the redirection operators < > >| << <<- >> <> <& >&
+ prefixed with a nonempty sequence of unquoted digits. */
+ int c2 = phase2_getc ();
+ if ((c == '<' ? c2 == '<' : c2 == '|') || c2 == '>' || c2 == '&')
+ {
+ if (c == '<' && c2 == '<')
+ {
+ int c3 = phase2_getc ();
+ if (c3 != '-')
+ phase2_ungetc (c3);
+ }
+ }
+ else
+ phase2_ungetc (c2);
+
+ wp->type = t_redirect;
+ free_token (wp->token);
+ free (wp->token);
+
+ last_non_comment_line = line_number;
+
+ return;
+ }
+
+ all_unquoted_digits = all_unquoted_digits && (c >= '0' && c <= '9');
+
+ if (c == '$')
+ {
+ int c2;
+
+ /* An unquoted dollar indicates we are not inside '...'. */
+ if (open_singlequote)
+ abort ();
+ /* After reading a dollar, we know that there is no pushed back
+ character from an earlier lookahead. */
+ if (phase2_pushback_length > 0)
+ abort ();
+ /* Therefore we can use phase1 without interfering with phase2.
+ We need to recognize $( outside and inside double-quotes.
+ It would be incorrect to do
+ c2 = phase2_getc ();
+ if (c2 == '(' || c2 == QUOTED ('('))
+ because that would also trigger for $\(. */
+ c2 = phase1_getc ();
+ if (c2 == '(')
+ {
+ bool saved_open_doublequote;
+ int c3;
+
+ phase1_ungetc (c2);
+
+ /* The entire inner command or arithmetic expression is read
+ ignoring possible surrounding double-quotes. */
+ saved_open_doublequote = open_doublequote;
+ open_doublequote = false;
+
+ c2 = phase2_getc ();
+ if (c2 != '(')
+ abort ();
+
+ c3 = phase2_getc ();
+ if (c3 == '(')
+ {
+ /* Arithmetic expression (Bash syntax). Skip until the
+ matching closing parenthesis. */
+ unsigned int depth = 2;
+
+ do
+ {
+ c = phase2_getc ();
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ if (--depth == 0)
+ break;
+ }
+ while (c != EOF);
+ }
+ else
+ {
+ /* Command substitution (Bash syntax). */
+ phase2_ungetc (c3);
+ read_command_list (')', context);
+ }
+
+ open_doublequote = saved_open_doublequote;
+ }
+ else
+ {
+ phase1_ungetc (c2);
+ c2 = phase2_getc ();
+
+ if (c2 == '\'' && !open_singlequote)
+ {
+ /* Bash builtin for string with ANSI-C escape sequences. */
+ for (;;)
+ {
+ /* We have to use phase1 throughout this loop,
+ because phase2 does debackslashification,
+ which is undesirable when parsing ANSI-C
+ escape sequences. */
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c == '\'')
+ break;
+ if (c == '\\')
+ {
+ c = phase1_getc ();
+ switch (c)
+ {
+ default:
+ phase1_ungetc (c);
+ c = '\\';
+ break;
+
+ case '\\':
+ break;
+ case '\'':
+ break;
+ case '"':
+ break;
+
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'e':
+ case 'E':
+ c = 0x1b; /* ESC */
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+
+ case 'x':
+ c = phase1_getc ();
+ if ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || (c >= 'a' && c <= 'f'))
+ {
+ int n;
+
+ if (c >= '0' && c <= '9')
+ n = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ n = 10 + c - 'A';
+ else if (c >= 'a' && c <= 'f')
+ n = 10 + c - 'a';
+ else
+ abort ();
+
+ c = phase1_getc ();
+ if ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || (c >= 'a' && c <= 'f'))
+ {
+ if (c >= '0' && c <= '9')
+ n = n * 16 + c - '0';
+ else if (c >= 'A' && c <= 'F')
+ n = n * 16 + 10 + c - 'A';
+ else if (c >= 'a' && c <= 'f')
+ n = n * 16 + 10 + c - 'a';
+ else
+ abort ();
+ }
+ else
+ phase1_ungetc (c);
+
+ c = n;
+ }
+ else
+ {
+ phase1_ungetc (c);
+ phase1_ungetc ('x');
+ c = '\\';
+ }
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = phase1_getc ();
+ if (c >= '0' && c <= '7')
+ {
+ n = n * 8 + c - '0';
+
+ c = phase1_getc ();
+ if (c >= '0' && c <= '7')
+ n = n * 8 + c - '0';
+ else
+ phase1_ungetc (c);
+ }
+ else
+ phase1_ungetc (c);
+
+ c = n;
+ }
+ break;
+ }
+ }
+ if (wp->type == t_string)
+ {
+ grow_token (wp->token);
+ wp->token->chars[wp->token->charcount++] =
+ (unsigned char) c;
+ }
+ }
+ /* The result is a literal string. Don't change wp->type. */
+ continue;
+ }
+ else if (c2 == '"' && !open_doublequote)
+ {
+ /* Bash builtin for internationalized string. */
+ lex_pos_ty pos;
+ struct token string;
+
+ saw_opening_singlequote ();
+ open_singlequote_terminator = '"';
+ pos.file_name = logical_file_name;
+ pos.line_number = line_number;
+ init_token (&string);
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == EOF)
+ break;
+ if (c == '"')
+ {
+ saw_closing_singlequote ();
+ break;
+ }
+ grow_token (&string);
+ string.chars[string.charcount++] = (unsigned char) c;
+ }
+ remember_a_message (mlp, NULL, string_of_token (&string),
+ context, &pos, NULL, savable_comment);
+ free_token (&string);
+
+ error_with_progname = false;
+ error (0, 0, _("%s:%lu: warning: the syntax $\"...\" is deprecated due to security reasons; use eval_gettext instead"),
+ pos.file_name, (unsigned long) pos.line_number);
+ error_with_progname = true;
+
+ /* The result at runtime is not constant. Therefore we
+ change wp->type. */
+ }
+ else
+ phase2_ungetc (c2);
+ }
+ wp->type = t_other;
+ continue;
+ }
+
+ if (c == '\'')
+ {
+ if (!open_singlequote)
+ {
+ /* Handle an opening single quote. */
+ saw_opening_singlequote ();
+ }
+ else
+ {
+ /* Handle a closing single quote. */
+ saw_closing_singlequote ();
+ }
+ continue;
+ }
+
+ if (c == '"')
+ {
+ if (open_singlequote && open_singlequote_terminator == '"')
+ {
+ /* Handle a closing i18n quote. */
+ saw_closing_singlequote ();
+ }
+ else if (!open_doublequote)
+ {
+ /* Handle an opening double quote. */
+ saw_opening_doublequote ();
+ }
+ else
+ {
+ /* Handle a closing double quote. */
+ saw_closing_doublequote ();
+ }
+ continue;
+ }
+
+ if (c == OPENING_BACKQUOTE)
+ {
+ /* Handle an opening backquote. */
+ saw_opening_backquote ();
+
+ read_command_list (CLOSING_BACKQUOTE, context);
+
+ wp->type = t_other;
+ continue;
+ }
+ if (c == CLOSING_BACKQUOTE)
+ break;
+
+ if (c == '<' || c == '>')
+ {
+ int c2;
+
+ /* An unquoted c indicates we are not inside '...' nor "...". */
+ if (open_singlequote || open_doublequote)
+ abort ();
+
+ c2 = phase2_getc ();
+ if (c2 == '(')
+ {
+ /* Process substitution (Bash syntax). */
+ read_command_list (')', context);
+
+ wp->type = t_other;
+ continue;
+ }
+ else
+ phase2_ungetc (c2);
+ }
+
+ if (!open_singlequote && !open_doublequote
+ && (is_whitespace (c) || is_operator_start (c)))
+ break;
+
+ if (wp->type == t_string)
+ {
+ grow_token (wp->token);
+ wp->token->chars[wp->token->charcount++] = (unsigned char) c;
+ }
+ }
+
+ phase2_ungetc (c);
+
+ if (wp->type != t_string)
+ {
+ free_token (wp->token);
+ free (wp->token);
+ }
+ last_non_comment_line = line_number;
+}
+
+
+/* Read the next command.
+ 'looking_for' denotes a parse terminator, either CLOSING_BACKQUOTE, ')'
+ or '\0'.
+ Returns the type of the word that terminated the command. */
+static enum word_type
+read_command (int looking_for, flag_context_ty outer_context)
+{
+ /* Read the words that make up the command.
+ Here we completely ignore field splitting at whitespace and wildcard
+ expansions; i.e. we assume that the source is written in such a way that
+ every word in the program determines exactly one word in the resulting
+ command.
+ But we do not require that the 'gettext'/'ngettext' command is the
+ first in the command; this is because 1. we want to allow for prefixes
+ like "$verbose" that may expand to nothing, and 2. it's a big effort
+ to know where a command starts in a $(for ...) or $(case ...) compound
+ command. */
+ int arg = 0; /* Current argument number. */
+ bool arg_of_redirect = false; /* True right after a redirection operator. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;;)
+ {
+ struct word inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_word (&inner, looking_for, inner_context);
+
+ /* Recognize end of command. */
+ if (inner.type == t_separator
+ || inner.type == t_backquote || inner.type == t_paren
+ || inner.type == t_eof)
+ {
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return inner.type;
+ }
+
+ if (extract_all)
+ {
+ if (inner.type == t_string)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = inner.line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_word (&inner),
+ inner_context, &pos, NULL, savable_comment);
+ }
+ }
+
+ if (arg_of_redirect)
+ {
+ /* Ignore arguments of redirection operators. */
+ arg_of_redirect = false;
+ }
+ else if (inner.type == t_redirect)
+ {
+ /* Ignore this word and the following one. */
+ arg_of_redirect = true;
+ }
+ else
+ {
+ if (argparser == NULL)
+ {
+ /* This is the function position. */
+ arg = 0;
+ if (inner.type == t_string)
+ {
+ char *function_name = string_of_word (&inner);
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords,
+ function_name, strlen (function_name),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ function_name, strlen (function_name)));
+
+ free (function_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_word (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+
+ if (arglist_parser_decidedp (argparser, arg))
+ {
+ /* Stop looking for arguments of the last function_name. */
+ /* FIXME: What about context_iter? */
+ arglist_parser_done (argparser, arg);
+ shapes = NULL;
+ argparser = NULL;
+ }
+ }
+
+ arg++;
+ }
+
+ free_word (&inner);
+ }
+}
+
+
+/* Read a list of commands.
+ 'looking_for' denotes a parse terminator, either CLOSING_BACKQUOTE, ')'
+ or '\0'.
+ Returns the type of the word that terminated the command list. */
+static enum word_type
+read_command_list (int looking_for, flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ enum word_type terminator;
+
+ terminator = read_command (looking_for, outer_context);
+ if (terminator != t_separator)
+ return terminator;
+ }
+}
+
+
+void
+extract_sh (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ nested_backquotes = 0;
+ open_doublequotes_mask = 0;
+ open_doublequote = false;
+ open_singlequote = false;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. */
+ read_command_list ('\0', null_context);
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-sh.h b/gettext-tools/src/x-sh.h
new file mode 100644
index 0000000..d235751
--- /dev/null
+++ b/gettext-tools/src/x-sh.h
@@ -0,0 +1,52 @@
+/* xgettext sh backend.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_SH \
+ { "sh", "Shell" }, \
+ { "bash", "Shell" }, \
+
+#define SCANNERS_SH \
+ { "Shell", extract_sh, \
+ &flag_table_sh, &formatstring_sh, NULL, NULL }, \
+
+/* Scan a shell script file and add its translatable strings to mdlp. */
+extern void extract_sh (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_sh_keyword (const char *keyword);
+extern void x_sh_extract_all (void);
+
+extern void init_flag_table_sh (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-smalltalk.c b/gettext-tools/src/x-smalltalk.c
new file mode 100644
index 0000000..c3f9699
--- /dev/null
+++ b/gettext-tools/src/x-smalltalk.c
@@ -0,0 +1,598 @@
+/* xgettext Smalltalk backend.
+ Copyright (C) 2002-2003, 2005-2009, 2011 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-smalltalk.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The relevant parts of the Smalltalk syntax are:
+
+ stringliteral ::= string | stringconst | symconst
+ stringconst ::= "#"string
+ string ::= "'"[char]*"'"
+ symconst ::= "#"symbol
+ symbol ::= id | binsel | keysel[keysel]*
+ keysel ::= id":"
+ id ::= letter[letter|digit]*
+ letter ::= "A".."Z" | "a".."z"
+ digit ::= "0".."9"
+ binsel ::= selchar[selchar]
+ selchar ::= "+" | "-" | "*" | "/" | "~" | "|" | "," | "<" | ">"
+ | "=" | "&" | "@" | "?" | "%" | "\"
+
+ Strings can contain any characters; to include the string delimiter itself,
+ it must be duplicated.
+
+ Character constants are written "$"char
+
+ Comments are enclosed within double quotes.
+
+ In well-formed expressions, {} and [] and () are balanced.
+ */
+
+
+/* ======================== Reading of characters. ======================== */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 1. line_number handling. */
+
+static int
+phase1_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ ungetc (c, fp);
+ }
+}
+
+
+/* Accumulating comments. */
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end ()
+{
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_uniq, /* # */
+ token_type_symbol, /* symbol */
+ token_type_string_literal, /* string, stringconst, symbolconst */
+ token_type_other /* misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ int line_number;
+};
+
+
+/* 2. Combine characters into tokens. Discard comments and whitespace. */
+
+static token_ty phase2_pushback[1];
+static int phase2_pushback_length;
+
+static void
+phase2_get (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+
+ if (phase2_pushback_length)
+ {
+ *tp = phase2_pushback[--phase2_pushback_length];
+ return;
+ }
+
+ tp->string = NULL;
+
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase1_getc ();
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '"':
+ {
+ /* Comment. */
+ int lineno;
+
+ comment_start ();
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '"' || c == EOF)
+ break;
+ if (c == '\n')
+ {
+ comment_line_end ();
+ comment_start ();
+ }
+ else
+ {
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ }
+ comment_line_end ();
+ last_comment_line = lineno;
+ continue;
+ }
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\t':
+ case '\r':
+ /* Ignore whitespace. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case '\'':
+ /* String literal. */
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ if (c == '\'')
+ {
+ c = phase1_getc ();
+ if (c != '\'')
+ {
+ phase1_ungetc (c);
+ break;
+ }
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = 0;
+ tp->type = token_type_string_literal;
+ tp->string = xstrdup (buffer);
+ return;
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '~':
+ case '|':
+ case ',':
+ case '<':
+ case '>':
+ case '=':
+ case '&':
+ case '@':
+ case '?':
+ case '%':
+ case '\\':
+ {
+ char *name;
+ int c2 = phase1_getc ();
+ switch (c2)
+ {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '~':
+ case '|':
+ case ',':
+ case '<':
+ case '>':
+ case '=':
+ case '&':
+ case '@':
+ case '?':
+ case '%':
+ name = XNMALLOC (3, char);
+ name[0] = c;
+ name[1] = c2;
+ name[2] = '\0';
+ tp->type = token_type_symbol;
+ tp->string = name;
+ return;
+ default:
+ phase1_ungetc (c2);
+ break;
+ }
+ name = XNMALLOC (2, char);
+ name[0] = c;
+ name[1] = '\0';
+ tp->type = token_type_symbol;
+ tp->string = name;
+ return;
+ }
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ /* Recognize id or id":"[id":"]* or id":"[id":"]*id. */
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ case ':':
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase1_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ continue;
+ default:
+ phase1_ungetc (c);
+ break;
+ }
+ break;
+ default:
+ phase1_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ return;
+
+ case '#':
+ /* Uniquification operator. */
+ tp->type = token_type_uniq;
+ return;
+
+ case '$':
+ c = phase1_getc ();
+ tp->type = token_type_other;
+ return;
+
+ default:
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports only one pushback token. */
+static void
+phase2_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = *tp;
+ }
+}
+
+
+/* 3. Combine "# string_literal" and "# symbol" to a single token. */
+
+static void
+x_smalltalk_lex (token_ty *tp)
+{
+ phase2_get (tp);
+ if (tp->type == token_type_uniq)
+ {
+ token_ty token2;
+
+ phase2_get (&token2);
+ if (token2.type == token_type_symbol
+ || token2.type == token_type_string_literal)
+ {
+ tp->type = token_type_string_literal;
+ tp->string = token2.string;
+ }
+ else
+ phase2_unget (&token2);
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+/* The file is broken into tokens. Scan the token stream, looking for the
+ following patterns
+ NLS ? <string>
+ NLS at: <string>
+ NLS at: <string> plural: <string>
+ where <string> is one of
+ string_literal
+ # string_literal
+ # symbol
+ */
+
+void
+extract_smalltalk (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ /* Eat tokens until eof is seen. */
+ {
+ /* 0 when no "NLS" has been seen.
+ 1 after "NLS".
+ 2 after "NLS ?".
+ 3 after "NLS at:".
+ 4 after "NLS at: <string>".
+ 5 after "NLS at: <string> plural:". */
+ int state;
+ /* Remember the message containing the msgid, for msgid_plural.
+ Non-NULL in states 4, 5. */
+ message_ty *plural_mp = NULL;
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_smalltalk_lex (&token);
+
+ switch (token.type)
+ {
+ case token_type_symbol:
+ state = (strcmp (token.string, "NLS") == 0 ? 1 :
+ strcmp (token.string, "?") == 0 && state == 1 ? 2 :
+ strcmp (token.string, "at:") == 0 && state == 1 ? 3 :
+ strcmp (token.string, "plural:") == 0 && state == 4 ? 5 :
+ 0);
+ free (token.string);
+ break;
+
+ case token_type_string_literal:
+ if (state == 2)
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+ remember_a_message (mlp, NULL, token.string, null_context,
+ &pos, NULL, savable_comment);
+ state = 0;
+ break;
+ }
+ if (state == 3)
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+ plural_mp = remember_a_message (mlp, NULL, token.string,
+ null_context, &pos,
+ NULL, savable_comment);
+ state = 4;
+ break;
+ }
+ if (state == 5)
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+ if (plural_mp != NULL)
+ remember_a_message_plural (plural_mp, token.string,
+ null_context, &pos,
+ savable_comment);
+ state = 0;
+ break;
+ }
+ state = 0;
+ free (token.string);
+ break;
+
+ case token_type_uniq:
+ case token_type_other:
+ state = 0;
+ break;
+
+ case token_type_eof:
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (token.type == token_type_eof)
+ break;
+ }
+ }
+
+ /* Close scanner. */
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-smalltalk.h b/gettext-tools/src/x-smalltalk.h
new file mode 100644
index 0000000..10767d9
--- /dev/null
+++ b/gettext-tools/src/x-smalltalk.h
@@ -0,0 +1,46 @@
+/* xgettext Smalltalk backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_SMALLTALK \
+ { "st", "Smalltalk" }, \
+
+#define SCANNERS_SMALLTALK \
+ { "Smalltalk", extract_smalltalk, \
+ NULL, &formatstring_smalltalk, NULL, NULL }, \
+
+/* Scan a Smalltalk file and add its translatable strings to mdlp. */
+extern void extract_smalltalk (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-stringtable.h b/gettext-tools/src/x-stringtable.h
new file mode 100644
index 0000000..57df745
--- /dev/null
+++ b/gettext-tools/src/x-stringtable.h
@@ -0,0 +1,45 @@
+/* xgettext NXStringTable backend.
+ Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_STRINGTABLE \
+ { "strings", "NXStringTable" }, \
+
+#define SCANNERS_STRINGTABLE \
+ { "NXStringTable", extract_stringtable, NULL, NULL, NULL, NULL }, \
+
+/* Scan a JavaProperties file and add its translatable strings to mdlp. */
+extern void extract_stringtable (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-tcl.c b/gettext-tools/src/x-tcl.c
new file mode 100644
index 0000000..37dd19e
--- /dev/null
+++ b/gettext-tools/src/x-tcl.c
@@ -0,0 +1,999 @@
+/* xgettext Tcl backend.
+ Copyright (C) 2002-2003, 2005-2009 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-tcl.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "hash.h"
+#include "c-ctype.h"
+#include "po-charset.h"
+#include "unistr.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The Tcl syntax is defined in the Tcl.n manual page.
+ Summary of Tcl syntax:
+ Like sh syntax, except that `...` is replaced with [...]. In detail:
+ - In a preprocessing pass, backslash-newline-anywhitespace is replaced
+ with single space.
+ - Input is broken into words, which are then subject to command
+ substitution [...] , variable substitution $var, backslash substitution
+ \escape.
+ - Strings are enclosed in "..."; command substitution, variable
+ substitution and backslash substitutions are performed here as well.
+ - {...} is a string without substitutions.
+ - The list of resulting words is split into commands by semicolon and
+ newline.
+ - '#' at the beginning of a command introduces a comment until end of line.
+ The parser is implemented in tcl8.3.3/generic/tclParse.c. */
+
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_tcl_extract_all ()
+{
+ extract_all = true;
+}
+
+
+void
+x_tcl_keyword (const char *name)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+
+ if (keywords.table == NULL)
+ hash_init (&keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid Tcl
+ function name. A leading "::" is redundant. */
+ if (end - name >= 2 && name[0] == ':' && name[1] == ':')
+ name += 2;
+
+ insert_keyword_callshape (&keywords, name, end - name, &shape);
+ }
+}
+
+/* Finish initializing the keywords hash table.
+ Called after argument processing, before each file is processed. */
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_tcl_keyword ("::msgcat::mc");
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_tcl ()
+{
+ xgettext_record_flag ("::msgcat::mc:1:pass-tcl-format");
+ xgettext_record_flag ("format:1:tcl-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* Fetch the next character from the input file. */
+static int
+do_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("\
+error while reading \"%s\""), real_file_name);
+ }
+ else if (c == '\n')
+ line_number++;
+
+ return c;
+}
+
+/* Put back the last fetched character, not EOF. */
+static void
+do_ungetc (int c)
+{
+ if (c == '\n')
+ line_number--;
+ ungetc (c, fp);
+}
+
+
+/* Combine backslash followed by newline and additional whitespace to
+ a single space. */
+
+/* An int that becomes a space when casted to 'unsigned char'. */
+#define BS_NL (UCHAR_MAX + 1 + ' ')
+
+static int phase1_pushback[1];
+static int phase1_pushback_length;
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ {
+ c = phase1_pushback[--phase1_pushback_length];
+ if (c == '\n' || c == BS_NL)
+ ++line_number;
+ return c;
+ }
+ c = do_getc ();
+ if (c != '\\')
+ return c;
+ c = do_getc ();
+ if (c != '\n')
+ {
+ if (c != EOF)
+ do_ungetc (c);
+ return '\\';
+ }
+ for (;;)
+ {
+ c = do_getc ();
+ if (!(c == ' ' || c == '\t'))
+ break;
+ }
+ if (c != EOF)
+ do_ungetc (c);
+ return BS_NL;
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ switch (c)
+ {
+ case EOF:
+ break;
+
+ case '\n':
+ case BS_NL:
+ --line_number;
+ /* FALLTHROUGH */
+
+ default:
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ break;
+ }
+}
+
+
+/* Keep track of brace nesting depth.
+ When a word starts with an opening brace, a character group begins that
+ ends with the corresponding closing brace. In theory these character
+ groups are string literals, but they are used by so many Tcl primitives
+ (proc, if, ...) as representing command lists, that we treat them as
+ command lists. */
+
+/* An int that becomes a closing brace when casted to 'unsigned char'. */
+#define CL_BRACE (UCHAR_MAX + 1 + '}')
+
+static int phase2_pushback[2];
+static int phase2_pushback_length;
+
+/* Brace nesting depth inside the current character group. */
+static int brace_depth;
+
+static int
+phase2_push ()
+{
+ int previous_depth = brace_depth;
+ brace_depth = 1;
+ return previous_depth;
+}
+
+static void
+phase2_pop (int previous_depth)
+{
+ brace_depth = previous_depth;
+}
+
+static int
+phase2_getc ()
+{
+ int c;
+
+ if (phase2_pushback_length)
+ {
+ c = phase2_pushback[--phase2_pushback_length];
+ if (c == '\n' || c == BS_NL)
+ ++line_number;
+ else if (c == '{')
+ ++brace_depth;
+ else if (c == '}')
+ --brace_depth;
+ return c;
+ }
+ c = phase1_getc ();
+ if (c == '{')
+ ++brace_depth;
+ else if (c == '}')
+ {
+ if (--brace_depth == 0)
+ c = CL_BRACE;
+ }
+ return c;
+}
+
+/* Supports 2 characters of pushback. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ switch (c)
+ {
+ case '\n':
+ case BS_NL:
+ --line_number;
+ break;
+
+ case '{':
+ --brace_depth;
+ break;
+
+ case '}':
+ ++brace_depth;
+ break;
+ }
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+/* A token consists of a sequence of characters. */
+struct token
+{
+ int allocated; /* number of allocated 'token_char's */
+ int charcount; /* number of used 'token_char's */
+ char *chars; /* the token's constituents */
+};
+
+/* Initialize a 'struct token'. */
+static inline void
+init_token (struct token *tp)
+{
+ tp->allocated = 10;
+ tp->chars = XNMALLOC (tp->allocated, char);
+ tp->charcount = 0;
+}
+
+/* Free the memory pointed to by a 'struct token'. */
+static inline void
+free_token (struct token *tp)
+{
+ free (tp->chars);
+}
+
+/* Ensure there is enough room in the token for one more character. */
+static inline void
+grow_token (struct token *tp)
+{
+ if (tp->charcount == tp->allocated)
+ {
+ tp->allocated *= 2;
+ tp->chars = (char *) xrealloc (tp->chars, tp->allocated * sizeof (char));
+ }
+}
+
+
+/* ========================= Accumulating comments ========================= */
+
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end ()
+{
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* ========================= Accumulating messages ========================= */
+
+
+static message_list_ty *mlp;
+
+
+/* ========================== Reading of commands ========================== */
+
+
+/* We are only interested in constant strings (e.g. "msgcat::mc" or other
+ string literals). Other words need not to be represented precisely. */
+enum word_type
+{
+ t_string, /* constant string */
+ t_other, /* other string */
+ t_separator, /* command separator: semicolon or newline */
+ t_bracket, /* ']' pseudo word */
+ t_brace, /* '}' pseudo word */
+ t_eof /* EOF marker */
+};
+
+struct word
+{
+ enum word_type type;
+ struct token *token; /* for t_string */
+ int line_number_at_start; /* for t_string */
+};
+
+/* Free the memory pointed to by a 'struct word'. */
+static inline void
+free_word (struct word *wp)
+{
+ if (wp->type == t_string)
+ {
+ free_token (wp->token);
+ free (wp->token);
+ }
+}
+
+/* Convert a t_string token to a char*. */
+static char *
+string_of_word (const struct word *wp)
+{
+ char *str;
+ int n;
+
+ if (!(wp->type == t_string))
+ abort ();
+ n = wp->token->charcount;
+ str = XNMALLOC (n + 1, char);
+ memcpy (str, wp->token->chars, n);
+ str[n] = '\0';
+ return str;
+}
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* Read an escape sequence. The value is an ISO-8859-1 character (in the
+ range 0x00..0xff) or a Unicode character (in the range 0x0000..0xffff). */
+static int
+do_getc_escaped ()
+{
+ int c;
+
+ c = phase1_getc ();
+ switch (c)
+ {
+ case EOF:
+ return '\\';
+ case 'a':
+ return '\a';
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case 'x':
+ {
+ int n = 0;
+ unsigned int i;
+
+ for (i = 0;; i++)
+ {
+ c = phase1_getc ();
+ if (c == EOF || !c_isxdigit ((unsigned char) c))
+ break;
+
+ if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ }
+ phase1_ungetc (c);
+ return (i > 0 ? (unsigned char) n : 'x');
+ }
+ case 'u':
+ {
+ int n = 0;
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ c = phase1_getc ();
+ if (c == EOF || !c_isxdigit ((unsigned char) c))
+ {
+ phase1_ungetc (c);
+ break;
+ }
+
+ if (c >= '0' && c <= '9')
+ n = (n << 4) + (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ n = (n << 4) + (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ n = (n << 4) + (c - 'a' + 10);
+ }
+ return (i > 0 ? n : 'u');
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ {
+ int n = c - '0';
+
+ c = phase1_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ {
+ n = (n << 3) + (c - '0');
+ c = phase1_getc ();
+ if (c != EOF)
+ {
+ if (c >= '0' && c <= '7')
+ n = (n << 3) + (c - '0');
+ else
+ phase1_ungetc (c);
+ }
+ }
+ else
+ phase1_ungetc (c);
+ }
+ return (unsigned char) n;
+ }
+ default:
+ /* Note: If c is non-ASCII, Tcl's behaviour is undefined here. */
+ return (unsigned char) c;
+ }
+}
+
+
+enum terminator
+{
+ te_space_separator, /* looking for space semicolon newline */
+ te_space_separator_bracket, /* looking for space semicolon newline ']' */
+ te_paren, /* looking for ')' */
+ te_quote /* looking for '"' */
+};
+
+/* Forward declaration of local functions. */
+static enum word_type read_command_list (int looking_for,
+ flag_context_ty outer_context);
+
+/* Accumulate tokens into the given word.
+ 'looking_for' denotes a parse terminator combination.
+ Return the first character past the token. */
+static int
+accumulate_word (struct word *wp, enum terminator looking_for,
+ flag_context_ty context)
+{
+ int c;
+
+ for (;;)
+ {
+ c = phase2_getc ();
+
+ if (c == EOF || c == CL_BRACE)
+ return c;
+ if ((looking_for == te_space_separator
+ || looking_for == te_space_separator_bracket)
+ && (c == ' ' || c == BS_NL
+ || c == '\t' || c == '\v' || c == '\f' || c == '\r'
+ || c == ';' || c == '\n'))
+ return c;
+ if (looking_for == te_space_separator_bracket && c == ']')
+ return c;
+ if (looking_for == te_paren && c == ')')
+ return c;
+ if (looking_for == te_quote && c == '"')
+ return c;
+
+ if (c == '$')
+ {
+ /* Distinguish $varname, ${varname} and lone $. */
+ c = phase2_getc ();
+ if (c == '{')
+ {
+ /* ${varname} */
+ do
+ c = phase2_getc ();
+ while (c != EOF && c != '}');
+ wp->type = t_other;
+ }
+ else
+ {
+ bool nonempty = false;
+
+ for (; c != EOF && c != CL_BRACE; c = phase2_getc ())
+ {
+ if (c_isalnum ((unsigned char) c) || (c == '_'))
+ {
+ nonempty = true;
+ continue;
+ }
+ if (c == ':')
+ {
+ c = phase2_getc ();
+ if (c == ':')
+ {
+ do
+ c = phase2_getc ();
+ while (c == ':');
+
+ phase2_ungetc (c);
+ nonempty = true;
+ continue;
+ }
+ phase2_ungetc (c);
+ c = ':';
+ }
+ break;
+ }
+ if (c == '(')
+ {
+ /* $varname(index) */
+ struct word index_word;
+
+ index_word.type = t_other;
+ c = accumulate_word (&index_word, te_paren, null_context);
+ if (c != EOF && c != ')')
+ phase2_ungetc (c);
+ wp->type = t_other;
+ }
+ else
+ {
+ phase2_ungetc (c);
+ if (nonempty)
+ {
+ /* $varname */
+ wp->type = t_other;
+ }
+ else
+ {
+ /* lone $ */
+ if (wp->type == t_string)
+ {
+ grow_token (wp->token);
+ wp->token->chars[wp->token->charcount++] = '$';
+ }
+ }
+ }
+ }
+ }
+ else if (c == '[')
+ {
+ read_command_list (']', context);
+ wp->type = t_other;
+ }
+ else if (c == '\\')
+ {
+ unsigned int uc;
+ unsigned char utf8buf[6];
+ int count;
+ int i;
+
+ uc = do_getc_escaped ();
+ assert (uc < 0x10000);
+ count = u8_uctomb (utf8buf, uc, 6);
+ assert (count > 0);
+ if (wp->type == t_string)
+ for (i = 0; i < count; i++)
+ {
+ grow_token (wp->token);
+ wp->token->chars[wp->token->charcount++] = utf8buf[i];
+ }
+ }
+ else
+ {
+ if (wp->type == t_string)
+ {
+ grow_token (wp->token);
+ wp->token->chars[wp->token->charcount++] = (unsigned char) c;
+ }
+ }
+ }
+}
+
+
+/* Read the next word.
+ 'looking_for' denotes a parse terminator, either ']' or '\0'. */
+static void
+read_word (struct word *wp, int looking_for, flag_context_ty context)
+{
+ int c;
+
+ do
+ c = phase2_getc ();
+ while (c == ' ' || c == BS_NL
+ || c == '\t' || c == '\v' || c == '\f' || c == '\r');
+
+ if (c == EOF)
+ {
+ wp->type = t_eof;
+ return;
+ }
+
+ if (c == CL_BRACE)
+ {
+ wp->type = t_brace;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ if (c == '\n')
+ {
+ /* Comments assumed to be grouped with a message must immediately
+ precede it, with no non-whitespace token on a line between both. */
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ wp->type = t_separator;
+ return;
+ }
+
+ if (c == ';')
+ {
+ wp->type = t_separator;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ if (looking_for == ']' && c == ']')
+ {
+ wp->type = t_bracket;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ if (c == '{')
+ {
+ int previous_depth;
+ enum word_type terminator;
+
+ /* Start a new nested character group, which lasts until the next
+ balanced '}' (ignoring \} things). */
+ previous_depth = phase2_push () - 1;
+
+ /* Interpret it as a command list. */
+ terminator = read_command_list ('\0', null_context);
+
+ if (terminator == t_brace)
+ phase2_pop (previous_depth);
+
+ wp->type = t_other;
+ last_non_comment_line = line_number;
+ return;
+ }
+
+ wp->type = t_string;
+ wp->token = XMALLOC (struct token);
+ init_token (wp->token);
+ wp->line_number_at_start = line_number;
+
+ if (c == '"')
+ {
+ c = accumulate_word (wp, te_quote, context);
+ if (c != EOF && c != '"')
+ phase2_ungetc (c);
+ }
+ else
+ {
+ phase2_ungetc (c);
+ c = accumulate_word (wp,
+ looking_for == ']'
+ ? te_space_separator_bracket
+ : te_space_separator,
+ context);
+ if (c != EOF)
+ phase2_ungetc (c);
+ }
+
+ if (wp->type != t_string)
+ {
+ free_token (wp->token);
+ free (wp->token);
+ }
+ last_non_comment_line = line_number;
+}
+
+
+/* Read the next command.
+ 'looking_for' denotes a parse terminator, either ']' or '\0'.
+ Returns the type of the word that terminated the command: t_separator or
+ t_bracket (only if looking_for is ']') or t_brace or t_eof. */
+static enum word_type
+read_command (int looking_for, flag_context_ty outer_context)
+{
+ int c;
+
+ /* Skip whitespace and comments. */
+ for (;;)
+ {
+ c = phase2_getc ();
+
+ if (c == ' ' || c == BS_NL
+ || c == '\t' || c == '\v' || c == '\f' || c == '\r')
+ continue;
+ if (c == '#')
+ {
+ /* Skip a comment up to end of line. */
+ last_comment_line = line_number;
+ comment_start ();
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (c == EOF || c == CL_BRACE || c == '\n')
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end ();
+ continue;
+ }
+ break;
+ }
+ phase2_ungetc (c);
+
+ /* Read the words that make up the command. */
+ {
+ int arg = 0; /* Current argument number. */
+ flag_context_list_iterator_ty context_iter;
+ const struct callshapes *shapes = NULL;
+ struct arglist_parser *argparser = NULL;
+
+ for (;; arg++)
+ {
+ struct word inner;
+ flag_context_ty inner_context;
+
+ if (arg == 0)
+ inner_context = null_context;
+ else
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+
+ read_word (&inner, looking_for, inner_context);
+
+ /* Recognize end of command. */
+ if (inner.type == t_separator || inner.type == t_bracket
+ || inner.type == t_brace || inner.type == t_eof)
+ {
+ if (argparser != NULL)
+ arglist_parser_done (argparser, arg);
+ return inner.type;
+ }
+
+ if (extract_all)
+ {
+ if (inner.type == t_string)
+ {
+ lex_pos_ty pos;
+
+ pos.file_name = logical_file_name;
+ pos.line_number = inner.line_number_at_start;
+ remember_a_message (mlp, NULL, string_of_word (&inner),
+ inner_context, &pos,
+ NULL, savable_comment);
+ }
+ }
+
+ if (arg == 0)
+ {
+ /* This is the function position. */
+ if (inner.type == t_string)
+ {
+ char *function_name = string_of_word (&inner);
+ char *stripped_name;
+ void *keyword_value;
+
+ /* A leading "::" is redundant. */
+ stripped_name = function_name;
+ if (function_name[0] == ':' && function_name[1] == ':')
+ stripped_name += 2;
+
+ if (hash_find_entry (&keywords,
+ stripped_name, strlen (stripped_name),
+ &keyword_value)
+ == 0)
+ shapes = (const struct callshapes *) keyword_value;
+
+ argparser = arglist_parser_alloc (mlp, shapes);
+
+ context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ stripped_name, strlen (stripped_name)));
+
+ free (function_name);
+ }
+ else
+ context_iter = null_context_list_iterator;
+ }
+ else
+ {
+ /* These are the argument positions. */
+ if (argparser != NULL && inner.type == t_string)
+ arglist_parser_remember (argparser, arg,
+ string_of_word (&inner),
+ inner_context,
+ logical_file_name,
+ inner.line_number_at_start,
+ savable_comment);
+ }
+
+ free_word (&inner);
+ }
+ }
+}
+
+
+/* Read a list of commands.
+ 'looking_for' denotes a parse terminator, either ']' or '\0'.
+ Returns the type of the word that terminated the command list:
+ t_bracket (only if looking_for is ']') or t_brace or t_eof. */
+static enum word_type
+read_command_list (int looking_for, flag_context_ty outer_context)
+{
+ for (;;)
+ {
+ enum word_type terminator;
+
+ terminator = read_command (looking_for, outer_context);
+ if (terminator != t_separator)
+ return terminator;
+ }
+}
+
+
+void
+extract_tcl (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ mlp = mdlp->item[0]->messages;
+
+ /* We convert our strings to UTF-8 encoding. */
+ xgettext_current_source_encoding = po_charset_utf8;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ /* Initially, no brace is open. */
+ brace_depth = 1000000;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. */
+ read_command_list ('\0', null_context);
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-tcl.h b/gettext-tools/src/x-tcl.h
new file mode 100644
index 0000000..1dc5412
--- /dev/null
+++ b/gettext-tools/src/x-tcl.h
@@ -0,0 +1,54 @@
+/* xgettext Tcl Lisp backend.
+ Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2002.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_TCL \
+ { "tcl", "Tcl" }, \
+
+#define SCANNERS_TCL \
+ { "Tcl", extract_tcl, \
+ &flag_table_tcl, &formatstring_tcl, NULL, NULL }, \
+
+/* Scan a Tcl file and add its translatable strings to mdlp. */
+extern void extract_tcl (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+
+/* Handling of options specific to this language. */
+
+extern void x_tcl_extract_all (void);
+extern void x_tcl_keyword (const char *name);
+
+extern void init_flag_table_tcl (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-vala.c b/gettext-tools/src/x-vala.c
new file mode 100644
index 0000000..4806b36
--- /dev/null
+++ b/gettext-tools/src/x-vala.c
@@ -0,0 +1,1250 @@
+/* xgettext Vala backend.
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file was written by Daiki Ueno <ueno@gnu.org>, 2013.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-vala.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "error-progname.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "hash.h"
+#include "po-charset.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* The Vala syntax is defined in the Vala Reference Manual
+ http://www.vala-project.org/doc/vala/.
+ See also vala/valascanner.vala. */
+
+/* ====================== Keyword set customization. ====================== */
+
+/* If true extract all strings. */
+static bool extract_all = false;
+
+static hash_table keywords;
+static bool default_keywords = true;
+
+
+void
+x_vala_extract_all ()
+{
+ extract_all = true;
+}
+
+
+static void
+add_keyword (const char *name, hash_table *keywords)
+{
+ if (name == NULL)
+ default_keywords = false;
+ else
+ {
+ const char *end;
+ struct callshape shape;
+ const char *colon;
+
+ if (keywords->table == NULL)
+ hash_init (keywords, 100);
+
+ split_keywordspec (name, &end, &shape);
+
+ /* The characters between name and end should form a valid C identifier.
+ A colon means an invalid parse in split_keywordspec(). */
+ colon = strchr (name, ':');
+ if (colon == NULL || colon >= end)
+ insert_keyword_callshape (keywords, name, end - name, &shape);
+ }
+}
+
+void
+x_vala_keyword (const char *name)
+{
+ add_keyword (name, &keywords);
+}
+
+static void
+init_keywords ()
+{
+ if (default_keywords)
+ {
+ /* When adding new keywords here, also update the documentation in
+ xgettext.texi! */
+ x_vala_keyword ("dgettext:2");
+ x_vala_keyword ("dcgettext:2");
+ x_vala_keyword ("ngettext:1,2");
+ x_vala_keyword ("dngettext:2,3");
+ x_vala_keyword ("dpgettext:2g");
+ x_vala_keyword ("dpgettext2:2c,3");
+ x_vala_keyword ("_");
+ x_vala_keyword ("Q_");
+ x_vala_keyword ("N_");
+ x_vala_keyword ("NC_:1c,2");
+
+ default_keywords = false;
+ }
+}
+
+void
+init_flag_table_vala ()
+{
+ xgettext_record_flag ("dgettext:2:pass-c-format");
+ xgettext_record_flag ("dcgettext:2:pass-c-format");
+ xgettext_record_flag ("ngettext:1:pass-c-format");
+ xgettext_record_flag ("ngettext:2:pass-c-format");
+ xgettext_record_flag ("dngettext:2:pass-c-format");
+ xgettext_record_flag ("dngettext:3:pass-c-format");
+ xgettext_record_flag ("dpgettext:2:pass-c-format");
+ xgettext_record_flag ("dpgettext2:3:pass-c-format");
+ xgettext_record_flag ("_:1:pass-c-format");
+ xgettext_record_flag ("Q_:1:pass-c-format");
+ xgettext_record_flag ("N_:1:pass-c-format");
+ xgettext_record_flag ("NC_:2:pass-c-format");
+
+ /* Vala leaves string formatting to Glib functions and thus the
+ format string is exactly same as C. See also
+ vapi/glib-2.0.vapi. */
+ xgettext_record_flag ("printf:1:c-format");
+ xgettext_record_flag ("vprintf:1:c-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+
+/* The input file stream. */
+static FILE *fp;
+
+
+/* 1. line_number handling. */
+
+#define MAX_PHASE1_PUSHBACK 16
+static unsigned char phase1_pushback[MAX_PHASE1_PUSHBACK];
+static int phase1_pushback_length;
+
+
+static int
+phase1_getc ()
+{
+ int c;
+
+ if (phase1_pushback_length)
+ c = phase1_pushback[--phase1_pushback_length];
+ else
+ {
+ c = getc (fp);
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+ }
+
+ if (c == '\n')
+ ++line_number;
+ return c;
+}
+
+
+/* Supports 2 characters of pushback. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ --line_number;
+
+ if (phase1_pushback_length == SIZEOF (phase1_pushback))
+ abort ();
+ phase1_pushback[phase1_pushback_length++] = c;
+ }
+}
+
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+/* Accumulating comments. */
+
+static char *buffer;
+static size_t bufmax;
+static size_t buflen;
+
+static inline void
+comment_start ()
+{
+ buflen = 0;
+}
+
+static inline void
+comment_add (int c)
+{
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+}
+
+static inline void
+comment_line_end (size_t chars_to_remove)
+{
+ buflen -= chars_to_remove;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
+ --buflen;
+ if (chars_to_remove == 0 && buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+}
+
+
+/* 2. Replace each comment that is not inside a character constant or
+ string literal with a space character. */
+
+static int
+phase2_getc ()
+{
+ int c;
+ bool last_was_star;
+
+ c = phase1_getc ();
+ if (c != '/')
+ return c;
+ c = phase1_getc ();
+ switch (c)
+ {
+ default:
+ phase1_ungetc (c);
+ return '/';
+
+ case '*':
+ /* C comment. */
+ comment_start ();
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ switch (c)
+ {
+ case '\n':
+ comment_line_end (1);
+ comment_start ();
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ comment_line_end (2);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = line_number;
+ return ' ';
+
+ case '/':
+ /* C++ or ISO C 99 comment. */
+ comment_start ();
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ comment_add (c);
+ }
+ comment_line_end (0);
+ last_comment_line = line_number;
+ return '\n';
+ }
+}
+
+
+static void
+phase2_ungetc (int c)
+{
+ phase1_ungetc (c);
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+enum token_type_ty
+{
+ token_type_character_constant, /* 'x' */
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_lbrace, /* { */
+ token_type_rbrace, /* } */
+ token_type_assign, /* = += -= *= /= %= <<= >>= &= |= ^= */
+ token_type_return, /* return */
+ token_type_plus, /* + */
+ token_type_arithmetic_operator, /* - * / % << >> & | ^ */
+ token_type_equality_test_operator, /* == < > >= <= != */
+ token_type_logic_operator, /* ! && || */
+ token_type_comma, /* , */
+ token_type_question, /* ? */
+ token_type_colon, /* : */
+ token_type_number, /* 2.7 */
+ token_type_string_literal, /* "abc" */
+ token_type_string_template, /* @"abc" */
+ token_type_regex_literal, /* /.../ */
+ token_type_symbol, /* if else etc. */
+ token_type_other
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_symbol, token_type_string_literal */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ enum literalstring_escape_type escape;
+ int line_number;
+};
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string_literal)
+ drop_reference (tp->comment);
+}
+
+
+/* 3. Parse each resulting logical line as preprocessing tokens and
+ white space. Preprocessing tokens and Vala tokens don't always
+ match. */
+
+static token_ty phase3_pushback[2];
+static int phase3_pushback_length;
+
+
+static token_type_ty last_token_type = token_type_other;
+
+static void
+phase3_scan_regex ()
+{
+ int c;
+
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '/')
+ break;
+ if (c == '\\')
+ {
+ c = phase1_getc ();
+ if (c != EOF)
+ continue;
+ }
+ if (c == EOF)
+ {
+ error_with_progname = false;
+ error (0, 0,
+ _("%s:%d: warning: regular expression literal terminated too early"),
+ logical_file_name, line_number);
+ error_with_progname = true;
+ return;
+ }
+ }
+
+ c = phase2_getc ();
+ if (!(c == 'i' || c == 's' || c == 'm' || c == 'x'))
+ phase2_ungetc (c);
+}
+
+static void
+phase3_get (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int last_was_backslash;
+
+#undef APPEND
+#define APPEND(c) \
+ do \
+ { \
+ if (bufpos >= bufmax) \
+ { \
+ bufmax = 2 * bufmax + 10; \
+ buffer = xrealloc (buffer, bufmax); \
+ } \
+ buffer[bufpos++] = c; \
+ } \
+ while (0)
+
+ if (phase3_pushback_length)
+ {
+ *tp = phase3_pushback[--phase3_pushback_length];
+ last_token_type = tp->type;
+ return;
+ }
+
+ for (;;)
+ {
+ bool template;
+ bool verbatim;
+ int c;
+
+ tp->line_number = line_number;
+ c = phase2_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = last_token_type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case ' ':
+ case '\f':
+ case '\t':
+ /* Ignore whitespace and comments. */
+ continue;
+ default:
+ break;
+ }
+
+ last_non_comment_line = tp->line_number;
+ template = false;
+ verbatim = false;
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ bufpos = 0;
+ for (;;)
+ {
+ APPEND (c);
+ c = phase2_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+
+ default:
+ phase2_ungetc (c);
+ break;
+ }
+ break;
+ }
+ APPEND (0);
+ if (strcmp (buffer, "return") == 0)
+ tp->type = last_token_type = token_type_return;
+ else
+ {
+ tp->string = xstrdup (buffer);
+ tp->type = last_token_type = token_type_symbol;
+ }
+ return;
+
+ case '.':
+ c = phase2_getc ();
+ phase2_ungetc (c);
+ switch (c)
+ {
+ default:
+ tp->string = xstrdup (".");
+ tp->type = last_token_type = token_type_symbol;
+ return;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ c = '.';
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* The preprocessing number token is more "generous" than the C
+ number tokens. This is mostly due to token pasting (another
+ thing we can ignore here). */
+ bufpos = 0;
+ for (;;)
+ {
+ APPEND (c);
+ c = phase2_getc ();
+ switch (c)
+ {
+ case 'e':
+ case 'E':
+ APPEND (c);
+ c = phase2_getc ();
+ if (c != '+' && c != '-')
+ {
+ phase2_ungetc (c);
+ break;
+ }
+ continue;
+
+ case 'A': case 'B': case 'C': case 'D': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.':
+ continue;
+
+ default:
+ phase2_ungetc (c);
+ break;
+ }
+ break;
+ }
+ APPEND (0);
+ tp->type = last_token_type = token_type_number;
+ return;
+
+ case '\'':
+ last_was_backslash = false;
+ for (;;)
+ {
+ c = phase2_getc ();
+ if (last_was_backslash)
+ {
+ last_was_backslash = false;
+ continue;
+ }
+ switch (c)
+ {
+ case '\\':
+ last_was_backslash = true;
+ /* FALLTHROUGH */
+ default:
+ continue;
+ case '\n':
+ error_with_progname = false;
+ error (0, 0, _("%s:%d: warning: unterminated character constant"),
+ logical_file_name, line_number - 1);
+ error_with_progname = true;
+ phase2_ungetc ('\n');
+ break;
+ case EOF: case '\'':
+ break;
+ }
+ break;
+ }
+ tp->type = last_token_type = token_type_character_constant;
+ return;
+
+ /* Vala provides strings in three different formats.
+
+ Usual string literals:
+ "..."
+ Verbatim string literals:
+ """...""" (where ... can include newlines and double quotes)
+ String templates.
+ @"...", @"""..."""
+
+ Note that, with the current implementation string
+ templates are not subject to translation, because they are
+ inspected at compile time. For example, the following code
+
+ string bar = "bar";
+ string foo = _(@"foo $bar");
+
+ will be translated into the C code, like:
+
+ _(g_strconcat ("foo ", "bar", NULL)); */
+ case '@':
+ c = phase2_getc ();
+ if (c != '"')
+ {
+ phase2_ungetc (c);
+ tp->type = last_token_type = token_type_other;
+ return;
+ }
+ template = true;
+ /* FALLTHROUGH */
+ case '"':
+ {
+ int c2 = phase2_getc ();
+
+ if (c2 == '"')
+ {
+ int c3 = phase2_getc ();
+ if (c3 == '"')
+ verbatim = true;
+ else
+ {
+ phase2_ungetc (c3);
+ phase2_ungetc (c2);
+ }
+ }
+ else
+ phase2_ungetc (c2);
+
+ if (verbatim)
+ {
+ bufpos = 0;
+ for (;;)
+ {
+ /* Use phase 1, because phase 2 elides comments. */
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+
+ if (c == '"')
+ {
+ int c2 = phase1_getc ();
+ if (c2 == '"')
+ {
+ int c3 = phase1_getc ();
+ if (c3 == '"')
+ break;
+ phase1_ungetc (c3);
+ }
+ phase1_ungetc (c2);
+ }
+ APPEND (c);
+ }
+ }
+ else
+ {
+ last_was_backslash = false;
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (last_was_backslash)
+ {
+ last_was_backslash = false;
+ APPEND (c);
+ continue;
+ }
+
+ switch (c)
+ {
+ case '\\':
+ last_was_backslash = true;
+ /* FALLTHROUGH */
+ default:
+ APPEND (c);
+ continue;
+
+ case '\n':
+ error_with_progname = false;
+ error (0, 0, _("\
+%s:%d: warning: unterminated string literal"),
+ logical_file_name, line_number - 1);
+ error_with_progname = true;
+ phase1_ungetc ('\n');
+ break;
+ case EOF: case '"':
+ break;
+ }
+ break;
+ }
+ }
+ APPEND (0);
+ tp->type = last_token_type = template
+ ? token_type_string_template : token_type_string_literal;
+ tp->string = xstrdup (buffer);
+ tp->comment = add_reference (savable_comment);
+ tp->escape = verbatim ? 0 : LET_ANSI_C | LET_UNICODE;
+ return;
+ }
+
+ case '/':
+ switch (last_token_type)
+ {
+ case token_type_lparen:
+ case token_type_lbrace:
+ case token_type_assign:
+ case token_type_return:
+ case token_type_plus:
+ case token_type_arithmetic_operator:
+ case token_type_equality_test_operator:
+ case token_type_logic_operator:
+ case token_type_comma:
+ case token_type_question:
+ case token_type_colon:
+ phase3_scan_regex ();
+ tp->type = last_token_type = token_type_regex_literal;
+ break;
+ default:
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '=')
+ tp->type = last_token_type = token_type_assign;
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_arithmetic_operator;
+ }
+ break;
+ }
+ }
+ return;
+
+ case '(':
+ tp->type = last_token_type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = last_token_type = token_type_rparen;
+ return;
+
+ case '{':
+ tp->type = last_token_type = token_type_lbrace;
+ return;
+
+ case '}':
+ tp->type = last_token_type = token_type_rbrace;
+ return;
+
+ case '+':
+ {
+ int c2 = phase2_getc ();
+ switch (c2)
+ {
+ case '+':
+ tp->type = last_token_type = token_type_other;
+ break;
+ case '=':
+ tp->type = last_token_type = token_type_assign;
+ break;
+ default:
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_plus;
+ break;
+ }
+ return;
+ }
+
+ case '-':
+ {
+ int c2 = phase2_getc ();
+ switch (c2)
+ {
+ case '-':
+ tp->type = last_token_type = token_type_other;
+ break;
+ case '=':
+ tp->type = last_token_type = token_type_assign;
+ break;
+ default:
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_arithmetic_operator;
+ break;
+ }
+ return;
+ }
+
+ case '%':
+ case '^':
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '=')
+ tp->type = last_token_type = token_type_assign;
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_logic_operator;
+ }
+ return;
+ }
+
+ case '=':
+ {
+ int c2 = phase2_getc ();
+ switch (c2)
+ {
+ case '=':
+ tp->type = last_token_type = token_type_equality_test_operator;
+ break;
+ case '>':
+ tp->type = last_token_type = token_type_other;
+ break;
+ default:
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_assign;
+ break;
+ }
+ return;
+ }
+
+ case '!':
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '=')
+ tp->type = last_token_type = token_type_equality_test_operator;
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_logic_operator;
+ }
+ return;
+ }
+
+ case '>':
+ case '<':
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '=')
+ tp->type = last_token_type = token_type_equality_test_operator;
+ else if (c2 == c)
+ {
+ int c3 = phase2_getc ();
+ if (c3 == '=')
+ tp->type = last_token_type = token_type_assign;
+ else
+ {
+ phase2_ungetc (c2);
+ phase2_ungetc (c3);
+ tp->type = last_token_type = token_type_other;
+ }
+ }
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_equality_test_operator;
+ }
+ return;
+ }
+
+ case ',':
+ tp->type = last_token_type = token_type_comma;
+ return;
+
+ case ':':
+ tp->type = last_token_type = token_type_colon;
+ return;
+
+ case '&':
+ case '|':
+ {
+ int c2 = phase2_getc ();
+ if (c2 == c)
+ tp->type = last_token_type = token_type_logic_operator;
+ else if (c2 == '=')
+ tp->type = last_token_type = token_type_assign;
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_arithmetic_operator;
+ }
+ return;
+ }
+
+ case '?':
+ {
+ int c2 = phase2_getc ();
+ if (c2 == '?')
+ tp->type = last_token_type = token_type_logic_operator;
+ else
+ {
+ phase2_ungetc (c2);
+ tp->type = last_token_type = token_type_question;
+ }
+ return;
+ }
+
+ default:
+ tp->type = last_token_type = token_type_other;
+ return;
+ }
+ }
+#undef APPEND
+}
+
+static void
+phase3_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase3_pushback_length == SIZEOF (phase3_pushback))
+ abort ();
+ phase3_pushback[phase3_pushback_length++] = *tp;
+ }
+}
+
+
+/* String concatenation with '+'. */
+
+static void
+x_vala_lex (token_ty *tp)
+{
+ phase3_get (tp);
+ if (tp->type == token_type_string_literal)
+ {
+ char *sum = tp->string;
+ size_t sum_len = strlen (sum);
+
+ for (;;)
+ {
+ token_ty token2;
+
+ phase3_get (&token2);
+ if (token2.type == token_type_plus)
+ {
+ token_ty token3;
+
+ phase3_get (&token3);
+ if (token3.type == token_type_string_literal)
+ {
+ char *addend = token3.string;
+ size_t addend_len = strlen (addend);
+
+ sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
+ memcpy (sum + sum_len, addend, addend_len + 1);
+ sum_len += addend_len;
+
+ free_token (&token3);
+ free_token (&token2);
+ continue;
+ }
+ phase3_unget (&token3);
+ }
+ phase3_unget (&token2);
+ break;
+ }
+ tp->string = sum;
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+/* Use the same literalstring_parser provided by the C scanner. */
+extern struct literalstring_parser literalstring_c;
+
+/* The file is broken into tokens. Scan the token stream, looking for
+ a keyword, followed by a left paren, followed by a string. When we
+ see this sequence, we have something to remember. We assume we are
+ looking at a valid Vala program, and leave the complaints about the
+ grammar to the compiler.
+
+ Normal handling: Look for
+ keyword ( ... msgid ... )
+ keyword msgid
+ Plural handling: Look for
+ keyword ( ... msgid ... msgid_plural ... )
+
+ We use recursion because the arguments before msgid or between msgid
+ and msgid_plural can contain subexpressions of the same form. */
+
+/* Extract messages until the next balanced closing parenthesis or bracket.
+ Extracted messages are added to MLP.
+ DELIM can be either token_type_rparen or token_type_rbracket, or
+ token_type_eof to accept both.
+ Return true upon eof, false upon closing parenthesis or bracket. */
+static bool
+extract_balanced (message_list_ty *mlp, token_type_ty delim,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ struct arglist_parser *argparser)
+{
+ /* Current argument number. */
+ int arg = 1;
+ /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
+ int state;
+ /* Parameters of the keyword just seen. Defined only in state 1. */
+ const struct callshapes *next_shapes = NULL;
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0. */
+ state = 0;
+
+ for (;;)
+ {
+ token_ty token;
+
+ x_vala_lex (&token);
+
+ switch (token.type)
+ {
+ case token_type_symbol:
+ {
+ void *keyword_value;
+
+ if (hash_find_entry (&keywords, token.string, strlen (token.string),
+ &keyword_value)
+ == 0)
+ {
+ next_shapes = (const struct callshapes *) keyword_value;
+ state = 1;
+ }
+ else
+ state = 0;
+ }
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free (token.string);
+ continue;
+
+ case token_type_lparen:
+ if (extract_balanced (mlp, token_type_rparen,
+ inner_context, next_context_iter,
+ arglist_parser_alloc (mlp,
+ state ? next_shapes : NULL)))
+ {
+ arglist_parser_done (argparser, arg);
+ return true;
+ }
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ break;
+
+ case token_type_rparen:
+ if (delim == token_type_rparen || delim == token_type_eof)
+ {
+ arglist_parser_done (argparser, arg);
+ return false;
+ }
+
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_comma:
+ arg++;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ arglist_parser_done (argparser, arg);
+ return true;
+
+ case token_type_string_literal:
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ if (extract_all)
+ {
+ char *string;
+ refcounted_string_list_ty *comment;
+ const char *encoding;
+
+ string = literalstring_c.parse (token.string, &pos,
+ token.escape);
+ free (token.string);
+ token.string = string;
+
+ if (token.comment != NULL)
+ {
+ comment = savable_comment_convert_encoding (token.comment,
+ &pos);
+ drop_reference (token.comment);
+ token.comment = comment;
+ }
+
+ /* token.string and token.comment are already converted
+ to UTF-8. Prevent further conversion in
+ remember_a_message. */
+ encoding = xgettext_current_source_encoding;
+ xgettext_current_source_encoding = po_charset_utf8;
+ remember_a_message (mlp, NULL, token.string, inner_context,
+ &pos, NULL, token.comment);
+ xgettext_current_source_encoding = encoding;
+ }
+ else
+ {
+ /* A string immediately after a symbol means a
+ function call. */
+ if (state)
+ {
+ struct arglist_parser *tmp_argparser;
+ tmp_argparser = arglist_parser_alloc (mlp, next_shapes);
+
+ arglist_parser_remember_literal (tmp_argparser, 1,
+ token.string,
+ inner_context,
+ pos.file_name,
+ pos.line_number,
+ token.comment,
+ token.escape);
+ arglist_parser_done (tmp_argparser, 1);
+ }
+ else
+ arglist_parser_remember_literal (argparser, arg, token.string,
+ inner_context, pos.file_name,
+ pos.line_number,
+ token.comment,
+ token.escape);
+ }
+ }
+ drop_reference (token.comment);
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_character_constant:
+ case token_type_lbrace:
+ case token_type_rbrace:
+ case token_type_assign:
+ case token_type_return:
+ case token_type_plus:
+ case token_type_arithmetic_operator:
+ case token_type_equality_test_operator:
+ case token_type_logic_operator:
+ case token_type_question:
+ case token_type_colon:
+ case token_type_number:
+ case token_type_string_template:
+ case token_type_regex_literal:
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+void
+extract_vala (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ init_keywords ();
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_balanced (mlp, token_type_eof,
+ null_context, null_context_list_iterator,
+ arglist_parser_alloc (mlp, NULL)))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+}
diff --git a/gettext-tools/src/x-vala.h b/gettext-tools/src/x-vala.h
new file mode 100644
index 0000000..9eb730b
--- /dev/null
+++ b/gettext-tools/src/x-vala.h
@@ -0,0 +1,50 @@
+/* xgettext Vala backend.
+ Copyright (C) 2002-2003, 2006, 2013 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_VALA \
+ { "vala", "Vala" }, \
+
+#define SCANNERS_VALA \
+ { "Vala", extract_vala, \
+ &flag_table_vala, &formatstring_c, NULL, &literalstring_c }, \
+
+/* Scan a Vala file and add its translatable strings to mdlp. */
+extern void extract_vala (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void x_vala_keyword (const char *keyword);
+extern void x_vala_extract_all (void);
+
+extern void init_flag_table_vala (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/x-ycp.c b/gettext-tools/src/x-ycp.c
new file mode 100644
index 0000000..ff8642b
--- /dev/null
+++ b/gettext-tools/src/x-ycp.c
@@ -0,0 +1,788 @@
+/* xgettext YCP backend.
+ Copyright (C) 2001-2003, 2005-2009, 2011 Free Software Foundation, Inc.
+
+ This file was written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "x-ycp.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "message.h"
+#include "xgettext.h"
+#include "error.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(s) gettext(s)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* The YCP syntax is defined in libycp/doc/syntax.html.
+ See also libycp/src/scanner.ll.
+ Both are part of the yast2-core package in SuSE Linux distributions. */
+
+
+void
+init_flag_table_ycp ()
+{
+ xgettext_record_flag ("sformat:1:ycp-format");
+ xgettext_record_flag ("y2debug:1:ycp-format");
+ xgettext_record_flag ("y2milestone:1:ycp-format");
+ xgettext_record_flag ("y2warning:1:ycp-format");
+ xgettext_record_flag ("y2error:1:ycp-format");
+ xgettext_record_flag ("y2security:1:ycp-format");
+ xgettext_record_flag ("y2internal:1:ycp-format");
+}
+
+
+/* ======================== Reading of characters. ======================== */
+
+
+/* Real filename, used in error messages about the input file. */
+static const char *real_file_name;
+
+/* Logical filename and line number, used to label the extracted messages. */
+static char *logical_file_name;
+static int line_number;
+static int char_in_line;
+
+/* The input file stream. */
+static FILE *fp;
+
+/* These are for tracking whether comments count as immediately before
+ keyword. */
+static int last_comment_line;
+static int last_non_comment_line;
+
+
+/* 1. line_number handling. */
+
+static int
+phase1_getc ()
+{
+ int c = getc (fp);
+
+ if (c == EOF)
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+ real_file_name);
+ return EOF;
+ }
+
+ if (c == '\n')
+ {
+ line_number++;
+ char_in_line = 0;
+ }
+ else
+ char_in_line++;
+
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase1_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (c == '\n')
+ {
+ --line_number;
+ char_in_line = INT_MAX;
+ }
+ else
+ --char_in_line;
+
+ ungetc (c, fp);
+ }
+}
+
+
+/* 2. Replace each comment that is not inside a character constant or
+ string literal with a space character. We need to remember the
+ comment for later, because it may be attached to a keyword string.
+ YCP comments can be in C comment syntax, C++ comment syntax or sh
+ comment syntax. */
+
+static unsigned char phase2_pushback[1];
+static int phase2_pushback_length;
+
+static int
+phase2_getc ()
+{
+ static char *buffer;
+ static size_t bufmax;
+ size_t buflen;
+ int lineno;
+ int c;
+ bool last_was_star;
+
+ if (phase2_pushback_length)
+ return phase2_pushback[--phase2_pushback_length];
+
+ if (char_in_line == 0)
+ {
+ /* Eat whitespace, to recognize ^[\t ]*# pattern. */
+ do
+ c = phase1_getc ();
+ while (c == '\t' || c == ' ');
+
+ if (c == '#')
+ {
+ /* sh comment. */
+ buflen = 0;
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ {
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ last_comment_line = lineno;
+ return '\n';
+ }
+ }
+ else
+ c = phase1_getc ();
+
+ if (c == '/')
+ {
+ c = phase1_getc ();
+
+ switch (c)
+ {
+ default:
+ phase1_ungetc (c);
+ return '/';
+
+ case '*':
+ /* C comment. */
+ buflen = 0;
+ lineno = line_number;
+ last_was_star = false;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (buflen == 0 && (c == ' ' || c == '\t'))
+ continue;
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+ switch (c)
+ {
+ case '\n':
+ --buflen;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' '
+ || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ buflen = 0;
+ lineno = line_number;
+ last_was_star = false;
+ continue;
+
+ case '*':
+ last_was_star = true;
+ continue;
+
+ case '/':
+ if (last_was_star)
+ {
+ buflen -= 2;
+ while (buflen >= 1
+ && (buffer[buflen - 1] == ' '
+ || buffer[buflen - 1] == '\t'))
+ --buflen;
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ last_was_star = false;
+ continue;
+ }
+ break;
+ }
+ last_comment_line = lineno;
+ return ' ';
+
+ case '/':
+ /* C++ comment. */
+ buflen = 0;
+ lineno = line_number;
+ for (;;)
+ {
+ c = phase1_getc ();
+ if (c == '\n' || c == EOF)
+ break;
+ /* We skip all leading white space, but not EOLs. */
+ if (!(buflen == 0 && (c == ' ' || c == '\t')))
+ {
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen++] = c;
+ }
+ }
+ if (buflen >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[buflen] = '\0';
+ savable_comment_add (buffer);
+ last_comment_line = lineno;
+ return '\n';
+ }
+ }
+ else
+ return c;
+}
+
+/* Supports only one pushback character. */
+static void
+phase2_ungetc (int c)
+{
+ if (c != EOF)
+ {
+ if (phase2_pushback_length == SIZEOF (phase2_pushback))
+ abort ();
+ phase2_pushback[phase2_pushback_length++] = c;
+ }
+}
+
+
+/* ========================== Reading of tokens. ========================== */
+
+
+enum token_type_ty
+{
+ token_type_eof,
+ token_type_lparen, /* ( */
+ token_type_rparen, /* ) */
+ token_type_comma, /* , */
+ token_type_i18n, /* _( */
+ token_type_string_literal, /* "abc" */
+ token_type_symbol, /* symbol, number */
+ token_type_other /* misc. operator */
+};
+typedef enum token_type_ty token_type_ty;
+
+typedef struct token_ty token_ty;
+struct token_ty
+{
+ token_type_ty type;
+ char *string; /* for token_type_string_literal, token_type_symbol */
+ refcounted_string_list_ty *comment; /* for token_type_string_literal */
+ int line_number;
+};
+
+
+/* 7. Replace escape sequences within character strings with their
+ single character equivalents. */
+
+#define P7_QUOTES (1000 + '"')
+
+static int
+phase7_getc ()
+{
+ int c;
+
+ for (;;)
+ {
+ /* Use phase 1, because phase 2 elides comments. */
+ c = phase1_getc ();
+
+ if (c == '"')
+ return P7_QUOTES;
+ if (c != '\\')
+ return c;
+ c = phase1_getc ();
+ if (c != '\n')
+ switch (c)
+ {
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+
+ /* FIXME: What is the octal escape syntax?
+ syntax.html says: [0] [0-7]+
+ scanner.ll says: [0-7] [0-7] [0-7]
+ */
+#if 0
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ int n, j;
+
+ n = 0;
+ for (j = 0; j < 3; ++j)
+ {
+ n = n * 8 + c - '0';
+ c = phase1_getc ();
+ switch (c)
+ {
+ default:
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ continue;
+ }
+ break;
+ }
+ phase1_ungetc (c);
+ return n;
+ }
+#endif
+
+ default:
+ return c;
+ }
+ }
+}
+
+
+/* Free the memory pointed to by a 'struct token_ty'. */
+static inline void
+free_token (token_ty *tp)
+{
+ if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
+ free (tp->string);
+ if (tp->type == token_type_string_literal)
+ drop_reference (tp->comment);
+}
+
+
+/* Combine characters into tokens. Discard whitespace. */
+
+static token_ty phase5_pushback[1];
+static int phase5_pushback_length;
+
+static void
+phase5_get (token_ty *tp)
+{
+ static char *buffer;
+ static int bufmax;
+ int bufpos;
+ int c;
+
+ if (phase5_pushback_length)
+ {
+ *tp = phase5_pushback[--phase5_pushback_length];
+ return;
+ }
+ for (;;)
+ {
+ tp->line_number = line_number;
+ c = phase2_getc ();
+
+ switch (c)
+ {
+ case EOF:
+ tp->type = token_type_eof;
+ return;
+
+ case '\n':
+ if (last_non_comment_line > last_comment_line)
+ savable_comment_reset ();
+ /* FALLTHROUGH */
+ case '\r':
+ case '\t':
+ case ' ':
+ /* Ignore whitespace and comments. */
+ continue;
+ }
+
+ last_non_comment_line = tp->line_number;
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* Symbol, or part of a number. */
+ bufpos = 0;
+ for (;;)
+ {
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ c = phase2_getc ();
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ continue;
+ default:
+ if (bufpos == 1 && buffer[0] == '_' && c == '(')
+ {
+ tp->type = token_type_i18n;
+ return;
+ }
+ phase2_ungetc (c);
+ break;
+ }
+ break;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_symbol;
+ return;
+
+ case '"':
+ bufpos = 0;
+ for (;;)
+ {
+ c = phase7_getc ();
+ if (c == EOF || c == P7_QUOTES)
+ break;
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos++] = c;
+ }
+ if (bufpos >= bufmax)
+ {
+ bufmax = 2 * bufmax + 10;
+ buffer = xrealloc (buffer, bufmax);
+ }
+ buffer[bufpos] = '\0';
+ tp->string = xstrdup (buffer);
+ tp->type = token_type_string_literal;
+ tp->comment = add_reference (savable_comment);
+ return;
+
+ case '(':
+ tp->type = token_type_lparen;
+ return;
+
+ case ')':
+ tp->type = token_type_rparen;
+ return;
+
+ case ',':
+ tp->type = token_type_comma;
+ return;
+
+ default:
+ /* We could carefully recognize each of the 2 and 3 character
+ operators, but it is not necessary, as we only need to recognize
+ gettext invocations. Don't bother. */
+ tp->type = token_type_other;
+ return;
+ }
+ }
+}
+
+/* Supports only one pushback token. */
+static void
+phase5_unget (token_ty *tp)
+{
+ if (tp->type != token_type_eof)
+ {
+ if (phase5_pushback_length == SIZEOF (phase5_pushback))
+ abort ();
+ phase5_pushback[phase5_pushback_length++] = *tp;
+ }
+}
+
+
+/* Concatenate adjacent string literals to form single string literals.
+ (See libycp/src/parser.yy, rule 'string' vs. terminal 'STRING'.) */
+
+static void
+phase8_get (token_ty *tp)
+{
+ phase5_get (tp);
+ if (tp->type != token_type_string_literal)
+ return;
+ for (;;)
+ {
+ token_ty tmp;
+ size_t len;
+
+ phase5_get (&tmp);
+ if (tmp.type != token_type_string_literal)
+ {
+ phase5_unget (&tmp);
+ return;
+ }
+ len = strlen (tp->string);
+ tp->string = xrealloc (tp->string, len + strlen (tmp.string) + 1);
+ strcpy (tp->string + len, tmp.string);
+ free_token (&tmp);
+ }
+}
+
+
+/* ========================= Extracting strings. ========================== */
+
+
+/* Context lookup table. */
+static flag_context_list_table_ty *flag_context_list_table;
+
+
+/* The file is broken into tokens.
+
+ Normal handling: Look for
+ [A] _( [B] msgid ... )
+ Plural handling: Look for
+ [A] _( [B] msgid [C] , [D] msgid_plural ... )
+ At point [A]: state == 0.
+ At point [B]: state == 1, plural_mp == NULL.
+ At point [C]: state == 2, plural_mp != NULL.
+ At point [D]: state == 1, plural_mp != NULL.
+
+ We use recursion because we have to set the context according to the given
+ flags. */
+
+
+/* Extract messages until the next balanced closing parenthesis.
+ Extracted messages are added to MLP.
+ Return true upon eof, false upon closing parenthesis. */
+static bool
+extract_parenthesized (message_list_ty *mlp,
+ flag_context_ty outer_context,
+ flag_context_list_iterator_ty context_iter,
+ bool in_i18n)
+{
+ int state; /* 1 or 2 inside _( ... ), otherwise 0 */
+ int plural_state = 0; /* defined only when in states 1 and 2 */
+ message_ty *plural_mp = NULL; /* defined only when in states 1 and 2 */
+ /* Context iterator that will be used if the next token is a '('. */
+ flag_context_list_iterator_ty next_context_iter =
+ passthrough_context_list_iterator;
+ /* Current context. */
+ flag_context_ty inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (&context_iter));
+
+ /* Start state is 0 or 1. */
+ state = (in_i18n ? 1 : 0);
+
+ for (;;)
+ {
+ token_ty token;
+
+ if (in_i18n)
+ phase8_get (&token);
+ else
+ phase5_get (&token);
+
+ switch (token.type)
+ {
+ case token_type_i18n:
+ if (extract_parenthesized (mlp, inner_context, next_context_iter,
+ true))
+ return true;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_string_literal:
+ if (state == 1)
+ {
+ lex_pos_ty pos;
+ pos.file_name = logical_file_name;
+ pos.line_number = token.line_number;
+
+ if (plural_state == 0)
+ {
+ /* Seen an msgid. */
+ plural_mp = remember_a_message (mlp, NULL, token.string,
+ inner_context, &pos,
+ NULL, token.comment);
+ plural_state = 1;
+ state = 2;
+ }
+ else
+ {
+ /* Seen an msgid_plural. */
+ if (plural_mp != NULL)
+ remember_a_message_plural (plural_mp, token.string,
+ inner_context, &pos,
+ token.comment);
+ state = 0;
+ }
+ drop_reference (token.comment);
+ }
+ else
+ {
+ free_token (&token);
+ state = 0;
+ }
+ next_context_iter = null_context_list_iterator;
+ continue;
+
+ case token_type_symbol:
+ next_context_iter =
+ flag_context_list_iterator (
+ flag_context_list_table_lookup (
+ flag_context_list_table,
+ token.string, strlen (token.string)));
+ free_token (&token);
+ state = 0;
+ continue;
+
+ case token_type_lparen:
+ if (extract_parenthesized (mlp, inner_context, next_context_iter,
+ false))
+ return true;
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_rparen:
+ return false;
+
+ case token_type_comma:
+ if (state == 2)
+ state = 1;
+ else
+ state = 0;
+ inner_context =
+ inherited_context (outer_context,
+ flag_context_list_iterator_advance (
+ &context_iter));
+ next_context_iter = passthrough_context_list_iterator;
+ continue;
+
+ case token_type_other:
+ next_context_iter = null_context_list_iterator;
+ state = 0;
+ continue;
+
+ case token_type_eof:
+ return true;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+
+void
+extract_ycp (FILE *f,
+ const char *real_filename, const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp)
+{
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ fp = f;
+ real_file_name = real_filename;
+ logical_file_name = xstrdup (logical_filename);
+ line_number = 1;
+ char_in_line = 0;
+
+ last_comment_line = -1;
+ last_non_comment_line = -1;
+
+ flag_context_list_table = flag_table;
+
+ /* Eat tokens until eof is seen. When extract_parenthesized returns
+ due to an unbalanced closing parenthesis, just restart it. */
+ while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
+ false))
+ ;
+
+ fp = NULL;
+ real_file_name = NULL;
+ logical_file_name = NULL;
+ line_number = 0;
+ char_in_line = 0;
+}
diff --git a/gettext-tools/src/x-ycp.h b/gettext-tools/src/x-ycp.h
new file mode 100644
index 0000000..78cc853
--- /dev/null
+++ b/gettext-tools/src/x-ycp.h
@@ -0,0 +1,48 @@
+/* xgettext YCP backend.
+ Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xgettext.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_YCP \
+ { "ycp", "YCP" }, \
+
+#define SCANNERS_YCP \
+ { "YCP", extract_ycp, \
+ &flag_table_ycp, &formatstring_ycp, NULL, NULL }, \
+
+/* Scan an YCP file and add its translatable strings to mdlp. */
+extern void extract_ycp (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+extern void init_flag_table_ycp (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c
new file mode 100644
index 0000000..28d28a0
--- /dev/null
+++ b/gettext-tools/src/xgettext.c
@@ -0,0 +1,3701 @@
+/* Extracts strings from C source file to Uniforum style .po file.
+ Copyright (C) 1995-1998, 2000-2012 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <alloca.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <locale.h>
+#include <limits.h>
+
+#include "xgettext.h"
+#include "closeout.h"
+#include "dir-list.h"
+#include "file-list.h"
+#include "str-list.h"
+#include "error.h"
+#include "error-progname.h"
+#include "progname.h"
+#include "relocatable.h"
+#include "basename.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+#include "xsize.h"
+#include "xalloc.h"
+#include "xmalloca.h"
+#include "c-strstr.h"
+#include "xerror.h"
+#include "filename.h"
+#include "concat-filename.h"
+#include "c-strcase.h"
+#include "open-catalog.h"
+#include "read-catalog-abstract.h"
+#include "read-po.h"
+#include "message.h"
+#include "po-charset.h"
+#include "msgl-iconv.h"
+#include "msgl-ascii.h"
+#include "po-time.h"
+#include "write-catalog.h"
+#include "write-po.h"
+#include "write-properties.h"
+#include "write-stringtable.h"
+#include "color.h"
+#include "format.h"
+#include "propername.h"
+#include "unistr.h"
+#include "gettext.h"
+
+/* A convenience macro. I don't like writing gettext() every time. */
+#define _(str) gettext (str)
+
+
+#include "x-c.h"
+#include "x-po.h"
+#include "x-sh.h"
+#include "x-python.h"
+#include "x-lisp.h"
+#include "x-elisp.h"
+#include "x-librep.h"
+#include "x-scheme.h"
+#include "x-smalltalk.h"
+#include "x-java.h"
+#include "x-properties.h"
+#include "x-csharp.h"
+#include "x-awk.h"
+#include "x-ycp.h"
+#include "x-tcl.h"
+#include "x-perl.h"
+#include "x-php.h"
+#include "x-stringtable.h"
+#include "x-rst.h"
+#include "x-glade.h"
+#include "x-lua.h"
+#include "x-javascript.h"
+#include "x-vala.h"
+#include "x-gsettings.h"
+#include "x-desktop.h"
+
+
+/* If nonzero add all comments immediately preceding one of the keywords. */
+static bool add_all_comments = false;
+
+/* Tag used in comment of prevailing domain. */
+static char *comment_tag;
+
+/* Name of default domain file. If not set defaults to messages.po. */
+static const char *default_domain;
+
+/* If called with --debug option the output reflects whether format
+ string recognition is done automatically or forced by the user. */
+static int do_debug;
+
+/* Content of .po files with symbols to be excluded. */
+message_list_ty *exclude;
+
+/* Force output of PO file even if empty. */
+static int force_po;
+
+/* Copyright holder of the output file and the translations. */
+static const char *copyright_holder = "THE PACKAGE'S COPYRIGHT HOLDER";
+
+/* Package name. */
+static const char *package_name = NULL;
+
+/* Package version. */
+static const char *package_version = NULL;
+
+/* Email address or URL for reports of bugs in msgids. */
+static const char *msgid_bugs_address = NULL;
+
+/* String used as prefix for msgstr. */
+static const char *msgstr_prefix;
+
+/* String used as suffix for msgstr. */
+static const char *msgstr_suffix;
+
+/* Directory in which output files are created. */
+static char *output_dir;
+
+/* The output syntax: .pot or .properties or .strings. */
+static catalog_output_format_ty output_syntax = &output_format_po;
+
+/* If nonzero omit header with information about this run. */
+int xgettext_omit_header;
+
+/* Table of flag_context_list_ty tables. */
+static flag_context_list_table_ty flag_table_c;
+static flag_context_list_table_ty flag_table_cxx_qt;
+static flag_context_list_table_ty flag_table_cxx_kde;
+static flag_context_list_table_ty flag_table_cxx_boost;
+static flag_context_list_table_ty flag_table_objc;
+static flag_context_list_table_ty flag_table_gcc_internal;
+static flag_context_list_table_ty flag_table_sh;
+static flag_context_list_table_ty flag_table_python;
+static flag_context_list_table_ty flag_table_lisp;
+static flag_context_list_table_ty flag_table_elisp;
+static flag_context_list_table_ty flag_table_librep;
+static flag_context_list_table_ty flag_table_scheme;
+static flag_context_list_table_ty flag_table_java;
+static flag_context_list_table_ty flag_table_csharp;
+static flag_context_list_table_ty flag_table_awk;
+static flag_context_list_table_ty flag_table_ycp;
+static flag_context_list_table_ty flag_table_tcl;
+static flag_context_list_table_ty flag_table_perl;
+static flag_context_list_table_ty flag_table_php;
+static flag_context_list_table_ty flag_table_lua;
+static flag_context_list_table_ty flag_table_javascript;
+static flag_context_list_table_ty flag_table_vala;
+
+/* If true, recognize Qt format strings. */
+static bool recognize_format_qt;
+
+/* If true, recognize KDE format strings. */
+static bool recognize_format_kde;
+
+/* If true, recognize Boost format strings. */
+static bool recognize_format_boost;
+
+/* 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
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "add-comments", optional_argument, NULL, 'c' },
+ { "add-location", optional_argument, NULL, 'n' },
+ { "boost", no_argument, NULL, CHAR_MAX + 11 },
+ { "c++", no_argument, NULL, 'C' },
+ { "color", optional_argument, NULL, CHAR_MAX + 14 },
+ { "copyright-holder", required_argument, NULL, CHAR_MAX + 1 },
+ { "debug", no_argument, &do_debug, 1 },
+ { "default-domain", required_argument, NULL, 'd' },
+ { "directory", required_argument, NULL, 'D' },
+ { "escape", no_argument, NULL, 'E' },
+ { "exclude-file", required_argument, NULL, 'x' },
+ { "extract-all", no_argument, NULL, 'a' },
+ { "files-from", required_argument, NULL, 'f' },
+ { "flag", required_argument, NULL, CHAR_MAX + 8 },
+ { "force-po", no_argument, &force_po, 1 },
+ { "foreign-user", no_argument, NULL, CHAR_MAX + 2 },
+ { "from-code", required_argument, NULL, CHAR_MAX + 3 },
+ { "help", no_argument, NULL, 'h' },
+ { "indent", no_argument, NULL, 'i' },
+ { "join-existing", no_argument, NULL, 'j' },
+ { "kde", no_argument, NULL, CHAR_MAX + 10 },
+ { "keyword", optional_argument, NULL, 'k' },
+ { "language", required_argument, NULL, 'L' },
+ { "msgid-bugs-address", required_argument, NULL, CHAR_MAX + 5 },
+ { "msgstr-prefix", optional_argument, NULL, 'm' },
+ { "msgstr-suffix", optional_argument, NULL, 'M' },
+ { "no-escape", no_argument, NULL, 'e' },
+ { "no-location", no_argument, NULL, CHAR_MAX + 16 },
+ { "no-wrap", no_argument, NULL, CHAR_MAX + 4 },
+ { "omit-header", no_argument, &xgettext_omit_header, 1 },
+ { "output", required_argument, NULL, 'o' },
+ { "output-dir", required_argument, NULL, 'p' },
+ { "package-name", required_argument, NULL, CHAR_MAX + 12 },
+ { "package-version", required_argument, NULL, CHAR_MAX + 13 },
+ { "properties-output", no_argument, NULL, CHAR_MAX + 6 },
+ { "qt", no_argument, NULL, CHAR_MAX + 9 },
+ { "sort-by-file", no_argument, NULL, 'F' },
+ { "sort-output", no_argument, NULL, 's' },
+ { "strict", no_argument, NULL, 'S' },
+ { "string-limit", required_argument, NULL, 'l' },
+ { "stringtable-output", no_argument, NULL, CHAR_MAX + 7 },
+ { "style", required_argument, NULL, CHAR_MAX + 15 },
+ { "trigraphs", no_argument, NULL, 'T' },
+ { "version", no_argument, NULL, 'V' },
+ { "width", required_argument, NULL, 'w', },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* The extractors must all be functions returning void and taking three
+ arguments designating the input stream and one message domain list argument
+ in which to add the messages. */
+typedef void (*extractor_func) (FILE *fp, const char *real_filename,
+ const char *logical_filename,
+ flag_context_list_table_ty *flag_table,
+ msgdomain_list_ty *mdlp);
+
+typedef struct extractor_ty extractor_ty;
+struct extractor_ty
+{
+ extractor_func func;
+ flag_context_list_table_ty *flag_table;
+ struct formatstring_parser *formatstring_parser1;
+ struct formatstring_parser *formatstring_parser2;
+ struct formatstring_parser *formatstring_parser3;
+ struct literalstring_parser *literalstring_parser;
+};
+
+
+/* Forward declaration of local functions. */
+static void usage (int status)
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ > 4) || __GNUC__ > 2)
+ __attribute__ ((noreturn))
+#endif
+;
+static void read_exclusion_file (char *file_name);
+static void extract_from_file (const char *file_name, extractor_ty extractor,
+ msgdomain_list_ty *mdlp);
+static message_ty *construct_header (void);
+static void finalize_header (msgdomain_list_ty *mdlp);
+static extractor_ty language_to_extractor (const char *name);
+static const char *extension_to_language (const char *extension);
+
+
+int
+main (int argc, char *argv[])
+{
+ int optchar;
+ bool do_help = false;
+ bool do_version = false;
+ msgdomain_list_ty *mdlp;
+ bool join_existing = false;
+ bool no_default_keywords = false;
+ bool some_additional_keywords = false;
+ bool sort_by_msgid = false;
+ bool sort_by_filepos = false;
+ const char *file_name;
+ const char *files_from = NULL;
+ string_list_ty *file_list;
+ char *output_file = NULL;
+ const char *language = NULL;
+ extractor_ty extractor = { NULL, NULL, NULL, NULL };
+ int cnt;
+ size_t i;
+
+ /* Set program name for messages. */
+ set_program_name (argv[0]);
+ error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+ /* Set locale via LC_ALL. */
+ setlocale (LC_ALL, "");
+#endif
+
+ /* Set the text message domain. */
+ bindtextdomain (PACKAGE, relocate (LOCALEDIR));
+ bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
+ textdomain (PACKAGE);
+
+ /* Ensure that write errors on stdout are detected. */
+ atexit (close_stdout);
+
+ /* Set initial value of variables. */
+ default_domain = MESSAGE_DOMAIN_DEFAULT;
+ xgettext_global_source_encoding = po_charset_ascii;
+ init_flag_table_c ();
+ init_flag_table_objc ();
+ init_flag_table_gcc_internal ();
+ init_flag_table_sh ();
+ init_flag_table_python ();
+ init_flag_table_lisp ();
+ init_flag_table_elisp ();
+ init_flag_table_librep ();
+ init_flag_table_scheme ();
+ init_flag_table_java ();
+ init_flag_table_csharp ();
+ init_flag_table_awk ();
+ init_flag_table_ycp ();
+ init_flag_table_tcl ();
+ init_flag_table_perl ();
+ init_flag_table_php ();
+ init_flag_table_lua ();
+ init_flag_table_javascript ();
+ init_flag_table_vala ();
+
+ while ((optchar = getopt_long (argc, argv,
+ "ac::Cd:D:eEf:Fhijk::l:L:m::M::no:p:sTVw:x:",
+ long_options, NULL)) != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'a':
+ x_c_extract_all ();
+ x_sh_extract_all ();
+ x_python_extract_all ();
+ x_lisp_extract_all ();
+ x_elisp_extract_all ();
+ x_librep_extract_all ();
+ x_scheme_extract_all ();
+ x_java_extract_all ();
+ x_csharp_extract_all ();
+ x_awk_extract_all ();
+ x_tcl_extract_all ();
+ x_perl_extract_all ();
+ x_php_extract_all ();
+ x_glade_extract_all ();
+ x_lua_extract_all ();
+ x_javascript_extract_all ();
+ x_vala_extract_all ();
+ break;
+
+ case 'c':
+ if (optarg == NULL)
+ {
+ add_all_comments = true;
+ comment_tag = NULL;
+ }
+ else
+ {
+ add_all_comments = false;
+ comment_tag = optarg;
+ /* We ignore leading white space. */
+ while (isspace ((unsigned char) *comment_tag))
+ ++comment_tag;
+ }
+ break;
+
+ case 'C':
+ language = "C++";
+ break;
+
+ case 'd':
+ default_domain = optarg;
+ break;
+
+ case 'D':
+ dir_list_append (optarg);
+ break;
+
+ case 'e':
+ message_print_style_escape (false);
+ break;
+
+ case 'E':
+ message_print_style_escape (true);
+ break;
+
+ case 'f':
+ files_from = optarg;
+ break;
+
+ case 'F':
+ sort_by_filepos = true;
+ break;
+
+ case 'h':
+ do_help = true;
+ break;
+
+ case 'i':
+ message_print_style_indent ();
+ break;
+
+ case 'j':
+ join_existing = true;
+ break;
+
+ case 'k':
+ if (optarg != NULL && *optarg == '\0')
+ /* Make "--keyword=" work like "--keyword" and "-k". */
+ optarg = NULL;
+ x_c_keyword (optarg);
+ x_objc_keyword (optarg);
+ x_sh_keyword (optarg);
+ x_python_keyword (optarg);
+ x_lisp_keyword (optarg);
+ x_elisp_keyword (optarg);
+ x_librep_keyword (optarg);
+ x_scheme_keyword (optarg);
+ x_java_keyword (optarg);
+ x_csharp_keyword (optarg);
+ x_awk_keyword (optarg);
+ x_tcl_keyword (optarg);
+ x_perl_keyword (optarg);
+ x_php_keyword (optarg);
+ x_glade_keyword (optarg);
+ x_lua_keyword (optarg);
+ x_javascript_keyword (optarg);
+ x_vala_keyword (optarg);
+ x_desktop_keyword (optarg);
+ if (optarg == NULL)
+ no_default_keywords = true;
+ else
+ some_additional_keywords = true;
+ break;
+
+ case 'l':
+ /* Accepted for backward compatibility with 0.10.35. */
+ break;
+
+ case 'L':
+ language = optarg;
+ break;
+
+ case 'm':
+ /* -m takes an optional argument. If none is given "" is assumed. */
+ msgstr_prefix = optarg == NULL ? "" : optarg;
+ break;
+
+ case 'M':
+ /* -M takes an optional argument. If none is given "" is assumed. */
+ msgstr_suffix = optarg == NULL ? "" : optarg;
+ break;
+
+ case 'n':
+ if (handle_filepos_comment_option (optarg))
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'o':
+ output_file = optarg;
+ break;
+
+ case 'p':
+ {
+ size_t len = strlen (optarg);
+
+ if (output_dir != NULL)
+ free (output_dir);
+
+ if (optarg[len - 1] == '/')
+ output_dir = xstrdup (optarg);
+ else
+ output_dir = xasprintf ("%s/", optarg);
+ }
+ break;
+
+ case 's':
+ sort_by_msgid = true;
+ break;
+
+ case 'S':
+ message_print_style_uniforum ();
+ break;
+
+ case 'T':
+ x_c_trigraphs ();
+ break;
+
+ case 'V':
+ do_version = true;
+ break;
+
+ case 'w':
+ {
+ int value;
+ char *endp;
+ value = strtol (optarg, &endp, 10);
+ if (endp != optarg)
+ message_page_width_set (value);
+ }
+ break;
+
+ case 'x':
+ read_exclusion_file (optarg);
+ break;
+
+ case CHAR_MAX + 1: /* --copyright-holder */
+ copyright_holder = optarg;
+ break;
+
+ case CHAR_MAX + 2: /* --foreign-user */
+ copyright_holder = "";
+ break;
+
+ case CHAR_MAX + 3: /* --from-code */
+ xgettext_global_source_encoding = po_charset_canonicalize (optarg);
+ if (xgettext_global_source_encoding == NULL)
+ {
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+'%s' is not a valid encoding name. Using ASCII as fallback.\n"),
+ optarg));
+ xgettext_global_source_encoding = po_charset_ascii;
+ }
+ break;
+
+ case CHAR_MAX + 4: /* --no-wrap */
+ message_page_width_ignore ();
+ break;
+
+ case CHAR_MAX + 5: /* --msgid-bugs-address */
+ msgid_bugs_address = optarg;
+ break;
+
+ case CHAR_MAX + 6: /* --properties-output */
+ output_syntax = &output_format_properties;
+ break;
+
+ case CHAR_MAX + 7: /* --stringtable-output */
+ output_syntax = &output_format_stringtable;
+ break;
+
+ case CHAR_MAX + 8: /* --flag */
+ xgettext_record_flag (optarg);
+ break;
+
+ case CHAR_MAX + 9: /* --qt */
+ recognize_format_qt = true;
+ break;
+
+ case CHAR_MAX + 10: /* --kde */
+ recognize_format_kde = true;
+ break;
+
+ case CHAR_MAX + 11: /* --boost */
+ recognize_format_boost = true;
+ break;
+
+ case CHAR_MAX + 12: /* --package-name */
+ package_name = optarg;
+ break;
+
+ case CHAR_MAX + 13: /* --package-version */
+ package_version = optarg;
+ break;
+
+ case CHAR_MAX + 14: /* --color */
+ if (handle_color_option (optarg) || color_test_mode)
+ usage (EXIT_FAILURE);
+ break;
+
+ case CHAR_MAX + 15: /* --style */
+ handle_style_option (optarg);
+ break;
+
+ case CHAR_MAX + 16: /* --no-location */
+ message_print_style_filepos (filepos_comment_none);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ /* Version information requested. */
+ if (do_version)
+ {
+ printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+ /* xgettext: no-wrap */
+ printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+"),
+ "1995-1998, 2000-2013");
+ printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
+ exit (EXIT_SUCCESS);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ usage (EXIT_SUCCESS);
+
+ /* Verify selected options. */
+ if (sort_by_msgid && sort_by_filepos)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--sort-output", "--sort-by-file");
+
+ /* We cannot support both Qt and KDE, or Qt and Boost, or KDE and Boost
+ format strings, because there are only two formatstring parsers per
+ language, and formatstring_c is the first one for C++. */
+ if (recognize_format_qt && recognize_format_kde)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--qt", "--kde");
+ if (recognize_format_qt && recognize_format_boost)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--qt", "--boost");
+ if (recognize_format_kde && recognize_format_boost)
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--kde", "--boost");
+
+ if (join_existing && strcmp (default_domain, "-") == 0)
+ error (EXIT_FAILURE, 0, _("\
+--join-existing cannot be used when output is written to stdout"));
+
+ if (no_default_keywords && !some_additional_keywords)
+ {
+ error (0, 0, _("\
+xgettext cannot work without keywords to look for"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Test whether we have some input files given. */
+ if (files_from == NULL && optind >= argc)
+ {
+ error (EXIT_SUCCESS, 0, _("no input file given"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Determine extractor from language. */
+ if (language != NULL)
+ extractor = language_to_extractor (language);
+
+ /* Canonize msgstr prefix/suffix. */
+ if (msgstr_prefix != NULL && msgstr_suffix == NULL)
+ msgstr_suffix = "";
+ else if (msgstr_prefix == NULL && msgstr_suffix != NULL)
+ msgstr_prefix = "";
+
+ /* Default output directory is the current directory. */
+ if (output_dir == NULL)
+ output_dir = ".";
+
+ /* Construct the name of the output file. If the default domain has
+ the special name "-" we write to stdout. */
+ if (output_file)
+ {
+ if (IS_ABSOLUTE_PATH (output_file) || strcmp (output_file, "-") == 0)
+ file_name = xstrdup (output_file);
+ else
+ /* Please do NOT add a .po suffix! */
+ file_name = xconcatenated_filename (output_dir, output_file, NULL);
+ }
+ else if (strcmp (default_domain, "-") == 0)
+ file_name = "-";
+ else
+ file_name = xconcatenated_filename (output_dir, default_domain, ".po");
+
+ /* Determine list of files we have to process. */
+ if (files_from != NULL)
+ file_list = read_names_from_file (files_from);
+ else
+ file_list = string_list_alloc ();
+ /* Append names from command line. */
+ for (cnt = optind; cnt < argc; ++cnt)
+ string_list_append_unique (file_list, argv[cnt]);
+
+ /* Allocate converter from xgettext_global_source_encoding to UTF-8 (except
+ from ASCII or UTF-8, when this conversion is a no-op). */
+ if (xgettext_global_source_encoding != po_charset_ascii
+ && xgettext_global_source_encoding != po_charset_utf8)
+ {
+#if HAVE_ICONV
+ iconv_t cd;
+
+ /* Avoid glibc-2.1 bug with EUC-KR. */
+# if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
+ && !defined _LIBICONV_VERSION
+ if (strcmp (xgettext_global_source_encoding, "EUC-KR") == 0)
+ cd = (iconv_t)(-1);
+ else
+# endif
+ cd = iconv_open (po_charset_utf8, xgettext_global_source_encoding);
+ if (cd == (iconv_t)(-1))
+ error (EXIT_FAILURE, 0, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), \
+and iconv() does not support this conversion."),
+ xgettext_global_source_encoding, po_charset_utf8,
+ basename (program_name));
+ xgettext_global_source_iconv = cd;
+#else
+ error (EXIT_FAILURE, 0, _("\
+Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). \
+This version was built without iconv()."),
+ xgettext_global_source_encoding, po_charset_utf8,
+ basename (program_name));
+#endif
+ }
+
+ /* Allocate a message list to remember all the messages. */
+ mdlp = msgdomain_list_alloc (true);
+
+ /* Generate a header, so that we know how and when this PO file was
+ created. */
+ if (!xgettext_omit_header)
+ message_list_append (mdlp->item[0]->messages, construct_header ());
+
+ /* Read in the old messages, so that we can add to them. */
+ if (join_existing)
+ {
+ /* Temporarily reset the directory list to empty, because file_name
+ is an output file and therefore should not be searched for. */
+ void *saved_directory_list = dir_list_save_reset ();
+ extractor_ty po_extractor = { extract_po, NULL, NULL, NULL };
+
+ extract_from_file (file_name, po_extractor, mdlp);
+ if (!is_ascii_msgdomain_list (mdlp))
+ mdlp = iconv_msgdomain_list (mdlp, "UTF-8", true, file_name);
+
+ dir_list_restore (saved_directory_list);
+ }
+
+ /* Process all input files. */
+ for (i = 0; i < file_list->nitems; i++)
+ {
+ const char *filename;
+ extractor_ty this_file_extractor;
+
+ filename = file_list->item[i];
+
+ if (extractor.func)
+ this_file_extractor = extractor;
+ else
+ {
+ const char *base;
+ char *reduced;
+ const char *extension;
+ const char *language;
+ const char *p;
+
+ base = strrchr (filename, '/');
+ if (!base)
+ base = filename;
+
+ reduced = xstrdup (base);
+ /* Remove a trailing ".in" - it's a generic suffix. */
+ while (strlen (reduced) >= 3
+ && memcmp (reduced + strlen (reduced) - 3, ".in", 3) == 0)
+ reduced[strlen (reduced) - 3] = '\0';
+
+ /* Work out what the file extension is. */
+ language = NULL;
+ p = reduced + strlen (reduced);
+ for (; p > reduced && language == NULL; p--)
+ {
+ if (*p == '.')
+ {
+ extension = p + 1;
+
+ /* Derive the language from the extension, and the extractor
+ function from the language. */
+ language = extension_to_language (extension);
+ }
+ }
+
+ if (language == NULL)
+ {
+ extension = strrchr (reduced, '.');
+ if (extension == NULL)
+ extension = "";
+ else
+ extension++;
+ error (0, 0, _("\
+warning: file '%s' extension '%s' is unknown; will try C"), filename, extension);
+ language = "C";
+ }
+ this_file_extractor = language_to_extractor (language);
+
+ free (reduced);
+ }
+
+ /* Extract the strings from the file. */
+ extract_from_file (filename, this_file_extractor, mdlp);
+ }
+ string_list_free (file_list);
+
+ /* Finalize the constructed header. */
+ if (!xgettext_omit_header)
+ finalize_header (mdlp);
+
+ /* Free the allocated converter. */
+#if HAVE_ICONV
+ if (xgettext_global_source_encoding != po_charset_ascii
+ && xgettext_global_source_encoding != po_charset_utf8)
+ iconv_close (xgettext_global_source_iconv);
+#endif
+
+ /* Sorting the list of messages. */
+ if (sort_by_filepos)
+ msgdomain_list_sort_by_filepos (mdlp);
+ else if (sort_by_msgid)
+ msgdomain_list_sort_by_msgid (mdlp);
+
+ /* Write the PO file. */
+ msgdomain_list_print (mdlp, file_name, output_syntax, force_po, do_debug);
+
+ exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try '%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] [INPUTFILE]...\n\
+"), program_name);
+ printf ("\n");
+ printf (_("\
+Extract translatable strings from given input files.\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+Similarly for optional arguments.\n\
+"));
+ printf ("\n");
+ printf (_("\
+Input file location:\n"));
+ printf (_("\
+ INPUTFILE ... input files\n"));
+ printf (_("\
+ -f, --files-from=FILE get list of input files from FILE\n"));
+ printf (_("\
+ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
+ printf (_("\
+If input file is -, standard input is read.\n"));
+ printf ("\n");
+ printf (_("\
+Output file location:\n"));
+ printf (_("\
+ -d, --default-domain=NAME use NAME.po for output (instead of messages.po)\n"));
+ printf (_("\
+ -o, --output=FILE write output to specified file\n"));
+ printf (_("\
+ -p, --output-dir=DIR output files will be placed in directory DIR\n"));
+ printf (_("\
+If output file is -, output is written to standard output.\n"));
+ printf ("\n");
+ printf (_("\
+Choice of input file language:\n"));
+ printf (_("\
+ -L, --language=NAME recognise the specified language\n\
+ (C, C++, ObjectiveC, PO, Shell, Python, Lisp,\n\
+ EmacsLisp, librep, Scheme, Smalltalk, Java,\n\
+ JavaProperties, C#, awk, YCP, Tcl, Perl, PHP,\n\
+ GCC-source, NXStringTable, RST, Glade, Lua,\n\
+ JavaScript, Vala, Desktop)\n"));
+ printf (_("\
+ -C, --c++ shorthand for --language=C++\n"));
+ printf (_("\
+By default the language is guessed depending on the input file name extension.\n"));
+ printf ("\n");
+ printf (_("\
+Input file interpretation:\n"));
+ printf (_("\
+ --from-code=NAME encoding of input files\n\
+ (except for Python, Tcl, Glade)\n"));
+ printf (_("\
+By default the input files are assumed to be in ASCII.\n"));
+ printf ("\n");
+ printf (_("\
+Operation mode:\n"));
+ printf (_("\
+ -j, --join-existing join messages with existing file\n"));
+ printf (_("\
+ -x, --exclude-file=FILE.po entries from FILE.po are not extracted\n"));
+ printf (_("\
+ -cTAG, --add-comments=TAG place comment blocks starting with TAG and\n\
+ preceding keyword lines in output file\n\
+ -c, --add-comments place all comment blocks preceding keyword lines\n\
+ in output file\n"));
+ printf ("\n");
+ printf (_("\
+Language specific options:\n"));
+ printf (_("\
+ -a, --extract-all extract all strings\n"));
+ printf (_("\
+ (only languages C, C++, ObjectiveC, Shell,\n\
+ Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
+ C#, awk, Tcl, Perl, PHP, GCC-source, Glade,\n\
+ Lua, JavaScript, Vala)\n"));
+ printf (_("\
+ -kWORD, --keyword=WORD look for WORD as an additional keyword\n\
+ -k, --keyword do not to use default keywords\n"));
+ printf (_("\
+ (only languages C, C++, ObjectiveC, Shell,\n\
+ Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
+ C#, awk, Tcl, Perl, PHP, GCC-source, Glade,\n\
+ Lua, JavaScript, Vala, Desktop)\n"));
+ printf (_("\
+ --flag=WORD:ARG:FLAG additional flag for strings inside the argument\n\
+ number ARG of keyword WORD\n"));
+ printf (_("\
+ (only languages C, C++, ObjectiveC, Shell,\n\
+ Python, Lisp, EmacsLisp, librep, Scheme, Java,\n\
+ C#, awk, YCP, Tcl, Perl, PHP, GCC-source,\n\
+ Lua, JavaScript, Vala)\n"));
+ printf (_("\
+ -T, --trigraphs understand ANSI C trigraphs for input\n"));
+ printf (_("\
+ (only languages C, C++, ObjectiveC)\n"));
+ printf (_("\
+ --qt recognize Qt format strings\n"));
+ printf (_("\
+ (only language C++)\n"));
+ printf (_("\
+ --kde recognize KDE 4 format strings\n"));
+ printf (_("\
+ (only language C++)\n"));
+ printf (_("\
+ --boost recognize Boost format strings\n"));
+ printf (_("\
+ (only language C++)\n"));
+ printf (_("\
+ --debug more detailed formatstring recognition result\n"));
+ printf ("\n");
+ printf (_("\
+Output details:\n"));
+ printf (_("\
+ --color use colors and other text attributes always\n\
+ --color=WHEN use colors and other text attributes if WHEN.\n\
+ WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
+ printf (_("\
+ --style=STYLEFILE specify CSS style rule file for --color\n"));
+ printf (_("\
+ -e, --no-escape do not use C escapes in output (default)\n"));
+ printf (_("\
+ -E, --escape use C escapes in output, no extended chars\n"));
+ printf (_("\
+ --force-po write PO file even if empty\n"));
+ printf (_("\
+ -i, --indent write the .po file using indented style\n"));
+ printf (_("\
+ --no-location do not write '#: filename:line' lines\n"));
+ printf (_("\
+ -n, --add-location generate '#: filename:line' lines (default)\n"));
+ printf (_("\
+ --strict write out strict Uniforum conforming .po file\n"));
+ printf (_("\
+ --properties-output write out a Java .properties file\n"));
+ printf (_("\
+ --stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
+ printf (_("\
+ -w, --width=NUMBER set output page width\n"));
+ printf (_("\
+ --no-wrap do not break long message lines, longer than\n\
+ the output page width, into several lines\n"));
+ printf (_("\
+ -s, --sort-output generate sorted output\n"));
+ printf (_("\
+ -F, --sort-by-file sort output by file location\n"));
+ printf (_("\
+ --omit-header don't write header with 'msgid \"\"' entry\n"));
+ printf (_("\
+ --copyright-holder=STRING set copyright holder in output\n"));
+ printf (_("\
+ --foreign-user omit FSF copyright in output for foreign user\n"));
+ printf (_("\
+ --package-name=PACKAGE set package name in output\n"));
+ printf (_("\
+ --package-version=VERSION set package version in output\n"));
+ printf (_("\
+ --msgid-bugs-address=EMAIL@ADDRESS set report address for msgid bugs\n"));
+ printf (_("\
+ -m[STRING], --msgstr-prefix[=STRING] use STRING or \"\" as prefix for msgstr\n\
+ values\n"));
+ printf (_("\
+ -M[STRING], --msgstr-suffix[=STRING] use STRING or \"\" as suffix for msgstr\n\
+ values\n"));
+ printf ("\n");
+ printf (_("\
+Informative output:\n"));
+ printf (_("\
+ -h, --help display this help and exit\n"));
+ printf (_("\
+ -V, --version output version information and exit\n"));
+ printf ("\n");
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+ stdout);
+ }
+
+ exit (status);
+}
+
+
+static void
+exclude_directive_domain (abstract_catalog_reader_ty *pop, char *name)
+{
+ po_gram_error_at_line (&gram_pos,
+ _("this file may not contain domain directives"));
+}
+
+
+static void
+exclude_directive_message (abstract_catalog_reader_ty *pop,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ message_ty *mp;
+
+ /* See if this message ID has been seen before. */
+ if (exclude == NULL)
+ exclude = message_list_alloc (true);
+ mp = message_list_search (exclude, msgctxt, msgid);
+ if (mp != NULL)
+ free (msgid);
+ else
+ {
+ mp = message_alloc (msgctxt, msgid, msgid_plural, "", 1, msgstr_pos);
+ /* Do not free msgid. */
+ message_list_append (exclude, mp);
+ }
+
+ /* All we care about is the msgid. Throw the msgstr away.
+ Don't even check for duplicate msgids. */
+ free (msgstr);
+}
+
+
+/* So that the one parser can be used for multiple programs, and also
+ use good data hiding and encapsulation practices, an object
+ oriented approach has been taken. An object instance is allocated,
+ and all actions resulting from the parse will be through
+ invocations of method functions of that object. */
+
+static abstract_catalog_reader_class_ty exclude_methods =
+{
+ sizeof (abstract_catalog_reader_ty),
+ NULL, /* constructor */
+ NULL, /* destructor */
+ NULL, /* parse_brief */
+ NULL, /* parse_debrief */
+ exclude_directive_domain,
+ exclude_directive_message,
+ NULL, /* comment */
+ NULL, /* comment_dot */
+ NULL, /* comment_filepos */
+ NULL, /* comment_special */
+};
+
+
+static void
+read_exclusion_file (char *filename)
+{
+ char *real_filename;
+ FILE *fp = open_catalog_file (filename, &real_filename, true);
+ abstract_catalog_reader_ty *pop;
+
+ pop = catalog_reader_alloc (&exclude_methods);
+ catalog_reader_parse (pop, fp, real_filename, filename, &input_format_po);
+ catalog_reader_free (pop);
+
+ if (fp != stdin)
+ fclose (fp);
+}
+
+
+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,
+ const char *name_start, const char *name_end,
+ int argnum, enum is_format value, bool pass)
+{
+ char *allocated_name = NULL;
+
+ if (table == &flag_table_lisp)
+ {
+ /* Convert NAME to upper case. */
+ size_t name_len = name_end - name_start;
+ char *name = allocated_name = (char *) xmalloca (name_len);
+ size_t i;
+
+ for (i = 0; i < name_len; i++)
+ name[i] = (name_start[i] >= 'a' && name_start[i] <= 'z'
+ ? name_start[i] - 'a' + 'A'
+ : name_start[i]);
+ name_start = name;
+ name_end = name + name_len;
+ }
+ else if (table == &flag_table_tcl)
+ {
+ /* Remove redundant "::" prefix. */
+ if (name_end - name_start > 2
+ && name_start[0] == ':' && name_start[1] == ':')
+ name_start += 2;
+ }
+
+ /* Insert the pair (VALUE, PASS) at INDEX in the element numbered ARGNUM
+ of the list corresponding to NAME in the TABLE. */
+ if (table->table == NULL)
+ 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;
+ }
+ }
+ }
+
+ if (allocated_name != NULL)
+ freea (allocated_name);
+}
+
+
+void
+xgettext_record_flag (const char *optionstring)
+{
+ /* Check the string has at least two colons. (Colons in the name are
+ allowed, needed for the Lisp and the Tcl backends.) */
+ const char *colon1;
+ const char *colon2;
+
+ for (colon2 = optionstring + strlen (optionstring); ; )
+ {
+ if (colon2 == optionstring)
+ goto err;
+ colon2--;
+ if (*colon2 == ':')
+ break;
+ }
+ for (colon1 = colon2; ; )
+ {
+ if (colon1 == optionstring)
+ goto err;
+ colon1--;
+ if (*colon1 == ':')
+ break;
+ }
+ {
+ const char *name_start = optionstring;
+ const char *name_end = colon1;
+ const char *argnum_start = colon1 + 1;
+ const char *argnum_end = colon2;
+ const char *flag = colon2 + 1;
+ int argnum;
+
+ /* Check the parts' syntax. */
+ if (name_end == name_start)
+ goto err;
+ if (argnum_end == argnum_start)
+ goto err;
+ {
+ char *endp;
+ argnum = strtol (argnum_start, &endp, 10);
+ if (endp != argnum_end)
+ goto err;
+ }
+ if (argnum <= 0)
+ goto err;
+
+ /* Analyze the flag part. */
+ {
+ bool pass;
+
+ pass = false;
+ if (strlen (flag) >= 5 && memcmp (flag, "pass-", 5) == 0)
+ {
+ pass = true;
+ flag += 5;
+ }
+
+ /* Unlike po_parse_comment_special(), we don't accept "fuzzy" or "wrap"
+ here - it has no sense. */
+ if (strlen (flag) >= 7
+ && memcmp (flag + strlen (flag) - 7, "-format", 7) == 0)
+ {
+ const char *p;
+ size_t n;
+ enum is_format value;
+ size_t type;
+
+ p = flag;
+ n = strlen (flag) - 7;
+
+ if (n >= 3 && memcmp (p, "no-", 3) == 0)
+ {
+ p += 3;
+ n -= 3;
+ value = no;
+ }
+ else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
+ {
+ p += 9;
+ n -= 9;
+ value = possible;
+ }
+ else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
+ {
+ p += 11;
+ n -= 11;
+ value = impossible;
+ }
+ else
+ value = yes_according_to_context;
+
+ for (type = 0; type < NFORMATS; type++)
+ if (strlen (format_language[type]) == n
+ && memcmp (format_language[type], p, n) == 0)
+ {
+ switch (type)
+ {
+ case format_c:
+ flag_context_list_table_insert (&flag_table_c, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ flag_context_list_table_insert (&flag_table_cxx_qt, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ flag_context_list_table_insert (&flag_table_cxx_kde, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ flag_context_list_table_insert (&flag_table_cxx_boost, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ flag_context_list_table_insert (&flag_table_objc, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_objc:
+ flag_context_list_table_insert (&flag_table_objc, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_sh:
+ flag_context_list_table_insert (&flag_table_sh, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_python:
+ flag_context_list_table_insert (&flag_table_python, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_python_brace:
+ flag_context_list_table_insert (&flag_table_python, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_lisp:
+ flag_context_list_table_insert (&flag_table_lisp, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_elisp:
+ flag_context_list_table_insert (&flag_table_elisp, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_librep:
+ flag_context_list_table_insert (&flag_table_librep, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_scheme:
+ flag_context_list_table_insert (&flag_table_scheme, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_smalltalk:
+ break;
+ case format_java:
+ flag_context_list_table_insert (&flag_table_java, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_csharp:
+ flag_context_list_table_insert (&flag_table_csharp, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_awk:
+ flag_context_list_table_insert (&flag_table_awk, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_pascal:
+ break;
+ case format_ycp:
+ flag_context_list_table_insert (&flag_table_ycp, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_tcl:
+ flag_context_list_table_insert (&flag_table_tcl, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_perl:
+ flag_context_list_table_insert (&flag_table_perl, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_perl_brace:
+ flag_context_list_table_insert (&flag_table_perl, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_php:
+ flag_context_list_table_insert (&flag_table_php, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_gcc_internal:
+ flag_context_list_table_insert (&flag_table_gcc_internal, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_gfc_internal:
+ flag_context_list_table_insert (&flag_table_gcc_internal, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_qt:
+ flag_context_list_table_insert (&flag_table_cxx_qt, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_qt_plural:
+ flag_context_list_table_insert (&flag_table_cxx_qt, 2,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_kde:
+ flag_context_list_table_insert (&flag_table_cxx_kde, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_boost:
+ flag_context_list_table_insert (&flag_table_cxx_boost, 1,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_lua:
+ flag_context_list_table_insert (&flag_table_lua, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ case format_javascript:
+ flag_context_list_table_insert (&flag_table_javascript, 0,
+ name_start, name_end,
+ argnum, value, pass);
+ break;
+ default:
+ abort ();
+ }
+ return;
+ }
+ /* If the flag is not among the valid values, the optionstring is
+ invalid. */
+ }
+ }
+ }
+
+err:
+ error (EXIT_FAILURE, 0, _("\
+A --flag argument doesn't have the <keyword>:<argnum>:[pass-]<flag> syntax: %s"),
+ optionstring);
+}
+
+
+/* Comment handling: There is a list of automatic comments that may be appended
+ to the next message. Used by remember_a_message(). */
+
+static string_list_ty *comment;
+
+static void
+xgettext_comment_add (const char *str)
+{
+ if (comment == NULL)
+ comment = string_list_alloc ();
+ string_list_append (comment, str);
+}
+
+static const char *
+xgettext_comment (size_t n)
+{
+ if (comment == NULL || n >= comment->nitems)
+ return NULL;
+ return comment->item[n];
+}
+
+static void
+xgettext_comment_reset ()
+{
+ if (comment != NULL)
+ {
+ string_list_free (comment);
+ comment = NULL;
+ }
+}
+
+
+refcounted_string_list_ty *savable_comment;
+
+void
+savable_comment_add (const char *str)
+{
+ if (savable_comment == NULL)
+ {
+ savable_comment = XMALLOC (refcounted_string_list_ty);
+ savable_comment->refcount = 1;
+ string_list_init (&savable_comment->contents);
+ }
+ else if (savable_comment->refcount > 1)
+ {
+ /* Unshare the list by making copies. */
+ struct string_list_ty *oldcontents;
+ size_t i;
+
+ savable_comment->refcount--;
+ oldcontents = &savable_comment->contents;
+
+ savable_comment = XMALLOC (refcounted_string_list_ty);
+ savable_comment->refcount = 1;
+ string_list_init (&savable_comment->contents);
+ for (i = 0; i < oldcontents->nitems; i++)
+ string_list_append (&savable_comment->contents, oldcontents->item[i]);
+ }
+ string_list_append (&savable_comment->contents, str);
+}
+
+void
+savable_comment_reset ()
+{
+ drop_reference (savable_comment);
+ savable_comment = NULL;
+}
+
+static void
+savable_comment_to_xgettext_comment (refcounted_string_list_ty *rslp)
+{
+ xgettext_comment_reset ();
+ if (rslp != NULL)
+ {
+ size_t i;
+
+ for (i = 0; i < rslp->contents.nitems; i++)
+ xgettext_comment_add (rslp->contents.item[i]);
+ }
+}
+
+refcounted_string_list_ty *
+savable_comment_convert_encoding (refcounted_string_list_ty *comment,
+ lex_pos_ty *pos)
+{
+ refcounted_string_list_ty *result;
+ size_t i;
+
+ result = XMALLOC (refcounted_string_list_ty);
+ result->refcount = 1;
+ string_list_init (&result->contents);
+
+ for (i = 0; i < comment->contents.nitems; i++)
+ {
+ const char *old_string = comment->contents.item[i];
+ char *string = from_current_source_encoding (old_string,
+ lc_comment,
+ pos->file_name,
+ pos->line_number);
+ string_list_append (&result->contents, string);
+ if (string != old_string)
+ free (string);
+ }
+
+ return result;
+}
+
+
+
+static FILE *
+xgettext_open (const char *fn,
+ char **logical_file_name_p, char **real_file_name_p)
+{
+ FILE *fp;
+ char *new_name;
+ char *logical_file_name;
+
+ if (strcmp (fn, "-") == 0)
+ {
+ new_name = xstrdup (_("standard input"));
+ logical_file_name = xstrdup (new_name);
+ fp = stdin;
+ }
+ else if (IS_ABSOLUTE_PATH (fn))
+ {
+ new_name = xstrdup (fn);
+ fp = fopen (fn, "r");
+ if (fp == NULL)
+ error (EXIT_FAILURE, errno, _("\
+error while opening \"%s\" for reading"), fn);
+ logical_file_name = xstrdup (new_name);
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; ; ++j)
+ {
+ const char *dir = dir_list_nth (j);
+
+ if (dir == NULL)
+ error (EXIT_FAILURE, ENOENT, _("\
+error while opening \"%s\" for reading"), fn);
+
+ new_name = xconcatenated_filename (dir, fn, NULL);
+
+ fp = fopen (new_name, "r");
+ if (fp != NULL)
+ break;
+
+ if (errno != ENOENT)
+ error (EXIT_FAILURE, errno, _("\
+error while opening \"%s\" for reading"), new_name);
+ free (new_name);
+ }
+
+ /* Note that the NEW_NAME variable contains the actual file name
+ and the logical file name is what is reported by xgettext. In
+ this case NEW_NAME is set to the file which was found along the
+ directory search path, and LOGICAL_FILE_NAME is is set to the
+ file name which was searched for. */
+ logical_file_name = xstrdup (fn);
+ }
+
+ *logical_file_name_p = logical_file_name;
+ *real_file_name_p = new_name;
+ return fp;
+}
+
+
+/* Language dependent format string parser.
+ NULL if the language has no notion of format strings. */
+static struct formatstring_parser *current_formatstring_parser1;
+static struct formatstring_parser *current_formatstring_parser2;
+static struct formatstring_parser *current_formatstring_parser3;
+
+static struct literalstring_parser *current_literalstring_parser;
+
+static void
+extract_from_file (const char *file_name, extractor_ty extractor,
+ msgdomain_list_ty *mdlp)
+{
+ char *logical_file_name;
+ char *real_file_name;
+ FILE *fp = xgettext_open (file_name, &logical_file_name, &real_file_name);
+
+ /* Set the default for the source file encoding. May be overridden by
+ the extractor function. */
+ xgettext_current_source_encoding = xgettext_global_source_encoding;
+#if HAVE_ICONV
+ xgettext_current_source_iconv = xgettext_global_source_iconv;
+#endif
+
+ current_formatstring_parser1 = extractor.formatstring_parser1;
+ current_formatstring_parser2 = extractor.formatstring_parser2;
+ current_formatstring_parser3 = extractor.formatstring_parser3;
+ current_literalstring_parser = extractor.literalstring_parser;
+ extractor.func (fp, real_file_name, logical_file_name, extractor.flag_table,
+ mdlp);
+
+ if (fp != stdin)
+ fclose (fp);
+ free (logical_file_name);
+ free (real_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;
+ 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;
+
+ 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;
+ }
+ 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 = NULL;
+ 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;
+ bool interesting;
+
+ t += strlen ("xgettext:");
+
+ po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range,
+ &tmp_wrap);
+
+ 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;
+ }
+
+ /* 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_boost]))))
+ {
+ 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. */
+
+ /* 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_boost]))))
+ {
+ 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->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->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_escape = LET_NONE;
+ 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_escape = LET_NONE;
+ 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_escape = LET_NONE;
+ 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->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_escape = cp->msgctxt_escape;
+ ccp->msgctxt_pos = cp->msgctxt_pos;
+ ccp->msgid = (cp->msgid != NULL ? xstrdup (cp->msgid) : NULL);
+ ccp->msgid_escape = cp->msgid_escape;
+ 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_escape = cp->msgid_plural_escape;
+ ccp->msgid_plural_context = cp->msgid_plural_context;
+ ccp->msgid_plural_pos = cp->msgid_plural_pos;
+ }
+
+ return copy;
+}
+
+
+void
+arglist_parser_remember_literal (struct arglist_parser *ap,
+ int argnum, char *string,
+ flag_context_ty context,
+ char *file_name, size_t line_number,
+ refcounted_string_list_ty *comment,
+ enum literalstring_escape_type type)
+{
+ 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_escape = type;
+ 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_escape = type;
+ 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_escape = type;
+ 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 (struct arglist_parser *ap,
+ int argnum, char *string,
+ flag_context_ty context,
+ char *file_name, size_t line_number,
+ refcounted_string_list_ty *comment)
+{
+ arglist_parser_remember_literal (ap, argnum, string, context,
+ file_name, line_number,
+ comment, LET_NONE);
+}
+
+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;
+ struct literalstring_parser *parser = current_literalstring_parser;
+ const char *encoding;
+
+ /* 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;
+ }
+
+ if (best_cp->msgctxt != NULL)
+ {
+ if (parser != NULL && best_cp->msgctxt_escape != 0)
+ {
+ char *msgctxt =
+ parser->parse (best_cp->msgctxt,
+ &best_cp->msgctxt_pos,
+ best_cp->msgctxt_escape);
+ free (best_cp->msgctxt);
+ best_cp->msgctxt = msgctxt;
+ }
+ else
+ {
+ lex_pos_ty *pos = &best_cp->msgctxt_pos;
+ CONVERT_STRING (best_cp->msgctxt, lc_string);
+ }
+ }
+
+ if (parser != NULL && best_cp->msgid_escape != 0)
+ {
+ char *msgid = parser->parse (best_cp->msgid,
+ &best_cp->msgid_pos,
+ best_cp->msgid_escape);
+ if (best_cp->msgid_plural == best_cp->msgid)
+ best_cp->msgid_plural = msgid;
+ free (best_cp->msgid);
+ best_cp->msgid = msgid;
+ }
+ else
+ {
+ lex_pos_ty *pos = &best_cp->msgid_pos;
+ CONVERT_STRING (best_cp->msgid, lc_string);
+ }
+
+ if (best_cp->msgid_plural)
+ {
+ /* best_cp->msgid_plural may point to best_cp->msgid.
+ In that case, it is already interpreted and converted. */
+ if (best_cp->msgid_plural != best_cp->msgid)
+ {
+ if (parser != NULL
+ && best_cp->msgid_plural_escape != 0)
+ {
+ char *msgid_plural =
+ parser->parse (best_cp->msgid_plural,
+ &best_cp->msgid_plural_pos,
+ best_cp->msgid_plural_escape);
+ free (best_cp->msgid_plural);
+ best_cp->msgid_plural = msgid_plural;
+ }
+ else
+ {
+ lex_pos_ty *pos = &best_cp->msgid_plural_pos;
+ CONVERT_STRING (best_cp->msgid_plural, lc_string);
+ }
+ }
+
+ /* If best_cp->msgid_plural equals to best_cp->msgid,
+ the ownership will be transferred to
+ remember_a_message before it is passed to
+ remember_a_message_plural.
+
+ Make a copy of the string in that case. */
+ if (best_cp->msgid_plural == best_cp->msgid)
+ best_cp->msgid_plural = xstrdup (best_cp->msgid);
+ }
+
+ if (best_cp->msgid_comment != NULL)
+ {
+ refcounted_string_list_ty *msgid_comment =
+ savable_comment_convert_encoding (best_cp->msgid_comment,
+ &best_cp->msgid_pos);
+ drop_reference (best_cp->msgid_comment);
+ best_cp->msgid_comment = msgid_comment;
+ }
+
+ /* best_cp->msgctxt, best_cp->msgid, and best_cp->msgid_plural
+ are already in UTF-8. Prevent further conversion in
+ remember_a_message. */
+ encoding = xgettext_current_source_encoding;
+ xgettext_current_source_encoding = po_charset_utf8;
+ 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);
+ xgettext_current_source_encoding = encoding;
+ }
+
+ 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)
+{
+ 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;
+}
+
+
+static message_ty *
+construct_header ()
+{
+ char *project_id_version;
+ time_t now;
+ char *timestring;
+ message_ty *mp;
+ char *msgstr;
+ char *comment;
+ static lex_pos_ty pos = { __FILE__, __LINE__ };
+
+ if (package_name != NULL)
+ {
+ if (package_version != NULL)
+ project_id_version = xasprintf ("%s %s", package_name, package_version);
+ else
+ project_id_version = xasprintf ("%s", package_name);
+ }
+ else
+ project_id_version = xstrdup ("PACKAGE VERSION");
+
+ if (msgid_bugs_address != NULL && msgid_bugs_address[0] == '\0')
+ multiline_warning (xasprintf (_("warning: ")),
+ xstrdup (_("\
+The option --msgid-bugs-address was not specified.\n\
+If you are using a 'Makevars' file, please specify\n\
+the MSGID_BUGS_ADDRESS variable there; otherwise please\n\
+specify an --msgid-bugs-address command line option.\n\
+")));
+
+ time (&now);
+ timestring = po_strftime (&now);
+
+ msgstr = xasprintf ("\
+Project-Id-Version: %s\n\
+Report-Msgid-Bugs-To: %s\n\
+POT-Creation-Date: %s\n\
+PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\
+Last-Translator: FULL NAME <EMAIL@ADDRESS>\n\
+Language-Team: LANGUAGE <LL@li.org>\n\
+Language: \n\
+MIME-Version: 1.0\n\
+Content-Type: text/plain; charset=CHARSET\n\
+Content-Transfer-Encoding: 8bit\n",
+ project_id_version,
+ msgid_bugs_address != NULL ? msgid_bugs_address : "",
+ timestring);
+ free (timestring);
+ free (project_id_version);
+
+ mp = message_alloc (NULL, "", NULL, msgstr, strlen (msgstr) + 1, &pos);
+
+ if (copyright_holder[0] != '\0')
+ comment = xasprintf ("\
+SOME DESCRIPTIVE TITLE.\n\
+Copyright (C) YEAR %s\n\
+This file is distributed under the same license as the PACKAGE package.\n\
+FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n",
+ copyright_holder);
+ else
+ comment = xstrdup ("\
+SOME DESCRIPTIVE TITLE.\n\
+This file is put in the public domain.\n\
+FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n");
+ message_comment_append (mp, comment);
+ free (comment);
+
+ mp->is_fuzzy = true;
+
+ return mp;
+}
+
+static void
+finalize_header (msgdomain_list_ty *mdlp)
+{
+ /* If the generated PO file has plural forms, add a Plural-Forms template
+ to the constructed header. */
+ {
+ bool has_plural;
+ size_t i, j;
+
+ has_plural = false;
+ for (i = 0; i < mdlp->nitems; i++)
+ {
+ message_list_ty *mlp = mdlp->item[i]->messages;
+
+ for (j = 0; j < mlp->nitems; j++)
+ {
+ message_ty *mp = mlp->item[j];
+
+ if (mp->msgid_plural != NULL)
+ {
+ has_plural = true;
+ break;
+ }
+ }
+ if (has_plural)
+ break;
+ }
+
+ if (has_plural)
+ {
+ message_ty *header =
+ message_list_search (mdlp->item[0]->messages, NULL, "");
+ if (header != NULL
+ && c_strstr (header->msgstr, "Plural-Forms:") == NULL)
+ {
+ size_t insertpos = strlen (header->msgstr);
+ const char *suffix;
+ size_t suffix_len;
+ char *new_msgstr;
+
+ suffix = "\nPlural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n";
+ if (insertpos == 0 || header->msgstr[insertpos-1] == '\n')
+ suffix++;
+ suffix_len = strlen (suffix);
+ new_msgstr = XNMALLOC (header->msgstr_len + suffix_len, char);
+ memcpy (new_msgstr, header->msgstr, insertpos);
+ memcpy (new_msgstr + insertpos, suffix, suffix_len);
+ memcpy (new_msgstr + insertpos + suffix_len,
+ header->msgstr + insertpos,
+ header->msgstr_len - insertpos);
+ header->msgstr = new_msgstr;
+ header->msgstr_len = header->msgstr_len + suffix_len;
+ }
+ }
+ }
+
+ /* If not all the strings were plain ASCII, or if the output syntax
+ requires a charset conversion, set the charset in the header to UTF-8.
+ All messages have already been converted to UTF-8 in remember_a_message
+ and remember_a_message_plural. */
+ {
+ bool has_nonascii = false;
+ size_t i;
+
+ for (i = 0; i < mdlp->nitems; i++)
+ {
+ message_list_ty *mlp = mdlp->item[i]->messages;
+
+ if (!is_ascii_message_list (mlp))
+ has_nonascii = true;
+ }
+
+ if (has_nonascii || output_syntax->requires_utf8)
+ {
+ message_list_ty *mlp = mdlp->item[0]->messages;
+
+ iconv_message_list (mlp, po_charset_utf8, po_charset_utf8, NULL);
+ }
+ }
+}
+
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+#define ENDOF(a) ((a) + SIZEOF(a))
+
+
+static extractor_ty
+language_to_extractor (const char *name)
+{
+ struct table_ty
+ {
+ const char *name;
+ extractor_func func;
+ flag_context_list_table_ty *flag_table;
+ struct formatstring_parser *formatstring_parser1;
+ struct formatstring_parser *formatstring_parser2;
+ struct literalstring_parser *literalstring_parser;
+ };
+ typedef struct table_ty table_ty;
+
+ static table_ty table[] =
+ {
+ SCANNERS_C
+ SCANNERS_PO
+ SCANNERS_SH
+ SCANNERS_PYTHON
+ SCANNERS_LISP
+ SCANNERS_ELISP
+ SCANNERS_LIBREP
+ SCANNERS_SCHEME
+ SCANNERS_SMALLTALK
+ SCANNERS_JAVA
+ SCANNERS_PROPERTIES
+ SCANNERS_CSHARP
+ SCANNERS_AWK
+ SCANNERS_YCP
+ SCANNERS_TCL
+ SCANNERS_PERL
+ SCANNERS_PHP
+ SCANNERS_STRINGTABLE
+ SCANNERS_RST
+ SCANNERS_GLADE
+ SCANNERS_LUA
+ SCANNERS_JAVASCRIPT
+ SCANNERS_VALA
+ SCANNERS_GSETTINGS
+ SCANNERS_DESKTOP
+ /* Here may follow more languages and their scanners: pike, etc...
+ Make sure new scanners honor the --exclude-file option. */
+ };
+
+ table_ty *tp;
+
+ for (tp = table; tp < ENDOF(table); ++tp)
+ if (c_strcasecmp (name, tp->name) == 0)
+ {
+ extractor_ty result;
+
+ result.func = tp->func;
+ result.flag_table = tp->flag_table;
+ result.formatstring_parser1 = tp->formatstring_parser1;
+ result.formatstring_parser2 = tp->formatstring_parser2;
+ result.formatstring_parser3 = NULL;
+ result.literalstring_parser = tp->literalstring_parser;
+
+ /* Handle --qt. It's preferrable to handle this facility here rather
+ than through an option --language=C++/Qt because the latter would
+ conflict with the language "C++" regarding the file extensions. */
+ if (recognize_format_qt && strcmp (tp->name, "C++") == 0)
+ {
+ result.flag_table = &flag_table_cxx_qt;
+ result.formatstring_parser2 = &formatstring_qt;
+ result.formatstring_parser3 = &formatstring_qt_plural;
+ }
+ /* Likewise for --kde. */
+ if (recognize_format_kde && strcmp (tp->name, "C++") == 0)
+ {
+ result.flag_table = &flag_table_cxx_kde;
+ result.formatstring_parser2 = &formatstring_kde;
+ }
+ /* Likewise for --boost. */
+ if (recognize_format_boost && strcmp (tp->name, "C++") == 0)
+ {
+ result.flag_table = &flag_table_cxx_boost;
+ result.formatstring_parser2 = &formatstring_boost;
+ }
+
+ return result;
+ }
+
+ error (EXIT_FAILURE, 0, _("language '%s' unknown"), name);
+ /* NOTREACHED */
+ {
+ extractor_ty result = { NULL, NULL, NULL, NULL };
+ return result;
+ }
+}
+
+
+static const char *
+extension_to_language (const char *extension)
+{
+ struct table_ty
+ {
+ const char *extension;
+ const char *language;
+ };
+ typedef struct table_ty table_ty;
+
+ static table_ty table[] =
+ {
+ EXTENSIONS_C
+ EXTENSIONS_PO
+ EXTENSIONS_SH
+ EXTENSIONS_PYTHON
+ EXTENSIONS_LISP
+ EXTENSIONS_ELISP
+ EXTENSIONS_LIBREP
+ EXTENSIONS_SCHEME
+ EXTENSIONS_SMALLTALK
+ EXTENSIONS_JAVA
+ EXTENSIONS_PROPERTIES
+ EXTENSIONS_CSHARP
+ EXTENSIONS_AWK
+ EXTENSIONS_YCP
+ EXTENSIONS_TCL
+ EXTENSIONS_PERL
+ EXTENSIONS_PHP
+ EXTENSIONS_STRINGTABLE
+ EXTENSIONS_RST
+ EXTENSIONS_GLADE
+ EXTENSIONS_LUA
+ EXTENSIONS_JAVASCRIPT
+ EXTENSIONS_VALA
+ EXTENSIONS_GSETTINGS
+ EXTENSIONS_DESKTOP
+ /* Here may follow more file extensions... */
+ };
+
+ table_ty *tp;
+
+ for (tp = table; tp < ENDOF(table); ++tp)
+ if (strcmp (extension, tp->extension) == 0)
+ return tp->language;
+ return NULL;
+}
diff --git a/gettext-tools/src/xgettext.h b/gettext-tools/src/xgettext.h
new file mode 100644
index 0000000..c852ae3
--- /dev/null
+++ b/gettext-tools/src/xgettext.h
@@ -0,0 +1,416 @@
+/* xgettext common functions.
+ Copyright (C) 2001-2003, 2005-2006, 2008-2009, 2011 Free Software Foundation, Inc.
+ Written by Peter Miller <millerp@canb.auug.org.au>
+ and Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _XGETTEXT_H
+#define _XGETTEXT_H
+
+#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"
+
+
+#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;
+
+
+/* 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);
+
+
+/* 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);
+/* 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;
+
+
+/* Comment handling for backends which support combining adjacent strings
+ even across lines.
+ In these backends we cannot use the xgettext_comment* functions directly,
+ because in multiline string expressions like
+ "string1" +
+ "string2"
+ the newline between "string1" and "string2" would cause a call to
+ xgettext_comment_reset(), thus destroying the accumulated comments
+ that we need a little later, when we have concatenated the two strings
+ and pass them to remember_a_message().
+ Instead, we do the bookkeeping of the accumulated comments directly,
+ and save a pointer to the accumulated comments when we read "string1".
+ 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);
+
+/* Convert character encoding of COMMENT according to the current
+ source encoding. Returns a new refcounted_string_list_ty. */
+extern refcounted_string_list_ty *
+ savable_comment_convert_encoding (refcounted_string_list_ty *comment,
+ lex_pos_ty *pos);
+
+
+enum literalstring_escape_type
+{
+ LET_NONE = 0,
+ LET_ANSI_C = 1 << 0,
+ LET_UNICODE = 1 << 1
+};
+
+struct literalstring_parser
+{
+ char * (*parse) (const char *string, lex_pos_ty *pos,
+ enum literalstring_escape_type type);
+};
+
+/* 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 */
+ enum literalstring_escape_type msgctxt_escape;
+ lex_pos_ty msgctxt_pos;
+ char *msgid; /* msgid - owned string, or NULL */
+ enum literalstring_escape_type msgid_escape;
+ 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 */
+ enum literalstring_escape_type msgid_plural_escape;
+ 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 */
+ 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 an uninterpreted string argument to an arglist_parser. ARGNUM
+ must be > 0.
+ STRING is 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_literal (struct arglist_parser *ap,
+ int argnum, char *string,
+ flag_context_ty context,
+ char *file_name, size_t line_number,
+ refcounted_string_list_ty *comment,
+ enum literalstring_escape_type type);
+/* 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);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _XGETTEXT_H */