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