No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / write-java.c
blob2d3933b6b23719224a67455d0b602a3e3c8d0582
1 /* Writing Java ResourceBundles.
2 Copyright (C) 2001-2003, 2005 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <alloca.h>
24 /* Specification. */
25 #include "write-java.h"
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
34 #include <sys/stat.h>
35 #if STAT_MACROS_BROKEN
36 # undef S_ISDIR
37 #endif
38 #if !defined S_ISDIR && defined S_IFDIR
39 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
40 #endif
41 #if !S_IRUSR && S_IREAD
42 # define S_IRUSR S_IREAD
43 #endif
44 #if !S_IRUSR
45 # define S_IRUSR 00400
46 #endif
47 #if !S_IWUSR && S_IWRITE
48 # define S_IWUSR S_IWRITE
49 #endif
50 #if !S_IWUSR
51 # define S_IWUSR 00200
52 #endif
53 #if !S_IXUSR && S_IEXEC
54 # define S_IXUSR S_IEXEC
55 #endif
56 #if !S_IXUSR
57 # define S_IXUSR 00100
58 #endif
60 #if HAVE_UNISTD_H
61 # include <unistd.h>
62 #endif
64 #ifdef __MINGW32__
65 /* mingw's mkdir() function has 1 argument, but we pass 2 arguments.
66 Therefore we have to disable the argument count checking. */
67 # define mkdir ((int (*)()) mkdir)
68 #endif
70 #include "c-ctype.h"
71 #include "error.h"
72 #include "javacomp.h"
73 #include "message.h"
74 #include "mkdtemp.h"
75 #include "msgfmt.h"
76 #include "msgl-iconv.h"
77 #include "pathmax.h"
78 #include "plural-exp.h"
79 #include "po-charset.h"
80 #include "xalloc.h"
81 #include "xallocsa.h"
82 #include "pathname.h"
83 #include "fatal-signal.h"
84 #include "fwriteerror.h"
85 #include "tmpdir.h"
86 #include "utf8-ucs4.h"
87 #include "gettext.h"
89 #define _(str) gettext (str)
92 /* Check that the resource name is a valid Java class name. To simplify
93 things, we allow only ASCII characters in the class name.
94 Return the number of dots in the class name, or -1 if not OK. */
95 static int
96 check_resource_name (const char *name)
98 int ndots = 0;
99 const char *p = name;
101 for (;;)
103 /* First character, see Character.isJavaIdentifierStart. */
104 if (!(c_isalpha (*p) || (*p == '$') || (*p == '_')))
105 return -1;
106 /* Following characters, see Character.isJavaIdentifierPart. */
108 p++;
109 while (c_isalpha (*p) || (*p == '$') || (*p == '_') || c_isdigit (*p));
110 if (*p == '\0')
111 break;
112 if (*p != '.')
113 return -1;
114 p++;
115 ndots++;
117 return ndots;
121 /* Return the Java hash code of a string mod 2^31.
122 The Java String.hashCode() function returns the same values across
123 Java implementations.
124 (See http://www.javasoft.com/docs/books/jls/clarify.html)
125 It returns a signed 32-bit integer. We add a mod 2^31 afterwards;
126 this removes one bit but greatly simplifies the following "mod hash_size"
127 and "mod (hash_size - 2)" operations. */
128 static unsigned int
129 string_hashcode (const char *str)
131 const char *str_limit = str + strlen (str);
132 int hash = 0;
133 while (str < str_limit)
135 unsigned int uc;
136 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
137 if (uc < 0x10000)
138 /* Single UCS-2 'char'. */
139 hash = 31 * hash + uc;
140 else
142 /* UTF-16 surrogate: two 'char's. */
143 unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
144 unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
145 hash = 31 * hash + uc1;
146 hash = 31 * hash + uc2;
149 return hash & 0x7fffffff;
153 /* Compute a good hash table size for the given set of msgids. */
154 static unsigned int
155 compute_hashsize (message_list_ty *mlp, bool *collisionp)
157 /* This is an O(n^2) algorithm, but should be sufficient because few
158 programs have more than 1000 messages in a single domain. */
159 #define XXN 3 /* can be tweaked */
160 #define XXS 3 /* can be tweaked */
161 unsigned int n = mlp->nitems;
162 unsigned int *hashcodes =
163 (unsigned int *) xallocsa (n * sizeof (unsigned int));
164 unsigned int hashsize;
165 unsigned int best_hashsize;
166 unsigned int best_score;
167 size_t j;
169 for (j = 0; j < n; j++)
170 hashcodes[j] = string_hashcode (mlp->item[j]->msgid);
172 /* Try all numbers between n and 3*n. The score depends on the size of the
173 table -- the smaller the better -- and the number of collision lookups,
174 i.e. total number of times that 1 + (hashcode % (hashsize - 2))
175 is added to the index during lookup. If there are collisions, only odd
176 hashsize values are allowed. */
177 best_hashsize = 0;
178 best_score = UINT_MAX;
179 for (hashsize = n; hashsize <= XXN * n; hashsize++)
181 char *bitmap;
182 unsigned int score;
184 /* Premature end of the loop if all future scores are known to be
185 larger than the already reached best_score. This relies on the
186 ascending loop and on the fact that score >= hashsize. */
187 if (hashsize >= best_score)
188 break;
190 bitmap = (char *) xmalloc (hashsize);
191 memset (bitmap, 0, hashsize);
193 score = 0;
194 for (j = 0; j < n; j++)
196 unsigned int idx = hashcodes[j] % hashsize;
198 if (bitmap[idx] != 0)
200 /* Collision. Cannot deal with it if hashsize is even. */
201 if ((hashsize % 2) == 0)
202 /* Try next hashsize. */
203 goto bad_hashsize;
204 else
206 unsigned int idx0 = idx;
207 unsigned int incr = 1 + (hashcodes[j] % (hashsize - 2));
208 score += 2; /* Big penalty for the additional division */
211 score++; /* Small penalty for each loop round */
212 idx += incr;
213 if (idx >= hashsize)
214 idx -= hashsize;
215 if (idx == idx0)
216 /* Searching for a hole, we performed a whole round
217 across the table. This happens particularly
218 frequently if gcd(hashsize,incr) > 1. Try next
219 hashsize. */
220 goto bad_hashsize;
222 while (bitmap[idx] != 0);
225 bitmap[idx] = 1;
228 /* Big hashsize also gives a penalty. */
229 score = XXS * score + hashsize;
231 /* If for any incr between 1 and hashsize - 2, an whole round
232 (idx0, idx0 + incr, ...) is occupied, and the lookup function
233 must deal with collisions, then some inputs would lead to
234 an endless loop in the lookup function. */
235 if (score > hashsize)
237 unsigned int incr;
239 /* Since the set { idx0, idx0 + incr, ... } depends only on idx0
240 and gcd(hashsize,incr), we only need to conside incr that
241 divides hashsize. */
242 for (incr = 1; incr <= hashsize / 2; incr++)
243 if ((hashsize % incr) == 0)
245 unsigned int idx0;
247 for (idx0 = 0; idx0 < incr; idx0++)
249 bool full = true;
250 unsigned int idx;
252 for (idx = idx0; idx < hashsize; idx += incr)
253 if (bitmap[idx] == 0)
255 full = false;
256 break;
258 if (full)
259 /* A whole round is occupied. */
260 goto bad_hashsize;
265 if (false)
266 bad_hashsize:
267 score = UINT_MAX;
269 free (bitmap);
271 if (score < best_score)
273 best_score = score;
274 best_hashsize = hashsize;
277 if (best_hashsize == 0 || best_score < best_hashsize)
278 abort ();
280 freesa (hashcodes);
282 /* There are collisions if and only if best_score > best_hashsize. */
283 *collisionp = (best_score > best_hashsize);
284 return best_hashsize;
288 struct table_item { unsigned int index; message_ty *mp; };
290 static int
291 compare_index (const void *pval1, const void *pval2)
293 return (int)((const struct table_item *) pval1)->index
294 - (int)((const struct table_item *) pval2)->index;
297 /* Compute the list of messages and table indices, sorted according to the
298 indices. */
299 static struct table_item *
300 compute_table_items (message_list_ty *mlp, unsigned int hashsize)
302 unsigned int n = mlp->nitems;
303 struct table_item *arr =
304 (struct table_item *) xmalloc (n * sizeof (struct table_item));
305 char *bitmap;
306 size_t j;
308 bitmap = (char *) xmalloc (hashsize);
309 memset (bitmap, 0, hashsize);
311 for (j = 0; j < n; j++)
313 unsigned int hashcode = string_hashcode (mlp->item[j]->msgid);
314 unsigned int idx = hashcode % hashsize;
316 if (bitmap[idx] != 0)
318 unsigned int incr = 1 + (hashcode % (hashsize - 2));
321 idx += incr;
322 if (idx >= hashsize)
323 idx -= hashsize;
325 while (bitmap[idx] != 0);
327 bitmap[idx] = 1;
329 arr[j].index = idx;
330 arr[j].mp = mlp->item[j];
333 free (bitmap);
335 qsort (arr, n, sizeof (arr[0]), compare_index);
337 return arr;
341 /* Write a string in Java Unicode notation to the given stream. */
342 static void
343 write_java_string (FILE *stream, const char *str)
345 static const char hexdigit[] = "0123456789abcdef";
346 const char *str_limit = str + strlen (str);
348 fprintf (stream, "\"");
349 while (str < str_limit)
351 unsigned int uc;
352 str += u8_mbtouc (&uc, (const unsigned char *) str, str_limit - str);
353 if (uc < 0x10000)
355 /* Single UCS-2 'char'. */
356 if (uc == 0x000a)
357 fprintf (stream, "\\n");
358 else if (uc == 0x000d)
359 fprintf (stream, "\\r");
360 else if (uc == 0x0022)
361 fprintf (stream, "\\\"");
362 else if (uc == 0x005c)
363 fprintf (stream, "\\\\");
364 else if (uc >= 0x0020 && uc < 0x007f)
365 fprintf (stream, "%c", uc);
366 else
367 fprintf (stream, "\\u%c%c%c%c",
368 hexdigit[(uc >> 12) & 0x0f], hexdigit[(uc >> 8) & 0x0f],
369 hexdigit[(uc >> 4) & 0x0f], hexdigit[uc & 0x0f]);
371 else
373 /* UTF-16 surrogate: two 'char's. */
374 unsigned int uc1 = 0xd800 + ((uc - 0x10000) >> 10);
375 unsigned int uc2 = 0xdc00 + ((uc - 0x10000) & 0x3ff);
376 fprintf (stream, "\\u%c%c%c%c",
377 hexdigit[(uc1 >> 12) & 0x0f], hexdigit[(uc1 >> 8) & 0x0f],
378 hexdigit[(uc1 >> 4) & 0x0f], hexdigit[uc1 & 0x0f]);
379 fprintf (stream, "\\u%c%c%c%c",
380 hexdigit[(uc2 >> 12) & 0x0f], hexdigit[(uc2 >> 8) & 0x0f],
381 hexdigit[(uc2 >> 4) & 0x0f], hexdigit[uc2 & 0x0f]);
384 fprintf (stream, "\"");
388 /* Write Java code that returns the value for a message. If the message
389 has plural forms, it is an expression of type String[], otherwise it is
390 an expression of type String. */
391 static void
392 write_java_msgstr (FILE *stream, message_ty *mp)
394 if (mp->msgid_plural != NULL)
396 bool first;
397 const char *p;
399 fprintf (stream, "new java.lang.String[] { ");
400 for (p = mp->msgstr, first = true;
401 p < mp->msgstr + mp->msgstr_len;
402 p += strlen (p) + 1, first = false)
404 if (!first)
405 fprintf (stream, ", ");
406 write_java_string (stream, p);
408 fprintf (stream, " }");
410 else
412 if (mp->msgstr_len != strlen (mp->msgstr) + 1)
413 abort ();
415 write_java_string (stream, mp->msgstr);
420 /* Writes the body of the function which returns the local value for a key
421 named 'msgid'. */
422 static void
423 write_lookup_code (FILE *stream, unsigned int hashsize, bool collisions)
425 fprintf (stream, " int hash_val = msgid.hashCode() & 0x7fffffff;\n");
426 fprintf (stream, " int idx = (hash_val %% %d) << 1;\n", hashsize);
427 if (collisions)
429 fprintf (stream, " {\n");
430 fprintf (stream, " java.lang.Object found = table[idx];\n");
431 fprintf (stream, " if (found == null)\n");
432 fprintf (stream, " return null;\n");
433 fprintf (stream, " if (msgid.equals(found))\n");
434 fprintf (stream, " return table[idx + 1];\n");
435 fprintf (stream, " }\n");
436 fprintf (stream, " int incr = ((hash_val %% %d) + 1) << 1;\n",
437 hashsize - 2);
438 fprintf (stream, " for (;;) {\n");
439 fprintf (stream, " idx += incr;\n");
440 fprintf (stream, " if (idx >= %d)\n", 2 * hashsize);
441 fprintf (stream, " idx -= %d;\n", 2 * hashsize);
442 fprintf (stream, " java.lang.Object found = table[idx];\n");
443 fprintf (stream, " if (found == null)\n");
444 fprintf (stream, " return null;\n");
445 fprintf (stream, " if (msgid.equals(found))\n");
446 fprintf (stream, " return table[idx + 1];\n");
447 fprintf (stream, " }\n");
449 else
451 fprintf (stream, " java.lang.Object found = table[idx];\n");
452 fprintf (stream, " if (found != null && msgid.equals(found))\n");
453 fprintf (stream, " return table[idx + 1];\n");
454 fprintf (stream, " return null;\n");
459 /* Tests whether a plural expression, evaluated according to the C rules,
460 can only produce the values 0 and 1. */
461 static bool
462 is_expression_boolean (struct expression *exp)
464 switch (exp->operation)
466 case var:
467 case mult:
468 case divide:
469 case module:
470 case plus:
471 case minus:
472 return false;
473 case lnot:
474 case less_than:
475 case greater_than:
476 case less_or_equal:
477 case greater_or_equal:
478 case equal:
479 case not_equal:
480 case land:
481 case lor:
482 return true;
483 case num:
484 return (exp->val.num == 0 || exp->val.num == 1);
485 case qmop:
486 return is_expression_boolean (exp->val.args[1])
487 && is_expression_boolean (exp->val.args[2]);
488 default:
489 abort ();
494 /* Write Java code that evaluates a plural expression according to the C rules.
495 The variable is called 'n'. */
496 static void
497 write_java_expression (FILE *stream, struct expression *exp, bool as_boolean)
499 /* We use parentheses everywhere. This frees us from tracking the priority
500 of arithmetic operators. */
501 if (as_boolean)
503 /* Emit a Java expression of type 'boolean'. */
504 switch (exp->operation)
506 case num:
507 fprintf (stream, "%s", exp->val.num ? "true" : "false");
508 return;
509 case lnot:
510 fprintf (stream, "(!");
511 write_java_expression (stream, exp->val.args[0], true);
512 fprintf (stream, ")");
513 return;
514 case less_than:
515 fprintf (stream, "(");
516 write_java_expression (stream, exp->val.args[0], false);
517 fprintf (stream, " < ");
518 write_java_expression (stream, exp->val.args[1], false);
519 fprintf (stream, ")");
520 return;
521 case greater_than:
522 fprintf (stream, "(");
523 write_java_expression (stream, exp->val.args[0], false);
524 fprintf (stream, " > ");
525 write_java_expression (stream, exp->val.args[1], false);
526 fprintf (stream, ")");
527 return;
528 case less_or_equal:
529 fprintf (stream, "(");
530 write_java_expression (stream, exp->val.args[0], false);
531 fprintf (stream, " <= ");
532 write_java_expression (stream, exp->val.args[1], false);
533 fprintf (stream, ")");
534 return;
535 case greater_or_equal:
536 fprintf (stream, "(");
537 write_java_expression (stream, exp->val.args[0], false);
538 fprintf (stream, " >= ");
539 write_java_expression (stream, exp->val.args[1], false);
540 fprintf (stream, ")");
541 return;
542 case equal:
543 fprintf (stream, "(");
544 write_java_expression (stream, exp->val.args[0], false);
545 fprintf (stream, " == ");
546 write_java_expression (stream, exp->val.args[1], false);
547 fprintf (stream, ")");
548 return;
549 case not_equal:
550 fprintf (stream, "(");
551 write_java_expression (stream, exp->val.args[0], false);
552 fprintf (stream, " != ");
553 write_java_expression (stream, exp->val.args[1], false);
554 fprintf (stream, ")");
555 return;
556 case land:
557 fprintf (stream, "(");
558 write_java_expression (stream, exp->val.args[0], true);
559 fprintf (stream, " && ");
560 write_java_expression (stream, exp->val.args[1], true);
561 fprintf (stream, ")");
562 return;
563 case lor:
564 fprintf (stream, "(");
565 write_java_expression (stream, exp->val.args[0], true);
566 fprintf (stream, " || ");
567 write_java_expression (stream, exp->val.args[1], true);
568 fprintf (stream, ")");
569 return;
570 case qmop:
571 if (is_expression_boolean (exp->val.args[1])
572 && is_expression_boolean (exp->val.args[2]))
574 fprintf (stream, "(");
575 write_java_expression (stream, exp->val.args[0], true);
576 fprintf (stream, " ? ");
577 write_java_expression (stream, exp->val.args[1], true);
578 fprintf (stream, " : ");
579 write_java_expression (stream, exp->val.args[2], true);
580 fprintf (stream, ")");
581 return;
583 /*FALLTHROUGH*/
584 case var:
585 case mult:
586 case divide:
587 case module:
588 case plus:
589 case minus:
590 fprintf (stream, "(");
591 write_java_expression (stream, exp, false);
592 fprintf (stream, " != 0)");
593 return;
594 default:
595 abort ();
598 else
600 /* Emit a Java expression of type 'long'. */
601 switch (exp->operation)
603 case var:
604 fprintf (stream, "n");
605 return;
606 case num:
607 fprintf (stream, "%lu", exp->val.num);
608 return;
609 case mult:
610 fprintf (stream, "(");
611 write_java_expression (stream, exp->val.args[0], false);
612 fprintf (stream, " * ");
613 write_java_expression (stream, exp->val.args[1], false);
614 fprintf (stream, ")");
615 return;
616 case divide:
617 fprintf (stream, "(");
618 write_java_expression (stream, exp->val.args[0], false);
619 fprintf (stream, " / ");
620 write_java_expression (stream, exp->val.args[1], false);
621 fprintf (stream, ")");
622 return;
623 case module:
624 fprintf (stream, "(");
625 write_java_expression (stream, exp->val.args[0], false);
626 fprintf (stream, " %% ");
627 write_java_expression (stream, exp->val.args[1], false);
628 fprintf (stream, ")");
629 return;
630 case plus:
631 fprintf (stream, "(");
632 write_java_expression (stream, exp->val.args[0], false);
633 fprintf (stream, " + ");
634 write_java_expression (stream, exp->val.args[1], false);
635 fprintf (stream, ")");
636 return;
637 case minus:
638 fprintf (stream, "(");
639 write_java_expression (stream, exp->val.args[0], false);
640 fprintf (stream, " - ");
641 write_java_expression (stream, exp->val.args[1], false);
642 fprintf (stream, ")");
643 return;
644 case qmop:
645 fprintf (stream, "(");
646 write_java_expression (stream, exp->val.args[0], true);
647 fprintf (stream, " ? ");
648 write_java_expression (stream, exp->val.args[1], false);
649 fprintf (stream, " : ");
650 write_java_expression (stream, exp->val.args[2], false);
651 fprintf (stream, ")");
652 return;
653 case lnot:
654 case less_than:
655 case greater_than:
656 case less_or_equal:
657 case greater_or_equal:
658 case equal:
659 case not_equal:
660 case land:
661 case lor:
662 fprintf (stream, "(");
663 write_java_expression (stream, exp, true);
664 fprintf (stream, " ? 1 : 0)");
665 return;
666 default:
667 abort ();
673 /* Write the Java code for the ResourceBundle subclass to the given stream.
674 Note that we use fully qualified class names and no "import" statements,
675 because applications can have their own classes called X.Y.ResourceBundle
676 or X.Y.String. */
677 static void
678 write_java_code (FILE *stream, const char *class_name, message_list_ty *mlp,
679 bool assume_java2)
681 const char *last_dot;
682 unsigned int plurals;
683 size_t j;
685 fprintf (stream,
686 "/* Automatically generated by GNU msgfmt. Do not modify! */\n");
687 last_dot = strrchr (class_name, '.');
688 if (last_dot != NULL)
690 fprintf (stream, "package ");
691 fwrite (class_name, 1, last_dot - class_name, stream);
692 fprintf (stream, ";\npublic class %s", last_dot + 1);
694 else
695 fprintf (stream, "public class %s", class_name);
696 fprintf (stream, " extends java.util.ResourceBundle {\n");
698 /* Determine whether there are plural messages. */
699 plurals = 0;
700 for (j = 0; j < mlp->nitems; j++)
701 if (mlp->item[j]->msgid_plural != NULL)
702 plurals++;
704 if (assume_java2)
706 unsigned int hashsize;
707 bool collisions;
708 struct table_item *table_items;
709 const char *table_eltype;
711 /* Determine the hash table size and whether it leads to collisions. */
712 hashsize = compute_hashsize (mlp, &collisions);
714 /* Determines which indices in the table contain a message. The others
715 are null. */
716 table_items = compute_table_items (mlp, hashsize);
718 /* Emit the table of pairs (msgid, msgstr). If there are plurals,
719 it is of type Object[], otherwise of type String[]. We use a static
720 code block because that makes less code: The Java compilers also
721 generate code for the 'null' entries, which is dumb. */
722 table_eltype = (plurals ? "java.lang.Object" : "java.lang.String");
723 fprintf (stream, " private static final %s[] table;\n", table_eltype);
724 fprintf (stream, " static {\n");
725 fprintf (stream, " %s[] t = new %s[%d];\n", table_eltype, table_eltype,
726 2 * hashsize);
727 for (j = 0; j < mlp->nitems; j++)
729 struct table_item *ti = &table_items[j];
731 fprintf (stream, " t[%d] = ", 2 * ti->index);
732 write_java_string (stream, ti->mp->msgid);
733 fprintf (stream, ";\n");
734 fprintf (stream, " t[%d] = ", 2 * ti->index + 1);
735 write_java_msgstr (stream, ti->mp);
736 fprintf (stream, ";\n");
738 fprintf (stream, " table = t;\n");
739 fprintf (stream, " }\n");
741 /* Emit the msgid_plural strings. Only used by msgunfmt. */
742 if (plurals)
744 bool first;
745 fprintf (stream, " public static final java.lang.String[] get_msgid_plural_table () {\n");
746 fprintf (stream, " return new java.lang.String[] { ");
747 first = true;
748 for (j = 0; j < mlp->nitems; j++)
750 struct table_item *ti = &table_items[j];
751 if (ti->mp->msgid_plural != NULL)
753 if (!first)
754 fprintf (stream, ", ");
755 write_java_string (stream, ti->mp->msgid_plural);
756 first = false;
759 fprintf (stream, " };\n");
760 fprintf (stream, " }\n");
763 if (plurals)
765 /* Emit the lookup function. It is a common subroutine for
766 handleGetObject and ngettext. */
767 fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
768 write_lookup_code (stream, hashsize, collisions);
769 fprintf (stream, " }\n");
772 /* Emit the handleGetObject function. It is declared abstract in
773 ResourceBundle. It implements a local version of gettext. */
774 fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
775 if (plurals)
777 fprintf (stream, " java.lang.Object value = lookup(msgid);\n");
778 fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
780 else
781 write_lookup_code (stream, hashsize, collisions);
782 fprintf (stream, " }\n");
784 /* Emit the getKeys function. It is declared abstract in ResourceBundle.
785 The inner class is not avoidable. */
786 fprintf (stream, " public java.util.Enumeration getKeys () {\n");
787 fprintf (stream, " return\n");
788 fprintf (stream, " new java.util.Enumeration() {\n");
789 fprintf (stream, " private int idx = 0;\n");
790 fprintf (stream, " { while (idx < %d && table[idx] == null) idx += 2; }\n",
791 2 * hashsize);
792 fprintf (stream, " public boolean hasMoreElements () {\n");
793 fprintf (stream, " return (idx < %d);\n", 2 * hashsize);
794 fprintf (stream, " }\n");
795 fprintf (stream, " public java.lang.Object nextElement () {\n");
796 fprintf (stream, " java.lang.Object key = table[idx];\n");
797 fprintf (stream, " do idx += 2; while (idx < %d && table[idx] == null);\n",
798 2 * hashsize);
799 fprintf (stream, " return key;\n");
800 fprintf (stream, " }\n");
801 fprintf (stream, " };\n");
802 fprintf (stream, " }\n");
804 else
806 /* Java 1.1.x uses a different hash function. If compatibility with
807 this Java version is required, the hash table must be built at run time,
808 not at compile time. */
809 fprintf (stream, " private static final java.util.Hashtable table;\n");
810 fprintf (stream, " static {\n");
811 fprintf (stream, " java.util.Hashtable t = new java.util.Hashtable();\n");
812 for (j = 0; j < mlp->nitems; j++)
814 fprintf (stream, " t.put(");
815 write_java_string (stream, mlp->item[j]->msgid);
816 fprintf (stream, ",");
817 write_java_msgstr (stream, mlp->item[j]);
818 fprintf (stream, ");\n");
820 fprintf (stream, " table = t;\n");
821 fprintf (stream, " }\n");
823 /* Emit the msgid_plural strings. Only used by msgunfmt. */
824 if (plurals)
826 fprintf (stream, " public static final java.util.Hashtable get_msgid_plural_table () {\n");
827 fprintf (stream, " java.util.Hashtable p = new java.util.Hashtable();\n");
828 for (j = 0; j < mlp->nitems; j++)
829 if (mlp->item[j]->msgid_plural != NULL)
831 fprintf (stream, " p.put(");
832 write_java_string (stream, mlp->item[j]->msgid);
833 fprintf (stream, ",");
834 write_java_string (stream, mlp->item[j]->msgid_plural);
835 fprintf (stream, ");\n");
837 fprintf (stream, " return p;\n");
838 fprintf (stream, " }\n");
841 if (plurals)
843 /* Emit the lookup function. It is a common subroutine for
844 handleGetObject and ngettext. */
845 fprintf (stream, " public java.lang.Object lookup (java.lang.String msgid) {\n");
846 fprintf (stream, " return table.get(msgid);\n");
847 fprintf (stream, " }\n");
850 /* Emit the handleGetObject function. It is declared abstract in
851 ResourceBundle. It implements a local version of gettext. */
852 fprintf (stream, " public java.lang.Object handleGetObject (java.lang.String msgid) throws java.util.MissingResourceException {\n");
853 if (plurals)
855 fprintf (stream, " java.lang.Object value = table.get(msgid);\n");
856 fprintf (stream, " return (value instanceof java.lang.String[] ? ((java.lang.String[])value)[0] : value);\n");
858 else
859 fprintf (stream, " return table.get(msgid);\n");
860 fprintf (stream, " }\n");
862 /* Emit the getKeys function. It is declared abstract in
863 ResourceBundle. */
864 fprintf (stream, " public java.util.Enumeration getKeys () {\n");
865 fprintf (stream, " return table.keys();\n");
866 fprintf (stream, " }\n");
869 /* Emit the pluralEval function. It is a subroutine for ngettext. */
870 if (plurals)
872 message_ty *header_entry;
873 struct expression *plural;
874 unsigned long int nplurals;
876 header_entry = message_list_search (mlp, "");
877 extract_plural_expression (header_entry ? header_entry->msgstr : NULL,
878 &plural, &nplurals);
880 fprintf (stream, " public static long pluralEval (long n) {\n");
881 fprintf (stream, " return ");
882 write_java_expression (stream, plural, false);
883 fprintf (stream, ";\n");
884 fprintf (stream, " }\n");
887 /* Emit the getParent function. It is a subroutine for ngettext. */
888 fprintf (stream, " public java.util.ResourceBundle getParent () {\n");
889 fprintf (stream, " return parent;\n");
890 fprintf (stream, " }\n");
892 fprintf (stream, "}\n");
896 /* Asynchronously cleaning up temporary files, when we receive any of the
897 usually occurring signals whose default action is to terminate the
898 program. */
900 static struct
902 const char *tmpdir;
903 unsigned int subdir_count;
904 const char * const *subdir;
905 const char *file_name;
906 } cleanup_list;
908 /* The signal handler. It gets called asynchronously. */
909 static void
910 cleanup ()
912 unsigned int i;
914 /* First cleanup the files in the subdirectory. */
916 const char *filename = cleanup_list.file_name;
918 if (filename != NULL)
919 unlink (filename);
922 /* Then cleanup the subdirectories. */
923 for (i = cleanup_list.subdir_count; i > 0; )
925 const char *filename = cleanup_list.subdir[--i];
927 rmdir (filename);
930 /* Then cleanup the main temporary directory. */
932 const char *filename = cleanup_list.tmpdir;
934 if (filename != NULL)
935 rmdir (filename);
941 msgdomain_write_java (message_list_ty *mlp, const char *canon_encoding,
942 const char *resource_name, const char *locale_name,
943 const char *directory,
944 bool assume_java2)
946 int retval;
947 char *template;
948 char *tmpdir;
949 int ndots;
950 char *class_name;
951 char **subdirs;
952 char *java_file_name;
953 FILE *java_file;
954 const char *java_sources[1];
956 /* If no entry for this resource/domain, don't even create the file. */
957 if (mlp->nitems == 0)
958 return 0;
960 retval = 1;
962 /* Convert the messages to Unicode. */
963 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
965 cleanup_list.tmpdir = NULL;
966 cleanup_list.subdir_count = 0;
967 cleanup_list.file_name = NULL;
969 static bool cleanup_already_registered = false;
970 if (!cleanup_already_registered)
972 at_fatal_signal (&cleanup);
973 cleanup_already_registered = true;
977 /* Create a temporary directory where we can put the Java file. */
978 template = (char *) xallocsa (PATH_MAX);
979 if (path_search (template, PATH_MAX, NULL, "msg", 1))
981 error (0, errno,
982 _("cannot find a temporary directory, try setting $TMPDIR"));
983 goto quit1;
985 block_fatal_signals ();
986 tmpdir = mkdtemp (template);
987 cleanup_list.tmpdir = tmpdir;
988 unblock_fatal_signals ();
989 if (tmpdir == NULL)
991 error (0, errno,
992 _("cannot create a temporary directory using template \"%s\""),
993 template);
994 goto quit1;
997 /* Assign a default value to the resource name. */
998 if (resource_name == NULL)
999 resource_name = "Messages";
1001 /* Prepare the list of subdirectories. */
1002 ndots = check_resource_name (resource_name);
1003 if (ndots < 0)
1005 error (0, 0, _("not a valid Java class name: %s"), resource_name);
1006 goto quit2;
1009 if (locale_name != NULL)
1011 class_name =
1012 (char *) xmalloc (strlen (resource_name) + 1 + strlen (locale_name) + 1);
1013 sprintf (class_name, "%s_%s", resource_name, locale_name);
1015 else
1016 class_name = xstrdup (resource_name);
1018 subdirs = (ndots > 0 ? (char **) xallocsa (ndots * sizeof (char *)) : NULL);
1020 const char *p;
1021 const char *last_dir;
1022 int i;
1024 last_dir = tmpdir;
1025 p = resource_name;
1026 for (i = 0; i < ndots; i++)
1028 const char *q = strchr (p, '.');
1029 size_t n = q - p;
1030 char *part = (char *) xallocsa (n + 1);
1031 memcpy (part, p, n);
1032 part[n] = '\0';
1033 subdirs[i] = concatenated_pathname (last_dir, part, NULL);
1034 freesa (part);
1035 last_dir = subdirs[i];
1036 p = q + 1;
1039 if (locale_name != NULL)
1041 char *suffix = (char *) xmalloc (1 + strlen (locale_name) + 5 + 1);
1042 sprintf (suffix, "_%s.java", locale_name);
1043 java_file_name = concatenated_pathname (last_dir, p, suffix);
1044 free (suffix);
1046 else
1047 java_file_name = concatenated_pathname (last_dir, p, ".java");
1050 /* Create the subdirectories. This is needed because some older Java
1051 compilers verify that the source of class A.B.C really sits in a
1052 directory whose name ends in /A/B. */
1054 int i;
1056 cleanup_list.subdir = (const char * const *) subdirs;
1057 cleanup_list.subdir_count = 0;
1058 for (i = 0; i < ndots; i++)
1060 cleanup_list.subdir_count = i + 1;
1061 if (mkdir (subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR) < 0)
1063 error (0, errno, _("failed to create \"%s\""), subdirs[i]);
1064 while (i-- > 0)
1065 rmdir (subdirs[i]);
1066 goto quit3;
1071 /* Create the Java file. */
1072 cleanup_list.file_name = java_file_name;
1073 java_file = fopen (java_file_name, "w");
1074 if (java_file == NULL)
1076 error (0, errno, _("failed to create \"%s\""), java_file_name);
1077 goto quit4;
1080 write_java_code (java_file, class_name, mlp, assume_java2);
1082 if (fwriteerror (java_file))
1084 error (0, errno, _("error while writing \"%s\" file"), java_file_name);
1085 fclose (java_file);
1086 goto quit5;
1089 /* Compile the Java file to a .class file.
1090 directory must be non-NULL, because when the -d option is omitted, the
1091 Java compilers create the class files in the source file's directory -
1092 which is in a temporary directory in our case. */
1093 java_sources[0] = java_file_name;
1094 if (compile_java_class (java_sources, 1, NULL, 0, directory, true, false,
1095 true, verbose))
1097 error (0, 0, _("\
1098 compilation of Java class failed, please try --verbose or set $JAVAC"));
1099 goto quit5;
1102 retval = 0;
1104 quit5:
1105 unlink (java_file_name);
1106 quit4:
1107 cleanup_list.file_name = NULL;
1109 int i;
1110 for (i = ndots; i-- > 0; )
1111 rmdir (subdirs[i]);
1113 quit3:
1114 cleanup_list.subdir_count = 0;
1116 int i;
1117 free (java_file_name);
1118 for (i = 0; i < ndots; i++)
1119 free (subdirs[i]);
1121 freesa (subdirs);
1122 free (class_name);
1123 quit2:
1124 rmdir (tmpdir);
1125 quit1:
1126 cleanup_list.tmpdir = NULL;
1127 freesa (template);
1128 /* Here we could unregister the cleanup() handler. */
1129 return retval;