From: Bruno Haible Date: Wed, 19 Sep 2001 12:47:28 +0000 (+0000) Subject: msgfmt can now write Java ResourceBundles. X-Git-Tag: v0.11~486 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=77acaedf9485255d3a9f842a9c4e4b2fa34191ce;p=thirdparty%2Fgettext.git msgfmt can now write Java ResourceBundles. --- diff --git a/ChangeLog b/ChangeLog index be67ccc8c..ede7f0108 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2001-09-02 Bruno Haible + + * configure.in: Add check for 'raise'. Call gt_SIGNALBLOCKING. + 2001-09-08 Bruno Haible * configure.in: Call gt_JAVACOMP and check for 'jar'. Define BUILDJAVA. diff --git a/configure.in b/configure.in index 25f3f8353..eba7dced9 100644 --- a/configure.in +++ b/configure.in @@ -66,7 +66,8 @@ AC_TYPE_PID_T dnl Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_VPRINTF -AC_CHECK_FUNCS([getcwd mblen memcpy posix_spawn select strchr strerror uname]) +AC_CHECK_FUNCS([getcwd mblen memcpy posix_spawn raise select strchr strerror \ +uname]) AC_REPLACE_FUNCS([memmove memset stpcpy stpncpy strcspn \ strcasecmp strncasecmp strpbrk strstr strtoul vasprintf]) AM_FUNC_GETLINE @@ -78,6 +79,7 @@ AC_FUNC_VFORK gt_UNION_WAIT gt_TMPDIR gt_FUNC_MKDTEMP +gt_SIGNALBLOCKING gt_FUNC_SETENV AM_FUNC_ERROR_AT_LINE diff --git a/m4/ChangeLog b/m4/ChangeLog index 037c3104c..d4333258a 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,8 @@ +2001-09-02 Bruno Haible + + * signalblocking.m4: New file. + * Makefile.am (EXTRA_DIST): Add it. + 2001-09-08 Bruno Haible * javacomp.m4: New file. diff --git a/m4/Makefile.am b/m4/Makefile.am index 825b22a59..8c0edc9f6 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -10,5 +10,5 @@ EXTRA_DIST = README \ c-bs-a.m4 codeset.m4 flex.m4 getline.m4 gettext.m4 glibc21.m4 iconv.m4 \ inttypes_h.m4 isc-posix.m4 javacomp.m4 javaexec.m4 lcmessage.m4 libtool.m4 \ mbrtowc.m4 mbstate_t.m4 mbswidth.m4 mkdtemp.m4 progtest.m4 setenv.m4 \ -setlocale.m4 signed.m4 ssize_t.m4 stdbool.m4 tmpdir.m4 uintmax_t.m4 \ -ulonglong.m4 unionwait.m4 +setlocale.m4 signalblocking.m4 signed.m4 ssize_t.m4 stdbool.m4 tmpdir.m4 \ +uintmax_t.m4 ulonglong.m4 unionwait.m4 diff --git a/m4/signalblocking.m4 b/m4/signalblocking.m4 new file mode 100644 index 000000000..9b65e2e68 --- /dev/null +++ b/m4/signalblocking.m4 @@ -0,0 +1,17 @@ +#serial 1 + +# Determine available signal blocking primitives. Three different APIs exist: +# 1) POSIX: sigemptyset, sigaddset, sigprocmask +# 2) SYSV: sighold, sigrelse +# 3) BSD: sigblock, sigsetmask +# For simplicity, here we check only for the POSIX signal blocking. +AC_DEFUN([gt_SIGNALBLOCKING], +[ + signals_not_posix= + AC_EGREP_HEADER(sigset_t, signal.h, , signals_not_posix=1) + if test -z "$signals_not_posix"; then + AC_CHECK_FUNC(sigprocmask, + AC_DEFINE(HAVE_POSIX_SIGNALBLOCKING, 1, + [Define to 1 if you have the sigset_t type and the sigprocmask function.])) + fi +]) diff --git a/src/ChangeLog b/src/ChangeLog index 3466614d8..b02aee2ea 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,20 @@ +2001-09-02 Bruno Haible + + * 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 * Makefile.am (noinst_HEADERS): Add x-java.h. diff --git a/src/Makefile.am b/src/Makefile.am index c2b5d51a9..745db1bd0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,7 +46,8 @@ ngettext_SOURCES = ngettext.c msgcmp_SOURCES = message.c msgcmp.c open-po.c po-gram-gen.y po-hash-gen.y \ po-charset.c po-lex.c po.c str-list.c dir-list.c msgfmt_SOURCES = msgfmt.c open-po.c po-gram-gen.y po-hash-gen.y po-charset.c \ -po-lex.c po.c str-list.c message.c dir-list.c write-mo.c \ +po-lex.c po.c str-list.c message.c dir-list.c write-mo.c write-java.c \ +msgl-ascii.c msgl-iconv.c plural.c \ format.c format-c.c format-java.c format-lisp.c format-python.c format-ycp.c msgmerge_SOURCES = message.c msgmerge.c open-po.c po-gram-gen.y po-hash-gen.y \ po-charset.c po-lex.c po.c read-po.c str-list.c dir-list.c write-po.c \ diff --git a/src/msgfmt.c b/src/msgfmt.c index 68d975714..aa2da2b70 100644 --- a/src/msgfmt.c +++ b/src/msgfmt.c @@ -37,6 +37,7 @@ #include "system.h" #include "msgfmt.h" #include "write-mo.h" +#include "write-java.h" #include "libgettext.h" #include "message.h" @@ -70,6 +71,13 @@ static bool include_all = 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; + /* 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. */ @@ -128,8 +136,12 @@ static const struct option long_options[] = { "check-header", no_argument, NULL, CHAR_MAX + 3 }, { "directory", required_argument, NULL, 'D' }, { "help", no_argument, NULL, 'h' }, - { "no-hash", no_argument, NULL, CHAR_MAX + 4 }, + { "java", no_argument, NULL, 'j' }, + { "java2", no_argument, NULL, CHAR_MAX + 4 }, + { "locale", required_argument, NULL, 'l' }, + { "no-hash", no_argument, NULL, CHAR_MAX + 5 }, { "output-file", required_argument, NULL, 'o' }, + { "resource", required_argument, NULL, 'r' }, { "statistics", no_argument, &do_statistics, 1 }, { "strict", no_argument, NULL, 'S' }, { "use-fuzzy", no_argument, NULL, 'f' }, @@ -196,7 +208,8 @@ main (argc, argv) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); - while ((opt = getopt_long (argc, argv, "a:cCD:fho:vV", long_options, NULL)) + while ((opt = getopt_long (argc, argv, "a:cCd:D:fhjl:o:r:vV", long_options, + NULL)) != EOF) switch (opt) { @@ -219,6 +232,9 @@ main (argc, argv) case 'C': check_compatibility = true; break; + case 'd': + java_class_directory = optarg; + break; case 'D': dir_list_append (optarg); break; @@ -228,9 +244,18 @@ main (argc, argv) case 'h': do_help = true; break; + case 'j': + java_mode = true; + break; + case 'l': + java_locale_name = optarg; + break; case 'o': output_file_name = optarg; break; + case 'r': + java_resource_name = optarg; + break; case 'S': strict_uniforum = true; break; @@ -250,6 +275,10 @@ main (argc, argv) check_header = true; break; case CHAR_MAX + 4: + java_mode = true; + assume_java2 = true; + break; + case CHAR_MAX + 5: no_hash_table = true; break; default: @@ -282,6 +311,44 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ usage (EXIT_FAILURE); } + /* Check for contradicting options. */ + if (java_mode) + { + if (output_file_name != NULL) + { + error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"), + "--java-mode", "--output-file"); + } + if (java_class_directory == NULL) + { + error (EXIT_SUCCESS, 0, + _("%s requires a \"-d directory\" specification"), + "--java-mode"); + usage (EXIT_FAILURE); + } + } + else + { + if (java_resource_name != NULL) + { + error (EXIT_SUCCESS, 0, _("%s is only valid with %s"), + "--resource", "--java-mode"); + usage (EXIT_FAILURE); + } + if (java_locale_name != NULL) + { + error (EXIT_SUCCESS, 0, _("%s is only valid with %s"), + "--locale", "--java-mode"); + usage (EXIT_FAILURE); + } + if (java_class_directory != NULL) + { + error (EXIT_SUCCESS, 0, _("%s is only valid with %s"), + "-d", "--java-mode"); + usage (EXIT_FAILURE); + } + } + /* The -o option determines the name of the domain and therefore the output file. */ if (output_file_name != NULL) @@ -311,9 +378,19 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ for (domain = domain_list; domain != NULL; domain = domain->next) { - if (msgdomain_write_mo (domain->mlp, domain->domain_name, - domain->file_name)) - exit_status = EXIT_FAILURE; + if (java_mode) + { + if (msgdomain_write_java (domain->mlp, java_resource_name, + java_locale_name, java_class_directory, + assume_java2)) + exit_status = EXIT_FAILURE; + } + 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); @@ -377,6 +454,13 @@ Input file location:\n\ filename.po ... input files\n\ -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n\ If input file is -, standard input is read.\n\ +")); + printf ("\n"); + /* xgettext: no-wrap */ + printf (_("\ +Operation mode:\n\ + -j, --java Java mode: generate a Java ResourceBundle class\n\ + --java2 like --java, and assume Java2 (JDK 1.2 or higher)\n\ ")); printf ("\n"); /* xgettext: no-wrap */ @@ -385,6 +469,17 @@ Output file location:\n\ -o, --output-file=FILE write output to specified file\n\ --strict enable strict Uniforum mode\n\ If output file is -, output is written to standard output.\n\ +")); + printf ("\n"); + /* xgettext: no-wrap */ + printf (_("\ +Output file location in Java mode:\n\ + -r, --resource=RESOURCE resource name\n\ + -l, --locale=LOCALE locale name, either language or language_COUNTRY\n\ + -d DIRECTORY base directory of classes directory hierarchy\n\ +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"); /* xgettext: no-wrap */ @@ -684,7 +779,7 @@ format_directive_domain (pop, name) { /* If no output file was given, we change it with each `domain' directive. */ - if (output_file_name == NULL) + if (!java_mode && output_file_name == NULL) { size_t correct; diff --git a/src/plural.c b/src/plural.c new file mode 100644 index 000000000..69f4046d2 --- /dev/null +++ b/src/plural.c @@ -0,0 +1,22 @@ +/* Expression parsing for plural form selection. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Ulrich Drepper , 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 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Include the expression parsing code from libintl, with different function + names. */ +#include "../intl/plural.c" +#include "../intl/plural-exp.c" diff --git a/src/write-java.c b/src/write-java.c new file mode 100644 index 000000000..04d006ea8 --- /dev/null +++ b/src/write-java.c @@ -0,0 +1,1252 @@ +/* Writing Java ResourceBundles. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 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 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* Specification. */ +#include "write-java.h" + +#include +#include +#include +#include +#include +#include + +#include +#if STAT_MACROS_BROKEN +# undef S_ISDIR +#endif +#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 HAVE_UNISTD_H +# include +#endif + +#include "c-ctype.h" +#include "error.h" +#include "javacomp.h" +#include "message.h" +#include "mkdtemp.h" +#include "msgfmt.h" +#include "msgl-iconv.h" +#include "pathmax.h" +#include "plural-exp.h" +#include "po-charset.h" +#include "system.h" +#include "tmpdir.h" +#include "utf8-ucs4.h" +#include "libgettext.h" + +#define _(str) gettext (str) + + +/* Prototypes for local functions. Needed to ensure compiler checking of + function argument counts despite of K&R C function definition syntax. */ +static int check_resource_name PARAMS ((const char *name)); +static unsigned int string_hashcode PARAMS ((const char *str)); +static unsigned int compute_hashsize PARAMS ((message_list_ty *mlp, + bool *collisionp)); +static int compare_index PARAMS ((const void *pval1, const void *pval2)); +static struct table_item * compute_table_items PARAMS ((message_list_ty *mlp, + unsigned int hashsize)); +static void write_java_string PARAMS ((FILE *stream, const char *str)); +static void write_java_msgstr PARAMS ((FILE *stream, message_ty *mp)); +static void write_lookup_code PARAMS ((FILE *stream, unsigned int hashsize, + bool collisions)); +static bool is_expression_boolean PARAMS ((struct expression *exp)); +static void write_java_expression PARAMS ((FILE *stream, + struct expression *exp, + bool as_boolean)); +static void write_java_code PARAMS ((FILE *stream, const char *class_name, + message_list_ty *mlp, bool assume_java2)); +static void uninstall_handlers PARAMS ((void)); +static void cleanup PARAMS ((int sig)); +static void install_handlers PARAMS ((void)); +#if HAVE_POSIX_SIGNALBLOCKING +static void init_signal_set PARAMS ((void)); +static void block PARAMS ((void)); +static void unblock PARAMS ((void)); +#endif + + +/* 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 (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 (str) + const char *str; +{ + const char *str_limit = str + strlen (str); + int hash = 0; + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, str, str_limit - str); + if (uc < 0x10000) + /* Single UCS-2 'char'. */ + hash = 31 * hash + uc; + else + { + /* UTF-16 surrogate: two 'char's. */ + unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10); + unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff); + hash = 31 * hash + uc1; + hash = 31 * hash + uc2; + } + } + return hash & 0x7fffffff; +} + + +/* Compute a good hash table size for the given set of msgids. */ +static unsigned int +compute_hashsize (mlp, collisionp) + 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 *) alloca (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] = string_hashcode (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 = (char *) xmalloc (hashsize); + 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 (); + + /* 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 (pval1, pval2) + 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 (mlp, hashsize) + message_list_ty *mlp; + unsigned int hashsize; +{ + unsigned int n = mlp->nitems; + struct table_item *arr = + (struct table_item *) xmalloc (n * sizeof (struct table_item)); + char *bitmap; + size_t j; + + bitmap = (char *) xmalloc (hashsize); + memset (bitmap, 0, hashsize); + + for (j = 0; j < n; j++) + { + unsigned int hashcode = string_hashcode (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 (stream, str) + FILE *stream; + const char *str; +{ + static const char hexdigit[16] = "0123456789abcdef"; + const char *str_limit = str + strlen (str); + + fprintf (stream, "\""); + while (str < str_limit) + { + unsigned int uc; + str += u8_mbtouc (&uc, 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", 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. */ + unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10); + unsigned int 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 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 (stream, mp) + 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 (stream, hashsize, collisions) + 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 (exp) + 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 (stream, exp, as_boolean) + FILE *stream; + 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 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 (stream, class_name, mlp, assume_java2) + 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); + fprintf (stream, " static {\n"); + fprintf (stream, " %s[] t = new %s[%d];\n", table_eltype, table_eltype, + 2 * hashsize); + for (j = 0; j < mlp->nitems; j++) + { + struct table_item *ti = &table_items[j]; + + fprintf (stream, " t[%d] = ", 2 * ti->index); + write_java_string (stream, ti->mp->msgid); + fprintf (stream, ";\n"); + fprintf (stream, " t[%d] = ", 2 * ti->index + 1); + write_java_msgstr (stream, ti->mp); + fprintf (stream, ";\n"); + } + 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[] plural = 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"); + } + + 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"); + fprintf (stream, " static {\n"); + fprintf (stream, " java.util.Hashtable t = new java.util.Hashtable();\n"); + for (j = 0; j < mlp->nitems; j++) + { + fprintf (stream, " t.put("); + write_java_string (stream, mlp->item[j]->msgid); + fprintf (stream, ","); + write_java_msgstr (stream, mlp->item[j]); + fprintf (stream, ");\n"); + } + 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 plural;\n"); + fprintf (stream, " static {\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_string (stream, mlp->item[j]->msgid); + fprintf (stream, ","); + write_java_string (stream, mlp->item[j]->msgid_plural); + fprintf (stream, ");\n"); + } + fprintf (stream, " plural = 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; + struct expression *plural; + unsigned long int nplurals; + + header_entry = message_list_search (mlp, ""); + 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"); +} + + +/* Asynchronously cleaning up temporary files, when we receive any of the + usually occurring signals whose default action is to terminate the + program. */ + +static struct +{ + const char *tmpdir; + unsigned int subdir_count; + const char * const *subdir; + const char *file_name; +} cleanup_list; + +static void +uninstall_handlers () +{ +#ifdef SIGHUP + signal (SIGHUP, SIG_DFL); +#endif +#ifdef SIGINT + signal (SIGINT, SIG_DFL); +#endif +#ifdef SIGPIPE + signal (SIGPIPE, SIG_DFL); +#endif +#ifdef SIGTERM + signal (SIGTERM, SIG_DFL); +#endif +} + +/* The signal handler. It gets called asynchronously. */ +static void +cleanup (sig) + int sig; +{ + unsigned int i; + + /* First cleanup the files in the subdirectory. */ + { + const char *filename = cleanup_list.file_name; + + if (filename != NULL) + unlink (filename); + } + + /* Then cleanup the subdirectories. */ + for (i = cleanup_list.subdir_count; i > 0; ) + { + const char *filename = cleanup_list.subdir[--i]; + + rmdir (filename); + } + + /* Then cleanup the main temporary directory. */ + { + const char *filename = cleanup_list.tmpdir; + + if (filename != NULL) + rmdir (filename); + } + + /* Now execute the signal's default action. */ + uninstall_handlers (); +#if HAVE_RAISE + raise (sig); +#else + kill (getpid (), sig); +#endif +} + +static void +install_handlers () +{ +#ifdef SIGHUP + signal (SIGHUP, &cleanup); +#endif +#ifdef SIGINT + signal (SIGINT, &cleanup); +#endif +#ifdef SIGPIPE + signal (SIGPIPE, &cleanup); +#endif +#ifdef SIGTERM + signal (SIGTERM, &cleanup); +#endif +} + +#if HAVE_POSIX_SIGNALBLOCKING + +static sigset_t signal_set; + +static void +init_signal_set () +{ + static bool signal_set_initialized = false; + if (!signal_set_initialized) + { + sigemptyset (&signal_set); + +#ifdef SIGHUP + sigaddset (&signal_set, SIGHUP); +#endif +#ifdef SIGINT + sigaddset (&signal_set, SIGINT); +#endif +#ifdef SIGPIPE + sigaddset (&signal_set, SIGPIPE); +#endif +#ifdef SIGTERM + sigaddset (&signal_set, SIGTERM); +#endif + + signal_set_initialized = true; + } +} + +static void +block () +{ + init_signal_set (); + sigprocmask (SIG_BLOCK, &signal_set, NULL); +} + +static void +unblock () +{ + init_signal_set (); + sigprocmask (SIG_UNBLOCK, &signal_set, NULL); +} + +#else + +#define block() /* nothing */ +#define unblock() /* nothing */ + +#endif + + +int +msgdomain_write_java (mlp, resource_name, locale_name, directory, assume_java2) + message_list_ty *mlp; + const char *resource_name; + const char *locale_name; + const char *directory; + bool assume_java2; +{ + int retval; + char *template; + char *tmpdir; + int ndots; + char *class_name; + char **subdirs; + char *java_file_name; + FILE *java_file; + const char *java_sources[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, po_charset_canonicalize ("UTF-8")); + + cleanup_list.tmpdir = NULL; + cleanup_list.subdir_count = 0; + cleanup_list.file_name = NULL; + install_handlers (); + + /* Create a temporary directory where we can put the Java file. */ + template = (char *) alloca (PATH_MAX); + if (path_search (template, PATH_MAX, NULL, "msg", 1)) + { + error (0, errno, + _("cannot find a temporary directory, try setting $TMPDIR")); + goto quit1; + } + block (); + tmpdir = mkdtemp (template); + cleanup_list.tmpdir = tmpdir; + unblock (); + if (tmpdir == NULL) + { + error (0, errno, + _("cannot create a temporary directory using template \"%s\""), + template); + goto quit1; + } + + /* 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 = + (char *) xmalloc (strlen (resource_name) + 1 + strlen (locale_name) + 1); + sprintf (class_name, "%s_%s", resource_name, locale_name); + } + else + class_name = xstrdup (resource_name); + + subdirs = (ndots > 0 ? (char **) alloca (ndots * sizeof (char *)) : NULL); + { + const char *p; + const char *last_dir; + int i; + + last_dir = tmpdir; + p = resource_name; + for (i = 0; i < ndots; i++) + { + const char *q = strchr (p, '.'); + size_t n = q - p; + char *part = (char *) alloca (n + 1); + memcpy (part, p, n); + part[n] = '\0'; + subdirs[i] = concatenated_pathname (last_dir, part, NULL); + last_dir = subdirs[i]; + p = q + 1; + } + + if (locale_name != NULL) + { + char *suffix = (char *) xmalloc (1 + strlen (locale_name) + 5 + 1); + sprintf (suffix, "_%s.java", locale_name); + java_file_name = concatenated_pathname (last_dir, p, suffix); + free (suffix); + } + else + java_file_name = concatenated_pathname (last_dir, p, ".java"); + } + + /* 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; + + cleanup_list.subdir = (const char * const *) subdirs; + cleanup_list.subdir_count = 0; + for (i = 0; i < ndots; i++) + { + cleanup_list.subdir_count = i + 1; + if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0) + { + error (0, errno, _("failed to create \"%s\""), subdirs[i]); + while (i-- > 0) + rmdir (subdirs[i]); + goto quit3; + } + } + } + + /* Create the Java file. */ + cleanup_list.file_name = java_file_name; + java_file = fopen (java_file_name, "w"); + if (java_file == NULL) + { + error (0, errno, _("failed to create \"%s\""), java_file_name); + goto quit4; + } + + write_java_code (java_file, class_name, mlp, assume_java2); + + if (fflush (java_file) || ferror (java_file)) + { + error (0, errno, _("error while writing \"%s\" file"), java_file_name); + fclose (java_file); + goto quit5; + } + fclose (java_file); + + /* 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, directory, true, false, + true, verbose)) + { + error (0, 0, _("\ +compilation of Java class failed, please try --verbose or set $JAVAC")); + goto quit5; + } + + retval = 0; + + quit5: + unlink (java_file_name); + quit4: + cleanup_list.file_name = NULL; + { + int i; + for (i = ndots; i-- > 0; ) + rmdir (subdirs[i]); + } + quit3: + cleanup_list.subdir_count = 0; + { + int i; + free (java_file_name); + for (i = 0; i < ndots; i++) + free (subdirs[i]); + } + free (class_name); + quit2: + rmdir (tmpdir); + quit1: + cleanup_list.tmpdir = NULL; + uninstall_handlers (); + return retval; +} diff --git a/src/write-java.h b/src/write-java.h new file mode 100644 index 000000000..3cec77452 --- /dev/null +++ b/src/write-java.h @@ -0,0 +1,38 @@ +/* Writing Java ResourceBundles. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 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 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _WRITE_JAVA_H +#define _WRITE_JAVA_H + +#include + +#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 PARAMS ((message_list_ty *mlp, + const char *resource_name, + const char *locale_name, + const char *directory, + bool assume_java2)); + +#endif /* _WRITE_JAVA_H */