1 /* GNU gettext - internationalization aids
2 Copyright (C) 1995-1998, 2000-2010, 2012-2013, 2015-2016, 2019-2020, 2023 Free Software Foundation, Inc.
3
4 This file was written by Peter Miller <millerp@canb.auug.org.au>
5
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.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification. */
24 #include "message.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "fstrcmp.h"
30 #include "mem-hash-map.h"
31 #include "xalloc.h"
32 #include "xmalloca.h"
33
34
35 const char *const format_language[NFORMATS] =
36 {
37 /* format_c */ "c",
38 /* format_objc */ "objc",
39 /* format_cplusplus_brace */ "c++",
40 /* format_python */ "python",
41 /* format_python_brace */ "python-brace",
42 /* format_java */ "java",
43 /* format_java_printf */ "java-printf",
44 /* format_csharp */ "csharp",
45 /* format_javascript */ "javascript",
46 /* format_scheme */ "scheme",
47 /* format_lisp */ "lisp",
48 /* format_elisp */ "elisp",
49 /* format_librep */ "librep",
50 /* format_ruby */ "ruby",
51 /* format_sh */ "sh",
52 /* format_awk */ "awk",
53 /* format_lua */ "lua",
54 /* format_pascal */ "object-pascal",
55 /* format_smalltalk */ "smalltalk",
56 /* format_qt */ "qt",
57 /* format_qt_plursl */ "qt-plural",
58 /* format_kde */ "kde",
59 /* format_kde_kuit */ "kde-kuit",
60 /* format_boost */ "boost",
61 /* format_tcl */ "tcl",
62 /* format_perl */ "perl",
63 /* format_perl_brace */ "perl-brace",
64 /* format_php */ "php",
65 /* format_gcc_internal */ "gcc-internal",
66 /* format_gfc_internal */ "gfc-internal",
67 /* format_ycp */ "ycp"
68 };
69
70 const char *const format_language_pretty[NFORMATS] =
71 {
72 /* format_c */ "C",
73 /* format_objc */ "Objective C",
74 /* format_cplusplus_brace */ "C++",
75 /* format_python */ "Python",
76 /* format_python_brace */ "Python brace",
77 /* format_java */ "Java MessageFormat",
78 /* format_java_printf */ "Java printf",
79 /* format_csharp */ "C#",
80 /* format_javascript */ "JavaScript",
81 /* format_scheme */ "Scheme",
82 /* format_lisp */ "Lisp",
83 /* format_elisp */ "Emacs Lisp",
84 /* format_librep */ "librep",
85 /* format_ruby */ "Ruby",
86 /* format_sh */ "Shell",
87 /* format_awk */ "awk",
88 /* format_lua */ "Lua",
89 /* format_pascal */ "Object Pascal",
90 /* format_smalltalk */ "Smalltalk",
91 /* format_qt */ "Qt",
92 /* format_qt_plural */ "Qt plural",
93 /* format_kde */ "KDE",
94 /* format_kde_kuit */ "KDE KUIT",
95 /* format_boost */ "Boost",
96 /* format_tcl */ "Tcl",
97 /* format_perl */ "Perl",
98 /* format_perl_brace */ "Perl brace",
99 /* format_php */ "PHP",
100 /* format_gcc_internal */ "GCC internal",
101 /* format_gfc_internal */ "GFC internal",
102 /* format_ycp */ "YCP"
103 };
104
105
106 bool
107 possible_format_p (enum is_format is_format)
108 {
109 return is_format == possible
110 || is_format == yes_according_to_context
111 || is_format == yes;
112 }
113
114
115 const char *const syntax_check_name[NSYNTAXCHECKS] =
116 {
117 /* sc_ellipsis_unicode */ "ellipsis-unicode",
118 /* sc_space_ellipsis */ "space-ellipsis",
119 /* sc_quote_unicode */ "quote-unicode",
120 /* sc_bullet_unicode */ "bullet-unicode"
121 };
122
123
124 message_ty *
125 message_alloc (const char *msgctxt,
126 const char *msgid, const char *msgid_plural,
127 const char *msgstr, size_t msgstr_len,
128 const lex_pos_ty *pp)
129 {
130 message_ty *mp;
131 size_t i;
132
133 mp = XMALLOC (message_ty);
134 mp->msgctxt = msgctxt;
135 mp->msgid = msgid;
136 mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
137 mp->msgstr = msgstr;
138 mp->msgstr_len = msgstr_len;
139 mp->pos = *pp;
140 mp->comment = NULL;
141 mp->comment_dot = NULL;
142 mp->filepos_count = 0;
143 mp->filepos = NULL;
144 mp->is_fuzzy = false;
145 for (i = 0; i < NFORMATS; i++)
146 mp->is_format[i] = undecided;
147 mp->range.min = -1;
148 mp->range.max = -1;
149 mp->do_wrap = undecided;
150 for (i = 0; i < NSYNTAXCHECKS; i++)
151 mp->do_syntax_check[i] = undecided;
152 mp->prev_msgctxt = NULL;
153 mp->prev_msgid = NULL;
154 mp->prev_msgid_plural = NULL;
155 mp->used = 0;
156 mp->obsolete = false;
157 return mp;
158 }
159
160
161 void
162 message_free (message_ty *mp)
163 {
164 size_t j;
165
166 free ((char *) mp->msgid);
167 if (mp->msgid_plural != NULL)
168 free ((char *) mp->msgid_plural);
169 free ((char *) mp->msgstr);
170 if (mp->comment != NULL)
171 string_list_free (mp->comment);
172 if (mp->comment_dot != NULL)
173 string_list_free (mp->comment_dot);
174 for (j = 0; j < mp->filepos_count; ++j)
175 free ((char *) mp->filepos[j].file_name);
176 if (mp->filepos != NULL)
177 free (mp->filepos);
178 if (mp->prev_msgctxt != NULL)
179 free ((char *) mp->prev_msgctxt);
180 if (mp->prev_msgid != NULL)
181 free ((char *) mp->prev_msgid);
182 if (mp->prev_msgid_plural != NULL)
183 free ((char *) mp->prev_msgid_plural);
184 free (mp);
185 }
186
187
188 void
189 message_comment_append (message_ty *mp, const char *s)
190 {
191 if (mp->comment == NULL)
192 mp->comment = string_list_alloc ();
193 string_list_append (mp->comment, s);
194 }
195
196
197 void
198 message_comment_dot_append (message_ty *mp, const char *s)
199 {
200 if (mp->comment_dot == NULL)
201 mp->comment_dot = string_list_alloc ();
202 string_list_append (mp->comment_dot, s);
203 }
204
205
206 void
207 message_comment_filepos (message_ty *mp, const char *name, size_t line)
208 {
209 size_t j;
210 size_t nbytes;
211 lex_pos_ty *pp;
212
213 /* See if we have this position already. */
214 for (j = 0; j < mp->filepos_count; j++)
215 {
216 pp = &mp->filepos[j];
217 if (strcmp (pp->file_name, name) == 0 && pp->line_number == line)
218 return;
219 }
220
221 /* Extend the list so that we can add a position to it. */
222 nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]);
223 mp->filepos = xrealloc (mp->filepos, nbytes);
224
225 /* Insert the position at the end. Don't sort the file positions here. */
226 pp = &mp->filepos[mp->filepos_count++];
227 pp->file_name = xstrdup (name);
228 pp->line_number = line;
229 }
230
231
232 message_ty *
233 message_copy (message_ty *mp)
234 {
235 message_ty *result;
236 size_t j, i;
237
238 result = message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL,
239 xstrdup (mp->msgid), mp->msgid_plural,
240 mp->msgstr, mp->msgstr_len, &mp->pos);
241
242 if (mp->comment)
243 {
244 for (j = 0; j < mp->comment->nitems; ++j)
245 message_comment_append (result, mp->comment->item[j]);
246 }
247 if (mp->comment_dot)
248 {
249 for (j = 0; j < mp->comment_dot->nitems; ++j)
250 message_comment_dot_append (result, mp->comment_dot->item[j]);
251 }
252 result->is_fuzzy = mp->is_fuzzy;
253 for (i = 0; i < NFORMATS; i++)
254 result->is_format[i] = mp->is_format[i];
255 result->range = mp->range;
256 result->do_wrap = mp->do_wrap;
257 for (i = 0; i < NSYNTAXCHECKS; i++)
258 result->do_syntax_check[i] = mp->do_syntax_check[i];
259 for (j = 0; j < mp->filepos_count; ++j)
260 {
261 lex_pos_ty *pp = &mp->filepos[j];
262 message_comment_filepos (result, pp->file_name, pp->line_number);
263 }
264 result->prev_msgctxt =
265 (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL);
266 result->prev_msgid =
267 (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL);
268 result->prev_msgid_plural =
269 (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL);
270 return result;
271 }
272
273
274 message_list_ty *
275 message_list_alloc (bool use_hashtable)
276 {
277 message_list_ty *mlp;
278
279 mlp = XMALLOC (message_list_ty);
280 mlp->nitems = 0;
281 mlp->nitems_max = 0;
282 mlp->item = NULL;
283 if ((mlp->use_hashtable = use_hashtable))
284 hash_init (&mlp->htable, 10);
285 return mlp;
286 }
287
288
289 void
290 message_list_free (message_list_ty *mlp, int keep_messages)
291 {
292 size_t j;
293
294 if (keep_messages == 0)
295 for (j = 0; j < mlp->nitems; ++j)
296 message_free (mlp->item[j]);
297 if (mlp->item)
298 free (mlp->item);
299 if (mlp->use_hashtable)
300 hash_destroy (&mlp->htable);
301 free (mlp);
302 }
303
304
305 static int
306 message_list_hash_insert_entry (hash_table *htable, message_ty *mp)
307 {
308 char *alloced_key;
309 const char *key;
310 size_t keylen;
311 int found;
312
313 if (mp->msgctxt != NULL)
314 {
315 /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */
316 size_t msgctxt_len = strlen (mp->msgctxt);
317 size_t msgid_len = strlen (mp->msgid);
318 keylen = msgctxt_len + 1 + msgid_len + 1;
319 alloced_key = (char *) xmalloca (keylen);
320 memcpy (alloced_key, mp->msgctxt, msgctxt_len);
321 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
322 memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1);
323 key = alloced_key;
324 }
325 else
326 {
327 alloced_key = NULL;
328 key = mp->msgid;
329 keylen = strlen (mp->msgid) + 1;
330 }
331
332 found = (hash_insert_entry (htable, key, keylen, mp) == NULL);
333
334 if (mp->msgctxt != NULL)
335 freea (alloced_key);
336
337 return found;
338 }
339
340
341 void
342 message_list_append (message_list_ty *mlp, message_ty *mp)
343 {
344 if (mlp->nitems >= mlp->nitems_max)
345 {
346 size_t nbytes;
347
348 mlp->nitems_max = mlp->nitems_max * 2 + 4;
349 nbytes = mlp->nitems_max * sizeof (message_ty *);
350 mlp->item = xrealloc (mlp->item, nbytes);
351 }
352 mlp->item[mlp->nitems++] = mp;
353
354 if (mlp->use_hashtable)
355 if (message_list_hash_insert_entry (&mlp->htable, mp))
356 /* A message list has duplicates, although it was allocated with the
357 assertion that it wouldn't have duplicates. It is a bug. */
358 abort ();
359 }
360
361
362 void
363 message_list_prepend (message_list_ty *mlp, message_ty *mp)
364 {
365 size_t j;
366
367 if (mlp->nitems >= mlp->nitems_max)
368 {
369 size_t nbytes;
370
371 mlp->nitems_max = mlp->nitems_max * 2 + 4;
372 nbytes = mlp->nitems_max * sizeof (message_ty *);
373 mlp->item = xrealloc (mlp->item, nbytes);
374 }
375 for (j = mlp->nitems; j > 0; j--)
376 mlp->item[j] = mlp->item[j - 1];
377 mlp->item[0] = mp;
378 mlp->nitems++;
379
380 if (mlp->use_hashtable)
381 if (message_list_hash_insert_entry (&mlp->htable, mp))
382 /* A message list has duplicates, although it was allocated with the
383 assertion that it wouldn't have duplicates. It is a bug. */
384 abort ();
385 }
386
387
388 void
389 message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
390 {
391 size_t j;
392
393 if (mlp->nitems >= mlp->nitems_max)
394 {
395 size_t nbytes;
396
397 mlp->nitems_max = mlp->nitems_max * 2 + 4;
398 nbytes = mlp->nitems_max * sizeof (message_ty *);
399 mlp->item = xrealloc (mlp->item, nbytes);
400 }
401 for (j = mlp->nitems; j > n; j--)
402 mlp->item[j] = mlp->item[j - 1];
403 mlp->item[j] = mp;
404 mlp->nitems++;
405
406 if (mlp->use_hashtable)
407 if (message_list_hash_insert_entry (&mlp->htable, mp))
408 /* A message list has duplicates, although it was allocated with the
409 assertion that it wouldn't have duplicates. It is a bug. */
410 abort ();
411 }
412
413
414 #if 0 /* unused */
415 void
416 message_list_delete_nth (message_list_ty *mlp, size_t n)
417 {
418 size_t j;
419
420 if (n >= mlp->nitems)
421 return;
422 message_free (mlp->item[n]);
423 for (j = n + 1; j < mlp->nitems; ++j)
424 mlp->item[j - 1] = mlp->item[j];
425 mlp->nitems--;
426
427 if (mlp->use_hashtable)
428 {
429 /* Our simple-minded hash tables don't support removal. */
430 hash_destroy (&mlp->htable);
431 mlp->use_hashtable = false;
432 }
433 }
434 #endif
435
436
437 void
438 message_list_remove_if_not (message_list_ty *mlp,
439 message_predicate_ty *predicate)
440 {
441 size_t i, j;
442
443 for (j = 0, i = 0; j < mlp->nitems; j++)
444 if (predicate (mlp->item[j]))
445 mlp->item[i++] = mlp->item[j];
446 if (mlp->use_hashtable && i < mlp->nitems)
447 {
448 /* Our simple-minded hash tables don't support removal. */
449 hash_destroy (&mlp->htable);
450 mlp->use_hashtable = false;
451 }
452 mlp->nitems = i;
453 }
454
455
456 bool
457 message_list_msgids_changed (message_list_ty *mlp)
458 {
459 if (mlp->use_hashtable)
460 {
461 unsigned long int size = mlp->htable.size;
462 size_t j;
463
464 hash_destroy (&mlp->htable);
465 hash_init (&mlp->htable, size);
466
467 for (j = 0; j < mlp->nitems; j++)
468 {
469 message_ty *mp = mlp->item[j];
470
471 if (message_list_hash_insert_entry (&mlp->htable, mp))
472 /* A message list has duplicates, although it was allocated with
473 the assertion that it wouldn't have duplicates, and before the
474 msgids changed it indeed didn't have duplicates. */
475 {
476 hash_destroy (&mlp->htable);
477 mlp->use_hashtable = false;
478 return true;
479 }
480 }
481 }
482 return false;
483 }
484
485
486 message_list_ty *
487 message_list_copy (message_list_ty *mlp, int copy_level)
488 {
489 message_list_ty *result;
490 size_t j;
491
492 result = message_list_alloc (mlp->use_hashtable);
493 for (j = 0; j < mlp->nitems; j++)
494 {
495 message_ty *mp = mlp->item[j];
496
497 message_list_append (result, copy_level ? mp : message_copy (mp));
498 }
499
500 return result;
501 }
502
503
504 message_ty *
505 message_list_search (message_list_ty *mlp,
506 const char *msgctxt, const char *msgid)
507 {
508 if (mlp->use_hashtable)
509 {
510 char *alloced_key;
511 const char *key;
512 size_t keylen;
513
514 if (msgctxt != NULL)
515 {
516 /* Concatenate the msgctxt and msgid, to form the hash table key. */
517 size_t msgctxt_len = strlen (msgctxt);
518 size_t msgid_len = strlen (msgid);
519 keylen = msgctxt_len + 1 + msgid_len + 1;
520 alloced_key = (char *) xmalloca (keylen);
521 memcpy (alloced_key, msgctxt, msgctxt_len);
522 alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR;
523 memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1);
524 key = alloced_key;
525 }
526 else
527 {
528 alloced_key = NULL;
529 key = msgid;
530 keylen = strlen (msgid) + 1;
531 }
532
533 {
534 void *htable_value;
535 int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value);
536
537 if (msgctxt != NULL)
538 freea (alloced_key);
539
540 if (found)
541 return (message_ty *) htable_value;
542 else
543 return NULL;
544 }
545 }
546 else
547 {
548 size_t j;
549
550 for (j = 0; j < mlp->nitems; ++j)
551 {
552 message_ty *mp;
553
554 mp = mlp->item[j];
555 if ((msgctxt != NULL
556 ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0
557 : mp->msgctxt == NULL)
558 && strcmp (msgid, mp->msgid) == 0)
559 return mp;
560 }
561 return NULL;
562 }
563 }
564
565
566 double
567 fuzzy_search_goal_function (const message_ty *mp,
568 const char *msgctxt, const char *msgid,
569 double lower_bound)
570 {
571 double bonus = 0.0;
572 /* A translation for a context is a good proposal also for another. But
573 give mp a small advantage if mp is valid regardless of any context or
574 has the same context as the one being looked up. */
575 if (mp->msgctxt == NULL
576 || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0))
577 {
578 bonus = 0.00001;
579 /* Since we will consider (weight + bonus) at the end, we are only
580 interested in weights that are >= lower_bound - bonus. Subtract
581 a little more than the bonus, in order to avoid trouble due to
582 rounding errors. */
583 lower_bound -= bonus * 1.01;
584 }
585
586 {
587 /* The use of 'volatile' guarantees that excess precision bits are dropped
588 before the addition and before the following comparison at the caller's
589 site. It is necessary on x86 systems where double-floats are not IEEE
590 compliant by default, to avoid that msgmerge results become platform and
591 compiler option dependent. 'volatile' is a portable alternative to
592 gcc's -ffloat-store option. */
593 volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound);
594
595 weight += bonus;
596
597 return weight;
598 }
599 }
600
601
602 static message_ty *
603 message_list_search_fuzzy_inner (message_list_ty *mlp,
604 const char *msgctxt, const char *msgid,
605 double *best_weight_p)
606 {
607 size_t j;
608 message_ty *best_mp;
609
610 best_mp = NULL;
611 for (j = 0; j < mlp->nitems; ++j)
612 {
613 message_ty *mp;
614
615 mp = mlp->item[j];
616
617 if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
618 {
619 double weight =
620 fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p);
621 if (weight > *best_weight_p)
622 {
623 *best_weight_p = weight;
624 best_mp = mp;
625 }
626 }
627 }
628 return best_mp;
629 }
630
631
632 message_ty *
633 message_list_search_fuzzy (message_list_ty *mlp,
634 const char *msgctxt, const char *msgid)
635 {
636 double best_weight;
637
638 best_weight = FUZZY_THRESHOLD;
639 return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
640 }
641
642
643 message_list_list_ty *
644 message_list_list_alloc ()
645 {
646 message_list_list_ty *mllp;
647
648 mllp = XMALLOC (message_list_list_ty);
649 mllp->nitems = 0;
650 mllp->nitems_max = 0;
651 mllp->item = NULL;
652 return mllp;
653 }
654
655
656 void
657 message_list_list_free (message_list_list_ty *mllp, int keep_level)
658 {
659 size_t j;
660
661 if (keep_level < 2)
662 for (j = 0; j < mllp->nitems; ++j)
663 message_list_free (mllp->item[j], keep_level);
664 if (mllp->item)
665 free (mllp->item);
666 free (mllp);
667 }
668
669
670 void
671 message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp)
672 {
673 if (mllp->nitems >= mllp->nitems_max)
674 {
675 size_t nbytes;
676
677 mllp->nitems_max = mllp->nitems_max * 2 + 4;
678 nbytes = mllp->nitems_max * sizeof (message_list_ty *);
679 mllp->item = xrealloc (mllp->item, nbytes);
680 }
681 mllp->item[mllp->nitems++] = mlp;
682 }
683
684
685 void
686 message_list_list_append_list (message_list_list_ty *mllp,
687 message_list_list_ty *mllp2)
688 {
689 size_t j;
690
691 for (j = 0; j < mllp2->nitems; ++j)
692 message_list_list_append (mllp, mllp2->item[j]);
693 }
694
695
696 message_ty *
697 message_list_list_search (message_list_list_ty *mllp,
698 const char *msgctxt, const char *msgid)
699 {
700 message_ty *best_mp;
701 int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */
702 size_t j;
703
704 best_mp = NULL;
705 best_weight = 0;
706 for (j = 0; j < mllp->nitems; ++j)
707 {
708 message_list_ty *mlp;
709 message_ty *mp;
710
711 mlp = mllp->item[j];
712 mp = message_list_search (mlp, msgctxt, msgid);
713 if (mp)
714 {
715 int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2);
716 if (weight > best_weight)
717 {
718 best_mp = mp;
719 best_weight = weight;
720 }
721 }
722 }
723 return best_mp;
724 }
725
726
727 #if 0 /* unused */
728 message_ty *
729 message_list_list_search_fuzzy (message_list_list_ty *mllp,
730 const char *msgctxt, const char *msgid)
731 {
732 size_t j;
733 double best_weight;
734 message_ty *best_mp;
735
736 best_weight = FUZZY_THRESHOLD;
737 best_mp = NULL;
738 for (j = 0; j < mllp->nitems; ++j)
739 {
740 message_list_ty *mlp;
741 message_ty *mp;
742
743 mlp = mllp->item[j];
744 mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
745 if (mp)
746 best_mp = mp;
747 }
748 return best_mp;
749 }
750 #endif
751
752
753 msgdomain_ty*
754 msgdomain_alloc (const char *domain, bool use_hashtable)
755 {
756 msgdomain_ty *mdp;
757
758 mdp = XMALLOC (msgdomain_ty);
759 mdp->domain = domain;
760 mdp->messages = message_list_alloc (use_hashtable);
761 return mdp;
762 }
763
764
765 void
766 msgdomain_free (msgdomain_ty *mdp)
767 {
768 message_list_free (mdp->messages, 0);
769 free (mdp);
770 }
771
772
773 msgdomain_list_ty *
774 msgdomain_list_alloc (bool use_hashtable)
775 {
776 msgdomain_list_ty *mdlp;
777
778 mdlp = XMALLOC (msgdomain_list_ty);
779 /* Put the default domain first, so that when we output it,
780 we can omit the 'domain' directive. */
781 mdlp->nitems = 1;
782 mdlp->nitems_max = 1;
783 mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *);
784 mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable);
785 mdlp->use_hashtable = use_hashtable;
786 mdlp->encoding = NULL;
787 return mdlp;
788 }
789
790
791 void
792 msgdomain_list_free (msgdomain_list_ty *mdlp)
793 {
794 size_t j;
795
796 for (j = 0; j < mdlp->nitems; ++j)
797 msgdomain_free (mdlp->item[j]);
798 if (mdlp->item)
799 free (mdlp->item);
800 free (mdlp);
801 }
802
803
804 void
805 msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp)
806 {
807 if (mdlp->nitems >= mdlp->nitems_max)
808 {
809 size_t nbytes;
810
811 mdlp->nitems_max = mdlp->nitems_max * 2 + 4;
812 nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *);
813 mdlp->item = xrealloc (mdlp->item, nbytes);
814 }
815 mdlp->item[mdlp->nitems++] = mdp;
816 }
817
818
819 #if 0 /* unused */
820 void
821 msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2)
822 {
823 size_t j;
824
825 for (j = 0; j < mdlp2->nitems; ++j)
826 msgdomain_list_append (mdlp, mdlp2->item[j]);
827 }
828 #endif
829
830
831 message_list_ty *
832 msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
833 bool create)
834 {
835 size_t j;
836
837 for (j = 0; j < mdlp->nitems; j++)
838 if (strcmp (mdlp->item[j]->domain, domain) == 0)
839 return mdlp->item[j]->messages;
840
841 if (create)
842 {
843 msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable);
844 msgdomain_list_append (mdlp, mdp);
845 return mdp->messages;
846 }
847 else
848 return NULL;
849 }
850
851
852 /* Copy a message domain list.
853 If copy_level = 0, also copy the messages. If copy_level = 1, share the
854 messages but copy the domains. If copy_level = 2, share the domains. */
855 msgdomain_list_ty *
856 msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level)
857 {
858 msgdomain_list_ty *result;
859 size_t j;
860
861 result = XMALLOC (msgdomain_list_ty);
862 result->nitems = 0;
863 result->nitems_max = 0;
864 result->item = NULL;
865 result->use_hashtable = mdlp->use_hashtable;
866 result->encoding = mdlp->encoding;
867
868 for (j = 0; j < mdlp->nitems; j++)
869 {
870 msgdomain_ty *mdp = mdlp->item[j];
871
872 if (copy_level < 2)
873 {
874 msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty);
875
876 result_mdp->domain = mdp->domain;
877 result_mdp->messages = message_list_copy (mdp->messages, copy_level);
878
879 msgdomain_list_append (result, result_mdp);
880 }
881 else
882 msgdomain_list_append (result, mdp);
883 }
884
885 return result;
886 }
887
888
889 #if 0 /* unused */
890 message_ty *
891 msgdomain_list_search (msgdomain_list_ty *mdlp,
892 const char *msgctxt, const char *msgid)
893 {
894 size_t j;
895
896 for (j = 0; j < mdlp->nitems; ++j)
897 {
898 msgdomain_ty *mdp;
899 message_ty *mp;
900
901 mdp = mdlp->item[j];
902 mp = message_list_search (mdp->messages, msgctxt, msgid);
903 if (mp)
904 return mp;
905 }
906 return NULL;
907 }
908 #endif
909
910
911 #if 0 /* unused */
912 message_ty *
913 msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
914 const char *msgctxt, const char *msgid)
915 {
916 size_t j;
917 double best_weight;
918 message_ty *best_mp;
919
920 best_weight = FUZZY_THRESHOLD;
921 best_mp = NULL;
922 for (j = 0; j < mdlp->nitems; ++j)
923 {
924 msgdomain_ty *mdp;
925 message_ty *mp;
926
927 mdp = mdlp->item[j];
928 mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid,
929 &best_weight);
930 if (mp)
931 best_mp = mp;
932 }
933 return best_mp;
934 }
935 #endif