1 /* GNU gettext - internationalization aids
2 Copyright (C) 1995-1998, 2000-2009, 2015 Free Software Foundation,
5 This file was written by Peter Miller <millerp@canb.auug.org.au>
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
36 const char *const format_language[NFORMATS] =
39 /* format_objc */ "objc",
41 /* format_python */ "python",
42 /* format_python_brace */ "python-brace",
43 /* format_lisp */ "lisp",
44 /* format_elisp */ "elisp",
45 /* format_librep */ "librep",
46 /* format_scheme */ "scheme",
47 /* format_smalltalk */ "smalltalk",
48 /* format_java */ "java",
49 /* format_csharp */ "csharp",
50 /* format_awk */ "awk",
51 /* format_pascal */ "object-pascal",
52 /* format_ycp */ "ycp",
53 /* format_tcl */ "tcl",
54 /* format_perl */ "perl",
55 /* format_perl_brace */ "perl-brace",
56 /* format_php */ "php",
57 /* format_gcc_internal */ "gcc-internal",
58 /* format_gfc_internal */ "gfc-internal",
60 /* format_qt_plursl */ "qt-plural",
61 /* format_kde */ "kde",
62 /* format_kde_kuit */ "kde-kuit",
63 /* format_boost */ "boost",
64 /* format_lua */ "lua",
65 /* format_javascript */ "javascript"
68 const char *const format_language_pretty[NFORMATS] =
71 /* format_objc */ "Objective C",
72 /* format_sh */ "Shell",
73 /* format_python */ "Python",
74 /* format_python_brace */ "Python brace",
75 /* format_lisp */ "Lisp",
76 /* format_elisp */ "Emacs Lisp",
77 /* format_librep */ "librep",
78 /* format_scheme */ "Scheme",
79 /* format_smalltalk */ "Smalltalk",
80 /* format_java */ "Java",
81 /* format_csharp */ "C#",
82 /* format_awk */ "awk",
83 /* format_pascal */ "Object Pascal",
84 /* format_ycp */ "YCP",
85 /* format_tcl */ "Tcl",
86 /* format_perl */ "Perl",
87 /* format_perl_brace */ "Perl brace",
88 /* format_php */ "PHP",
89 /* format_gcc_internal */ "GCC internal",
90 /* format_gfc_internal */ "GFC internal",
92 /* format_qt_plural */ "Qt plural",
93 /* format_kde */ "KDE",
94 /* format_kde_kuit */ "KDE KUIT",
95 /* format_boost */ "Boost",
96 /* format_lua */ "Lua",
97 /* format_javascript */ "JavaScript"
102 possible_format_p (enum is_format is_format)
104 return is_format == possible
105 || is_format == yes_according_to_context
110 const char *const syntax_check_name[NSYNTAXCHECKS] =
112 /* sc_ellipsis_unicode */ "ellipsis-unicode",
113 /* sc_space_ellipsis */ "space-ellipsis",
114 /* sc_quote_unicode */ "quote-unicode"
119 message_alloc (const char *msgctxt,
120 const char *msgid, const char *msgid_plural,
121 const char *msgstr, size_t msgstr_len,
122 const lex_pos_ty *pp)
127 mp = XMALLOC (message_ty);
128 mp->msgctxt = msgctxt;
130 mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
132 mp->msgstr_len = msgstr_len;
135 mp->comment_dot = NULL;
136 mp->filepos_count = 0;
138 mp->is_fuzzy = false;
139 for (i = 0; i < NFORMATS; i++)
140 mp->is_format[i] = undecided;
143 mp->do_wrap = undecided;
144 for (i = 0; i < NSYNTAXCHECKS; i++)
145 mp->do_syntax_check[i] = undecided;
146 mp->prev_msgctxt = NULL;
147 mp->prev_msgid = NULL;
148 mp->prev_msgid_plural = NULL;
150 mp->obsolete = false;
156 message_free (message_ty *mp)
160 free ((char *) mp->msgid);
161 if (mp->msgid_plural != NULL)
162 free ((char *) mp->msgid_plural);
163 free ((char *) mp->msgstr);
164 if (mp->comment != NULL)
165 string_list_free (mp->comment);
166 if (mp->comment_dot != NULL)
167 string_list_free (mp->comment_dot);
168 for (j = 0; j < mp->filepos_count; ++j)
169 free ((char *) mp->filepos[j].file_name);
170 if (mp->filepos != NULL)
172 if (mp->prev_msgctxt != NULL)
173 free ((char *) mp->prev_msgctxt);
174 if (mp->prev_msgid != NULL)
175 free ((char *) mp->prev_msgid);
176 if (mp->prev_msgid_plural != NULL)
177 free ((char *) mp->prev_msgid_plural);
183 message_comment_append (message_ty *mp, const char *s)
185 if (mp->comment == NULL)
186 mp->comment = string_list_alloc ();
187 string_list_append (mp->comment, s);
192 message_comment_dot_append (message_ty *mp, const char *s)
194 if (mp->comment_dot == NULL)
195 mp->comment_dot = string_list_alloc ();
196 string_list_append (mp->comment_dot, s);
201 message_comment_filepos (message_ty *mp, const char *name, size_t line)
207 /* See if we have this position already. */
208 for (j = 0; j < mp->filepos_count; j++)
210 pp = &mp->filepos[j];
211 if (strcmp (pp->file_name, name) == 0 && pp->line_number == line)
215 /* Extend the list so that we can add a position to it. */
216 nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
217 mp->filepos = xrealloc (mp->filepos, nbytes);
219 /* Insert the position at the end. Don't sort the file positions here. */
220 pp = &mp->filepos[mp->filepos_count++];
221 pp->file_name = xstrdup (name);
222 pp->line_number = line;
227 message_copy (message_ty *mp)
232 result = message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL,
233 xstrdup (mp->msgid), mp->msgid_plural,
234 mp->msgstr, mp->msgstr_len, &mp->pos);
238 for (j = 0; j < mp->comment->nitems; ++j)
239 message_comment_append (result, mp->comment->item[j]);
243 for (j = 0; j < mp->comment_dot->nitems; ++j)
244 message_comment_dot_append (result, mp->comment_dot->item[j]);
246 result->is_fuzzy = mp->is_fuzzy;
247 for (i = 0; i < NFORMATS; i++)
248 result->is_format[i] = mp->is_format[i];
249 result->range = mp->range;
250 result->do_wrap = mp->do_wrap;
251 for (i = 0; i < NSYNTAXCHECKS; i++)
252 result->do_syntax_check[i] = mp->do_syntax_check[i];
253 for (j = 0; j < mp->filepos_count; ++j)
255 lex_pos_ty *pp = &mp->filepos[j];
256 message_comment_filepos (result, pp->file_name, pp->line_number);
258 result->prev_msgctxt =
259 (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL);
261 (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL);
262 result->prev_msgid_plural =
263 (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL);
269 message_list_alloc (bool use_hashtable)
271 message_list_ty *mlp;
273 mlp = XMALLOC (message_list_ty);
277 if ((mlp->use_hashtable = use_hashtable))
278 hash_init (&mlp->htable, 10);
284 message_list_free (message_list_ty *mlp, int keep_messages)
288 if (keep_messages == 0)
289 for (j = 0; j < mlp->nitems; ++j)
290 message_free (mlp->item[j]);
293 if (mlp->use_hashtable)
294 hash_destroy (&mlp->htable);
300 message_list_hash_insert_entry (hash_table *htable, message_ty *mp)
307 if (mp->msgctxt != NULL)
309 /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */
310 size_t msgctxt_len = strlen (mp->msgctxt);
311 size_t msgid_len = strlen (mp->msgid);
312 keylen = msgctxt_len + 1 + msgid_len + 1;
313 alloced_key = (char *) xmalloca (keylen);
314 memcpy (alloced_key, mp->msgctxt, msgctxt_len);
315 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
316 memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1);
323 keylen = strlen (mp->msgid) + 1;
326 found = (hash_insert_entry (htable, key, keylen, mp) == NULL);
328 if (mp->msgctxt != NULL)
336 message_list_append (message_list_ty *mlp, message_ty *mp)
338 if (mlp->nitems >= mlp->nitems_max)
342 mlp->nitems_max = mlp->nitems_max * 2 + 4;
343 nbytes = mlp->nitems_max * sizeof (message_ty *);
344 mlp->item = xrealloc (mlp->item, nbytes);
346 mlp->item[mlp->nitems++] = mp;
348 if (mlp->use_hashtable)
349 if (message_list_hash_insert_entry (&mlp->htable, mp))
350 /* A message list has duplicates, although it was allocated with the
351 assertion that it wouldn't have duplicates. It is a bug. */
357 message_list_prepend (message_list_ty *mlp, message_ty *mp)
361 if (mlp->nitems >= mlp->nitems_max)
365 mlp->nitems_max = mlp->nitems_max * 2 + 4;
366 nbytes = mlp->nitems_max * sizeof (message_ty *);
367 mlp->item = xrealloc (mlp->item, nbytes);
369 for (j = mlp->nitems; j > 0; j--)
370 mlp->item[j] = mlp->item[j - 1];
374 if (mlp->use_hashtable)
375 if (message_list_hash_insert_entry (&mlp->htable, mp))
376 /* A message list has duplicates, although it was allocated with the
377 assertion that it wouldn't have duplicates. It is a bug. */
383 message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
387 if (mlp->nitems >= mlp->nitems_max)
391 mlp->nitems_max = mlp->nitems_max * 2 + 4;
392 nbytes = mlp->nitems_max * sizeof (message_ty *);
393 mlp->item = xrealloc (mlp->item, nbytes);
395 for (j = mlp->nitems; j > n; j--)
396 mlp->item[j] = mlp->item[j - 1];
400 if (mlp->use_hashtable)
401 if (message_list_hash_insert_entry (&mlp->htable, mp))
402 /* A message list has duplicates, although it was allocated with the
403 assertion that it wouldn't have duplicates. It is a bug. */
410 message_list_delete_nth (message_list_ty *mlp, size_t n)
414 if (n >= mlp->nitems)
416 message_free (mlp->item[n]);
417 for (j = n + 1; j < mlp->nitems; ++j)
418 mlp->item[j - 1] = mlp->item[j];
421 if (mlp->use_hashtable)
423 /* Our simple-minded hash tables don't support removal. */
424 hash_destroy (&mlp->htable);
425 mlp->use_hashtable = false;
432 message_list_remove_if_not (message_list_ty *mlp,
433 message_predicate_ty *predicate)
437 for (j = 0, i = 0; j < mlp->nitems; j++)
438 if (predicate (mlp->item[j]))
439 mlp->item[i++] = mlp->item[j];
440 if (mlp->use_hashtable && i < mlp->nitems)
442 /* Our simple-minded hash tables don't support removal. */
443 hash_destroy (&mlp->htable);
444 mlp->use_hashtable = false;
451 message_list_msgids_changed (message_list_ty *mlp)
453 if (mlp->use_hashtable)
455 unsigned long int size = mlp->htable.size;
458 hash_destroy (&mlp->htable);
459 hash_init (&mlp->htable, size);
461 for (j = 0; j < mlp->nitems; j++)
463 message_ty *mp = mlp->item[j];
465 if (message_list_hash_insert_entry (&mlp->htable, mp))
466 /* A message list has duplicates, although it was allocated with
467 the assertion that it wouldn't have duplicates, and before the
468 msgids changed it indeed didn't have duplicates. */
470 hash_destroy (&mlp->htable);
471 mlp->use_hashtable = false;
481 message_list_copy (message_list_ty *mlp, int copy_level)
483 message_list_ty *result;
486 result = message_list_alloc (mlp->use_hashtable);
487 for (j = 0; j < mlp->nitems; j++)
489 message_ty *mp = mlp->item[j];
491 message_list_append (result, copy_level ? mp : message_copy (mp));
499 message_list_search (message_list_ty *mlp,
500 const char *msgctxt, const char *msgid)
502 if (mlp->use_hashtable)
510 /* Concatenate the msgctxt and msgid, to form the hash table key. */
511 size_t msgctxt_len = strlen (msgctxt);
512 size_t msgid_len = strlen (msgid);
513 keylen = msgctxt_len + 1 + msgid_len + 1;
514 alloced_key = (char *) xmalloca (keylen);
515 memcpy (alloced_key, msgctxt, msgctxt_len);
516 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
517 memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1);
524 keylen = strlen (msgid) + 1;
529 int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value);
535 return (message_ty *) htable_value;
544 for (j = 0; j < mlp->nitems; ++j)
550 ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0
551 : mp->msgctxt == NULL)
552 && strcmp (msgid, mp->msgid) == 0)
561 fuzzy_search_goal_function (const message_ty *mp,
562 const char *msgctxt, const char *msgid,
566 /* A translation for a context is a good proposal also for another. But
567 give mp a small advantage if mp is valid regardless of any context or
568 has the same context as the one being looked up. */
569 if (mp->msgctxt == NULL
570 || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0))
573 /* Since we will consider (weight + bonus) at the end, we are only
574 interested in weights that are >= lower_bound - bonus. Subtract
575 a little more than the bonus, in order to avoid trouble due to
577 lower_bound -= bonus * 1.01;
581 /* The use of 'volatile' guarantees that excess precision bits are dropped
582 before the addition and before the following comparison at the caller's
583 site. It is necessary on x86 systems where double-floats are not IEEE
584 compliant by default, to avoid that msgmerge results become platform and
585 compiler option dependent. 'volatile' is a portable alternative to
586 gcc's -ffloat-store option. */
587 volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound);
597 message_list_search_fuzzy_inner (message_list_ty *mlp,
598 const char *msgctxt, const char *msgid,
599 double *best_weight_p)
605 for (j = 0; j < mlp->nitems; ++j)
611 if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
614 fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p);
615 if (weight > *best_weight_p)
617 *best_weight_p = weight;
627 message_list_search_fuzzy (message_list_ty *mlp,
628 const char *msgctxt, const char *msgid)
632 best_weight = FUZZY_THRESHOLD;
633 return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
637 message_list_list_ty *
638 message_list_list_alloc ()
640 message_list_list_ty *mllp;
642 mllp = XMALLOC (message_list_list_ty);
644 mllp->nitems_max = 0;
651 message_list_list_free (message_list_list_ty *mllp, int keep_level)
656 for (j = 0; j < mllp->nitems; ++j)
657 message_list_free (mllp->item[j], keep_level);
665 message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp)
667 if (mllp->nitems >= mllp->nitems_max)
671 mllp->nitems_max = mllp->nitems_max * 2 + 4;
672 nbytes = mllp->nitems_max * sizeof (message_list_ty *);
673 mllp->item = xrealloc (mllp->item, nbytes);
675 mllp->item[mllp->nitems++] = mlp;
680 message_list_list_append_list (message_list_list_ty *mllp,
681 message_list_list_ty *mllp2)
685 for (j = 0; j < mllp2->nitems; ++j)
686 message_list_list_append (mllp, mllp2->item[j]);
691 message_list_list_search (message_list_list_ty *mllp,
692 const char *msgctxt, const char *msgid)
695 int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */
700 for (j = 0; j < mllp->nitems; ++j)
702 message_list_ty *mlp;
706 mp = message_list_search (mlp, msgctxt, msgid);
709 int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2);
710 if (weight > best_weight)
713 best_weight = weight;
723 message_list_list_search_fuzzy (message_list_list_ty *mllp,
724 const char *msgctxt, const char *msgid)
730 best_weight = FUZZY_THRESHOLD;
732 for (j = 0; j < mllp->nitems; ++j)
734 message_list_ty *mlp;
738 mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
748 msgdomain_alloc (const char *domain, bool use_hashtable)
752 mdp = XMALLOC (msgdomain_ty);
753 mdp->domain = domain;
754 mdp->messages = message_list_alloc (use_hashtable);
760 msgdomain_free (msgdomain_ty *mdp)
762 message_list_free (mdp->messages, 0);
768 msgdomain_list_alloc (bool use_hashtable)
770 msgdomain_list_ty *mdlp;
772 mdlp = XMALLOC (msgdomain_list_ty);
773 /* Put the default domain first, so that when we output it,
774 we can omit the 'domain' directive. */
776 mdlp->nitems_max = 1;
777 mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *);
778 mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable);
779 mdlp->use_hashtable = use_hashtable;
780 mdlp->encoding = NULL;
786 msgdomain_list_free (msgdomain_list_ty *mdlp)
790 for (j = 0; j < mdlp->nitems; ++j)
791 msgdomain_free (mdlp->item[j]);
799 msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp)
801 if (mdlp->nitems >= mdlp->nitems_max)
805 mdlp->nitems_max = mdlp->nitems_max * 2 + 4;
806 nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *);
807 mdlp->item = xrealloc (mdlp->item, nbytes);
809 mdlp->item[mdlp->nitems++] = mdp;
815 msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2)
819 for (j = 0; j < mdlp2->nitems; ++j)
820 msgdomain_list_append (mdlp, mdlp2->item[j]);
826 msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
831 for (j = 0; j < mdlp->nitems; j++)
832 if (strcmp (mdlp->item[j]->domain, domain) == 0)
833 return mdlp->item[j]->messages;
837 msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable);
838 msgdomain_list_append (mdlp, mdp);
839 return mdp->messages;
846 /* Copy a message domain list.
847 If copy_level = 0, also copy the messages. If copy_level = 1, share the
848 messages but copy the domains. If copy_level = 2, share the domains. */
850 msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level)
852 msgdomain_list_ty *result;
855 result = XMALLOC (msgdomain_list_ty);
857 result->nitems_max = 0;
859 result->use_hashtable = mdlp->use_hashtable;
860 result->encoding = mdlp->encoding;
862 for (j = 0; j < mdlp->nitems; j++)
864 msgdomain_ty *mdp = mdlp->item[j];
868 msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty);
870 result_mdp->domain = mdp->domain;
871 result_mdp->messages = message_list_copy (mdp->messages, copy_level);
873 msgdomain_list_append (result, result_mdp);
876 msgdomain_list_append (result, mdp);
885 msgdomain_list_search (msgdomain_list_ty *mdlp,
886 const char *msgctxt, const char *msgid)
890 for (j = 0; j < mdlp->nitems; ++j)
896 mp = message_list_search (mdp->messages, msgctxt, msgid);
907 msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
908 const char *msgctxt, const char *msgid)
914 best_weight = FUZZY_THRESHOLD;
916 for (j = 0; j < mdlp->nitems; ++j)
922 mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid,