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