1 /* GNU gettext - internationalization aids
2 Copyright (C) 1995-1998, 2000-2009 Free Software Foundation, Inc.
4 This file was written by Peter Miller <millerp@canb.auug.org.au>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
35 const char *const format_language[NFORMATS] =
38 /* format_objc */ "objc",
40 /* format_python */ "python",
41 /* format_python_brace */ "python-brace",
42 /* format_lisp */ "lisp",
43 /* format_elisp */ "elisp",
44 /* format_librep */ "librep",
45 /* format_scheme */ "scheme",
46 /* format_smalltalk */ "smalltalk",
47 /* format_java */ "java",
48 /* format_csharp */ "csharp",
49 /* format_awk */ "awk",
50 /* format_pascal */ "object-pascal",
51 /* format_ycp */ "ycp",
52 /* format_tcl */ "tcl",
53 /* format_perl */ "perl",
54 /* format_perl_brace */ "perl-brace",
55 /* format_php */ "php",
56 /* format_gcc_internal */ "gcc-internal",
57 /* format_gfc_internal */ "gfc-internal",
59 /* format_qt_plursl */ "qt-plural",
60 /* format_kde */ "kde",
61 /* format_boost */ "boost",
62 /* format_lua */ "lua",
63 /* format_javascript */ "javascript"
66 const char *const format_language_pretty[NFORMATS] =
69 /* format_objc */ "Objective C",
70 /* format_sh */ "Shell",
71 /* format_python */ "Python",
72 /* format_python_brace */ "Python brace",
73 /* format_lisp */ "Lisp",
74 /* format_elisp */ "Emacs Lisp",
75 /* format_librep */ "librep",
76 /* format_scheme */ "Scheme",
77 /* format_smalltalk */ "Smalltalk",
78 /* format_java */ "Java",
79 /* format_csharp */ "C#",
80 /* format_awk */ "awk",
81 /* format_pascal */ "Object Pascal",
82 /* format_ycp */ "YCP",
83 /* format_tcl */ "Tcl",
84 /* format_perl */ "Perl",
85 /* format_perl_brace */ "Perl brace",
86 /* format_php */ "PHP",
87 /* format_gcc_internal */ "GCC internal",
88 /* format_gfc_internal */ "GFC internal",
90 /* format_qt_plural */ "Qt plural",
91 /* format_kde */ "KDE",
92 /* format_boost */ "Boost",
93 /* format_lua */ "Lua",
94 /* format_javascript */ "JavaScript"
99 possible_format_p (enum is_format is_format)
101 return is_format == possible
102 || is_format == yes_according_to_context
108 message_alloc (const char *msgctxt,
109 const char *msgid, const char *msgid_plural,
110 const char *msgstr, size_t msgstr_len,
111 const lex_pos_ty *pp)
116 mp = XMALLOC (message_ty);
117 mp->msgctxt = msgctxt;
119 mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
121 mp->msgstr_len = msgstr_len;
124 mp->comment_dot = NULL;
125 mp->filepos_count = 0;
127 mp->is_fuzzy = false;
128 for (i = 0; i < NFORMATS; i++)
129 mp->is_format[i] = undecided;
132 mp->do_wrap = undecided;
133 mp->prev_msgctxt = NULL;
134 mp->prev_msgid = NULL;
135 mp->prev_msgid_plural = NULL;
137 mp->obsolete = false;
143 message_free (message_ty *mp)
147 free ((char *) mp->msgid);
148 if (mp->msgid_plural != NULL)
149 free ((char *) mp->msgid_plural);
150 free ((char *) mp->msgstr);
151 if (mp->comment != NULL)
152 string_list_free (mp->comment);
153 if (mp->comment_dot != NULL)
154 string_list_free (mp->comment_dot);
155 for (j = 0; j < mp->filepos_count; ++j)
156 free ((char *) mp->filepos[j].file_name);
157 if (mp->filepos != NULL)
159 if (mp->prev_msgctxt != NULL)
160 free ((char *) mp->prev_msgctxt);
161 if (mp->prev_msgid != NULL)
162 free ((char *) mp->prev_msgid);
163 if (mp->prev_msgid_plural != NULL)
164 free ((char *) mp->prev_msgid_plural);
170 message_comment_append (message_ty *mp, const char *s)
172 if (mp->comment == NULL)
173 mp->comment = string_list_alloc ();
174 string_list_append (mp->comment, s);
179 message_comment_dot_append (message_ty *mp, const char *s)
181 if (mp->comment_dot == NULL)
182 mp->comment_dot = string_list_alloc ();
183 string_list_append (mp->comment_dot, s);
188 message_comment_filepos (message_ty *mp, const char *name, size_t line)
194 /* See if we have this position already. */
195 for (j = 0; j < mp->filepos_count; j++)
197 pp = &mp->filepos[j];
198 if (strcmp (pp->file_name, name) == 0 && pp->line_number == line)
202 /* Extend the list so that we can add a position to it. */
203 nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
204 mp->filepos = xrealloc (mp->filepos, nbytes);
206 /* Insert the position at the end. Don't sort the file positions here. */
207 pp = &mp->filepos[mp->filepos_count++];
208 pp->file_name = xstrdup (name);
209 pp->line_number = line;
214 message_copy (message_ty *mp)
219 result = message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL,
220 xstrdup (mp->msgid), mp->msgid_plural,
221 mp->msgstr, mp->msgstr_len, &mp->pos);
225 for (j = 0; j < mp->comment->nitems; ++j)
226 message_comment_append (result, mp->comment->item[j]);
230 for (j = 0; j < mp->comment_dot->nitems; ++j)
231 message_comment_dot_append (result, mp->comment_dot->item[j]);
233 result->is_fuzzy = mp->is_fuzzy;
234 for (i = 0; i < NFORMATS; i++)
235 result->is_format[i] = mp->is_format[i];
236 result->range = mp->range;
237 result->do_wrap = mp->do_wrap;
238 for (j = 0; j < mp->filepos_count; ++j)
240 lex_pos_ty *pp = &mp->filepos[j];
241 message_comment_filepos (result, pp->file_name, pp->line_number);
243 result->prev_msgctxt =
244 (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL);
246 (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL);
247 result->prev_msgid_plural =
248 (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL);
254 message_list_alloc (bool use_hashtable)
256 message_list_ty *mlp;
258 mlp = XMALLOC (message_list_ty);
262 if ((mlp->use_hashtable = use_hashtable))
263 hash_init (&mlp->htable, 10);
269 message_list_free (message_list_ty *mlp, int keep_messages)
273 if (keep_messages == 0)
274 for (j = 0; j < mlp->nitems; ++j)
275 message_free (mlp->item[j]);
278 if (mlp->use_hashtable)
279 hash_destroy (&mlp->htable);
285 message_list_hash_insert_entry (hash_table *htable, message_ty *mp)
292 if (mp->msgctxt != NULL)
294 /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */
295 size_t msgctxt_len = strlen (mp->msgctxt);
296 size_t msgid_len = strlen (mp->msgid);
297 keylen = msgctxt_len + 1 + msgid_len + 1;
298 alloced_key = (char *) xmalloca (keylen);
299 memcpy (alloced_key, mp->msgctxt, msgctxt_len);
300 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
301 memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1);
308 keylen = strlen (mp->msgid) + 1;
311 found = (hash_insert_entry (htable, key, keylen, mp) == NULL);
313 if (mp->msgctxt != NULL)
321 message_list_append (message_list_ty *mlp, message_ty *mp)
323 if (mlp->nitems >= mlp->nitems_max)
327 mlp->nitems_max = mlp->nitems_max * 2 + 4;
328 nbytes = mlp->nitems_max * sizeof (message_ty *);
329 mlp->item = xrealloc (mlp->item, nbytes);
331 mlp->item[mlp->nitems++] = mp;
333 if (mlp->use_hashtable)
334 if (message_list_hash_insert_entry (&mlp->htable, mp))
335 /* A message list has duplicates, although it was allocated with the
336 assertion that it wouldn't have duplicates. It is a bug. */
342 message_list_prepend (message_list_ty *mlp, message_ty *mp)
346 if (mlp->nitems >= mlp->nitems_max)
350 mlp->nitems_max = mlp->nitems_max * 2 + 4;
351 nbytes = mlp->nitems_max * sizeof (message_ty *);
352 mlp->item = xrealloc (mlp->item, nbytes);
354 for (j = mlp->nitems; j > 0; j--)
355 mlp->item[j] = mlp->item[j - 1];
359 if (mlp->use_hashtable)
360 if (message_list_hash_insert_entry (&mlp->htable, mp))
361 /* A message list has duplicates, although it was allocated with the
362 assertion that it wouldn't have duplicates. It is a bug. */
368 message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
372 if (mlp->nitems >= mlp->nitems_max)
376 mlp->nitems_max = mlp->nitems_max * 2 + 4;
377 nbytes = mlp->nitems_max * sizeof (message_ty *);
378 mlp->item = xrealloc (mlp->item, nbytes);
380 for (j = mlp->nitems; j > n; j--)
381 mlp->item[j] = mlp->item[j - 1];
385 if (mlp->use_hashtable)
386 if (message_list_hash_insert_entry (&mlp->htable, mp))
387 /* A message list has duplicates, although it was allocated with the
388 assertion that it wouldn't have duplicates. It is a bug. */
395 message_list_delete_nth (message_list_ty *mlp, size_t n)
399 if (n >= mlp->nitems)
401 message_free (mlp->item[n]);
402 for (j = n + 1; j < mlp->nitems; ++j)
403 mlp->item[j - 1] = mlp->item[j];
406 if (mlp->use_hashtable)
408 /* Our simple-minded hash tables don't support removal. */
409 hash_destroy (&mlp->htable);
410 mlp->use_hashtable = false;
417 message_list_remove_if_not (message_list_ty *mlp,
418 message_predicate_ty *predicate)
422 for (j = 0, i = 0; j < mlp->nitems; j++)
423 if (predicate (mlp->item[j]))
424 mlp->item[i++] = mlp->item[j];
425 if (mlp->use_hashtable && i < mlp->nitems)
427 /* Our simple-minded hash tables don't support removal. */
428 hash_destroy (&mlp->htable);
429 mlp->use_hashtable = false;
436 message_list_msgids_changed (message_list_ty *mlp)
438 if (mlp->use_hashtable)
440 unsigned long int size = mlp->htable.size;
443 hash_destroy (&mlp->htable);
444 hash_init (&mlp->htable, size);
446 for (j = 0; j < mlp->nitems; j++)
448 message_ty *mp = mlp->item[j];
450 if (message_list_hash_insert_entry (&mlp->htable, mp))
451 /* A message list has duplicates, although it was allocated with
452 the assertion that it wouldn't have duplicates, and before the
453 msgids changed it indeed didn't have duplicates. */
455 hash_destroy (&mlp->htable);
456 mlp->use_hashtable = false;
466 message_list_copy (message_list_ty *mlp, int copy_level)
468 message_list_ty *result;
471 result = message_list_alloc (mlp->use_hashtable);
472 for (j = 0; j < mlp->nitems; j++)
474 message_ty *mp = mlp->item[j];
476 message_list_append (result, copy_level ? mp : message_copy (mp));
484 message_list_search (message_list_ty *mlp,
485 const char *msgctxt, const char *msgid)
487 if (mlp->use_hashtable)
495 /* Concatenate the msgctxt and msgid, to form the hash table key. */
496 size_t msgctxt_len = strlen (msgctxt);
497 size_t msgid_len = strlen (msgid);
498 keylen = msgctxt_len + 1 + msgid_len + 1;
499 alloced_key = (char *) xmalloca (keylen);
500 memcpy (alloced_key, msgctxt, msgctxt_len);
501 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
502 memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1);
509 keylen = strlen (msgid) + 1;
514 int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value);
520 return (message_ty *) htable_value;
529 for (j = 0; j < mlp->nitems; ++j)
535 ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0
536 : mp->msgctxt == NULL)
537 && strcmp (msgid, mp->msgid) == 0)
546 fuzzy_search_goal_function (const message_ty *mp,
547 const char *msgctxt, const char *msgid,
551 /* A translation for a context is a good proposal also for another. But
552 give mp a small advantage if mp is valid regardless of any context or
553 has the same context as the one being looked up. */
554 if (mp->msgctxt == NULL
555 || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0))
558 /* Since we will consider (weight + bonus) at the end, we are only
559 interested in weights that are >= lower_bound - bonus. Subtract
560 a little more than the bonus, in order to avoid trouble due to
562 lower_bound -= bonus * 1.01;
566 /* The use of 'volatile' guarantees that excess precision bits are dropped
567 before the addition and before the following comparison at the caller's
568 site. It is necessary on x86 systems where double-floats are not IEEE
569 compliant by default, to avoid that msgmerge results become platform and
570 compiler option dependent. 'volatile' is a portable alternative to
571 gcc's -ffloat-store option. */
572 volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound);
582 message_list_search_fuzzy_inner (message_list_ty *mlp,
583 const char *msgctxt, const char *msgid,
584 double *best_weight_p)
590 for (j = 0; j < mlp->nitems; ++j)
596 if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
599 fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p);
600 if (weight > *best_weight_p)
602 *best_weight_p = weight;
612 message_list_search_fuzzy (message_list_ty *mlp,
613 const char *msgctxt, const char *msgid)
617 best_weight = FUZZY_THRESHOLD;
618 return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
622 message_list_list_ty *
623 message_list_list_alloc ()
625 message_list_list_ty *mllp;
627 mllp = XMALLOC (message_list_list_ty);
629 mllp->nitems_max = 0;
636 message_list_list_free (message_list_list_ty *mllp, int keep_level)
641 for (j = 0; j < mllp->nitems; ++j)
642 message_list_free (mllp->item[j], keep_level);
650 message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp)
652 if (mllp->nitems >= mllp->nitems_max)
656 mllp->nitems_max = mllp->nitems_max * 2 + 4;
657 nbytes = mllp->nitems_max * sizeof (message_list_ty *);
658 mllp->item = xrealloc (mllp->item, nbytes);
660 mllp->item[mllp->nitems++] = mlp;
665 message_list_list_append_list (message_list_list_ty *mllp,
666 message_list_list_ty *mllp2)
670 for (j = 0; j < mllp2->nitems; ++j)
671 message_list_list_append (mllp, mllp2->item[j]);
676 message_list_list_search (message_list_list_ty *mllp,
677 const char *msgctxt, const char *msgid)
680 int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */
685 for (j = 0; j < mllp->nitems; ++j)
687 message_list_ty *mlp;
691 mp = message_list_search (mlp, msgctxt, msgid);
694 int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2);
695 if (weight > best_weight)
698 best_weight = weight;
708 message_list_list_search_fuzzy (message_list_list_ty *mllp,
709 const char *msgctxt, const char *msgid)
715 best_weight = FUZZY_THRESHOLD;
717 for (j = 0; j < mllp->nitems; ++j)
719 message_list_ty *mlp;
723 mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
733 msgdomain_alloc (const char *domain, bool use_hashtable)
737 mdp = XMALLOC (msgdomain_ty);
738 mdp->domain = domain;
739 mdp->messages = message_list_alloc (use_hashtable);
745 msgdomain_free (msgdomain_ty *mdp)
747 message_list_free (mdp->messages, 0);
753 msgdomain_list_alloc (bool use_hashtable)
755 msgdomain_list_ty *mdlp;
757 mdlp = XMALLOC (msgdomain_list_ty);
758 /* Put the default domain first, so that when we output it,
759 we can omit the 'domain' directive. */
761 mdlp->nitems_max = 1;
762 mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *);
763 mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable);
764 mdlp->use_hashtable = use_hashtable;
765 mdlp->encoding = NULL;
771 msgdomain_list_free (msgdomain_list_ty *mdlp)
775 for (j = 0; j < mdlp->nitems; ++j)
776 msgdomain_free (mdlp->item[j]);
784 msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp)
786 if (mdlp->nitems >= mdlp->nitems_max)
790 mdlp->nitems_max = mdlp->nitems_max * 2 + 4;
791 nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *);
792 mdlp->item = xrealloc (mdlp->item, nbytes);
794 mdlp->item[mdlp->nitems++] = mdp;
800 msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2)
804 for (j = 0; j < mdlp2->nitems; ++j)
805 msgdomain_list_append (mdlp, mdlp2->item[j]);
811 msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
816 for (j = 0; j < mdlp->nitems; j++)
817 if (strcmp (mdlp->item[j]->domain, domain) == 0)
818 return mdlp->item[j]->messages;
822 msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable);
823 msgdomain_list_append (mdlp, mdp);
824 return mdp->messages;
831 /* Copy a message domain list.
832 If copy_level = 0, also copy the messages. If copy_level = 1, share the
833 messages but copy the domains. If copy_level = 2, share the domains. */
835 msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level)
837 msgdomain_list_ty *result;
840 result = XMALLOC (msgdomain_list_ty);
842 result->nitems_max = 0;
844 result->use_hashtable = mdlp->use_hashtable;
845 result->encoding = mdlp->encoding;
847 for (j = 0; j < mdlp->nitems; j++)
849 msgdomain_ty *mdp = mdlp->item[j];
853 msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty);
855 result_mdp->domain = mdp->domain;
856 result_mdp->messages = message_list_copy (mdp->messages, copy_level);
858 msgdomain_list_append (result, result_mdp);
861 msgdomain_list_append (result, mdp);
870 msgdomain_list_search (msgdomain_list_ty *mdlp,
871 const char *msgctxt, const char *msgid)
875 for (j = 0; j < mdlp->nitems; ++j)
881 mp = message_list_search (mdp->messages, msgctxt, msgid);
892 msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
893 const char *msgctxt, const char *msgid)
899 best_weight = FUZZY_THRESHOLD;
901 for (j = 0; j < mdlp->nitems; ++j)
907 mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid,