Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / message.c
1 /* GNU gettext - internationalization aids
2    Copyright (C) 1995-1998, 2000-2009 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 <http://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 "hash.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_sh */               "sh",
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",
58   /* format_qt */               "qt",
59   /* format_qt_plursl */        "qt-plural",
60   /* format_kde */              "kde",
61   /* format_boost */            "boost",
62   /* format_lua */              "lua",
63   /* format_javascript */       "javascript"
64 };
65
66 const char *const format_language_pretty[NFORMATS] =
67 {
68   /* format_c */                "C",
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",
89   /* format_qt */               "Qt",
90   /* format_qt_plural */        "Qt plural",
91   /* format_kde */              "KDE",
92   /* format_boost */            "Boost",
93   /* format_lua */              "Lua",
94   /* format_javascript */       "JavaScript"
95 };
96
97
98 bool
99 possible_format_p (enum is_format is_format)
100 {
101   return is_format == possible
102          || is_format == yes_according_to_context
103          || is_format == yes;
104 }
105
106
107 message_ty *
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)
112 {
113   message_ty *mp;
114   size_t i;
115
116   mp = XMALLOC (message_ty);
117   mp->msgctxt = msgctxt;
118   mp->msgid = msgid;
119   mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL);
120   mp->msgstr = msgstr;
121   mp->msgstr_len = msgstr_len;
122   mp->pos = *pp;
123   mp->comment = NULL;
124   mp->comment_dot = NULL;
125   mp->filepos_count = 0;
126   mp->filepos = NULL;
127   mp->is_fuzzy = false;
128   for (i = 0; i < NFORMATS; i++)
129     mp->is_format[i] = undecided;
130   mp->range.min = -1;
131   mp->range.max = -1;
132   mp->do_wrap = undecided;
133   mp->prev_msgctxt = NULL;
134   mp->prev_msgid = NULL;
135   mp->prev_msgid_plural = NULL;
136   mp->used = 0;
137   mp->obsolete = false;
138   return mp;
139 }
140
141
142 void
143 message_free (message_ty *mp)
144 {
145   size_t j;
146
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)
158     free (mp->filepos);
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);
165   free (mp);
166 }
167
168
169 void
170 message_comment_append (message_ty *mp, const char *s)
171 {
172   if (mp->comment == NULL)
173     mp->comment = string_list_alloc ();
174   string_list_append (mp->comment, s);
175 }
176
177
178 void
179 message_comment_dot_append (message_ty *mp, const char *s)
180 {
181   if (mp->comment_dot == NULL)
182     mp->comment_dot = string_list_alloc ();
183   string_list_append (mp->comment_dot, s);
184 }
185
186
187 void
188 message_comment_filepos (message_ty *mp, const char *name, size_t line)
189 {
190   size_t j;
191   size_t nbytes;
192   lex_pos_ty *pp;
193
194   /* See if we have this position already.  */
195   for (j = 0; j < mp->filepos_count; j++)
196     {
197       pp = &mp->filepos[j];
198       if (strcmp (pp->file_name, name) == 0 && pp->line_number == line)
199         return;
200     }
201
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);
205
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;
210 }
211
212
213 message_ty *
214 message_copy (message_ty *mp)
215 {
216   message_ty *result;
217   size_t j, i;
218
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);
222
223   if (mp->comment)
224     {
225       for (j = 0; j < mp->comment->nitems; ++j)
226         message_comment_append (result, mp->comment->item[j]);
227     }
228   if (mp->comment_dot)
229     {
230       for (j = 0; j < mp->comment_dot->nitems; ++j)
231         message_comment_dot_append (result, mp->comment_dot->item[j]);
232     }
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)
239     {
240       lex_pos_ty *pp = &mp->filepos[j];
241       message_comment_filepos (result, pp->file_name, pp->line_number);
242     }
243   result->prev_msgctxt =
244     (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL);
245   result->prev_msgid =
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);
249   return result;
250 }
251
252
253 message_list_ty *
254 message_list_alloc (bool use_hashtable)
255 {
256   message_list_ty *mlp;
257
258   mlp = XMALLOC (message_list_ty);
259   mlp->nitems = 0;
260   mlp->nitems_max = 0;
261   mlp->item = NULL;
262   if ((mlp->use_hashtable = use_hashtable))
263     hash_init (&mlp->htable, 10);
264   return mlp;
265 }
266
267
268 void
269 message_list_free (message_list_ty *mlp, int keep_messages)
270 {
271   size_t j;
272
273   if (keep_messages == 0)
274     for (j = 0; j < mlp->nitems; ++j)
275       message_free (mlp->item[j]);
276   if (mlp->item)
277     free (mlp->item);
278   if (mlp->use_hashtable)
279     hash_destroy (&mlp->htable);
280   free (mlp);
281 }
282
283
284 static int
285 message_list_hash_insert_entry (hash_table *htable, message_ty *mp)
286 {
287   char *alloced_key;
288   const char *key;
289   size_t keylen;
290   int found;
291
292   if (mp->msgctxt != NULL)
293     {
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);
302       key = alloced_key;
303     }
304   else
305     {
306       alloced_key = NULL;
307       key = mp->msgid;
308       keylen = strlen (mp->msgid) + 1;
309     }
310
311   found = (hash_insert_entry (htable, key, keylen, mp) == NULL);
312
313   if (mp->msgctxt != NULL)
314     freea (alloced_key);
315
316   return found;
317 }
318
319
320 void
321 message_list_append (message_list_ty *mlp, message_ty *mp)
322 {
323   if (mlp->nitems >= mlp->nitems_max)
324     {
325       size_t nbytes;
326
327       mlp->nitems_max = mlp->nitems_max * 2 + 4;
328       nbytes = mlp->nitems_max * sizeof (message_ty *);
329       mlp->item = xrealloc (mlp->item, nbytes);
330     }
331   mlp->item[mlp->nitems++] = mp;
332
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.  */
337       abort ();
338 }
339
340
341 void
342 message_list_prepend (message_list_ty *mlp, message_ty *mp)
343 {
344   size_t j;
345
346   if (mlp->nitems >= mlp->nitems_max)
347     {
348       size_t nbytes;
349
350       mlp->nitems_max = mlp->nitems_max * 2 + 4;
351       nbytes = mlp->nitems_max * sizeof (message_ty *);
352       mlp->item = xrealloc (mlp->item, nbytes);
353     }
354   for (j = mlp->nitems; j > 0; j--)
355     mlp->item[j] = mlp->item[j - 1];
356   mlp->item[0] = mp;
357   mlp->nitems++;
358
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.  */
363       abort ();
364 }
365
366
367 void
368 message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp)
369 {
370   size_t j;
371
372   if (mlp->nitems >= mlp->nitems_max)
373     {
374       size_t nbytes;
375
376       mlp->nitems_max = mlp->nitems_max * 2 + 4;
377       nbytes = mlp->nitems_max * sizeof (message_ty *);
378       mlp->item = xrealloc (mlp->item, nbytes);
379     }
380   for (j = mlp->nitems; j > n; j--)
381     mlp->item[j] = mlp->item[j - 1];
382   mlp->item[j] = mp;
383   mlp->nitems++;
384
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.  */
389       abort ();
390 }
391
392
393 #if 0 /* unused */
394 void
395 message_list_delete_nth (message_list_ty *mlp, size_t n)
396 {
397   size_t j;
398
399   if (n >= mlp->nitems)
400     return;
401   message_free (mlp->item[n]);
402   for (j = n + 1; j < mlp->nitems; ++j)
403     mlp->item[j - 1] = mlp->item[j];
404   mlp->nitems--;
405
406   if (mlp->use_hashtable)
407     {
408       /* Our simple-minded hash tables don't support removal.  */
409       hash_destroy (&mlp->htable);
410       mlp->use_hashtable = false;
411     }
412 }
413 #endif
414
415
416 void
417 message_list_remove_if_not (message_list_ty *mlp,
418                             message_predicate_ty *predicate)
419 {
420   size_t i, j;
421
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)
426     {
427       /* Our simple-minded hash tables don't support removal.  */
428       hash_destroy (&mlp->htable);
429       mlp->use_hashtable = false;
430     }
431   mlp->nitems = i;
432 }
433
434
435 bool
436 message_list_msgids_changed (message_list_ty *mlp)
437 {
438   if (mlp->use_hashtable)
439     {
440       unsigned long int size = mlp->htable.size;
441       size_t j;
442
443       hash_destroy (&mlp->htable);
444       hash_init (&mlp->htable, size);
445
446       for (j = 0; j < mlp->nitems; j++)
447         {
448           message_ty *mp = mlp->item[j];
449
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.  */
454             {
455               hash_destroy (&mlp->htable);
456               mlp->use_hashtable = false;
457               return true;
458             }
459         }
460     }
461   return false;
462 }
463
464
465 message_list_ty *
466 message_list_copy (message_list_ty *mlp, int copy_level)
467 {
468   message_list_ty *result;
469   size_t j;
470
471   result = message_list_alloc (mlp->use_hashtable);
472   for (j = 0; j < mlp->nitems; j++)
473     {
474       message_ty *mp = mlp->item[j];
475
476       message_list_append (result, copy_level ? mp : message_copy (mp));
477     }
478
479   return result;
480 }
481
482
483 message_ty *
484 message_list_search (message_list_ty *mlp,
485                      const char *msgctxt, const char *msgid)
486 {
487   if (mlp->use_hashtable)
488     {
489       char *alloced_key;
490       const char *key;
491       size_t keylen;
492
493       if (msgctxt != NULL)
494         {
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);
503           key = alloced_key;
504         }
505       else
506         {
507           alloced_key = NULL;
508           key = msgid;
509           keylen = strlen (msgid) + 1;
510         }
511
512       {
513         void *htable_value;
514         int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value);
515
516         if (msgctxt != NULL)
517           freea (alloced_key);
518
519         if (found)
520           return (message_ty *) htable_value;
521         else
522           return NULL;
523       }
524     }
525   else
526     {
527       size_t j;
528
529       for (j = 0; j < mlp->nitems; ++j)
530         {
531           message_ty *mp;
532
533           mp = mlp->item[j];
534           if ((msgctxt != NULL
535                ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0
536                : mp->msgctxt == NULL)
537               && strcmp (msgid, mp->msgid) == 0)
538             return mp;
539         }
540       return NULL;
541     }
542 }
543
544
545 double
546 fuzzy_search_goal_function (const message_ty *mp,
547                             const char *msgctxt, const char *msgid,
548                             double lower_bound)
549 {
550   double bonus = 0.0;
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))
556     {
557       bonus = 0.00001;
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
561          rounding errors.  */
562       lower_bound -= bonus * 1.01;
563     }
564
565   {
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);
573
574     weight += bonus;
575
576     return weight;
577   }
578 }
579
580
581 static message_ty *
582 message_list_search_fuzzy_inner (message_list_ty *mlp,
583                                  const char *msgctxt, const char *msgid,
584                                  double *best_weight_p)
585 {
586   size_t j;
587   message_ty *best_mp;
588
589   best_mp = NULL;
590   for (j = 0; j < mlp->nitems; ++j)
591     {
592       message_ty *mp;
593
594       mp = mlp->item[j];
595
596       if (mp->msgstr != NULL && mp->msgstr[0] != '\0')
597         {
598           double weight =
599             fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p);
600           if (weight > *best_weight_p)
601             {
602               *best_weight_p = weight;
603               best_mp = mp;
604             }
605         }
606     }
607   return best_mp;
608 }
609
610
611 message_ty *
612 message_list_search_fuzzy (message_list_ty *mlp,
613                            const char *msgctxt, const char *msgid)
614 {
615   double best_weight;
616
617   best_weight = FUZZY_THRESHOLD;
618   return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
619 }
620
621
622 message_list_list_ty *
623 message_list_list_alloc ()
624 {
625   message_list_list_ty *mllp;
626
627   mllp = XMALLOC (message_list_list_ty);
628   mllp->nitems = 0;
629   mllp->nitems_max = 0;
630   mllp->item = NULL;
631   return mllp;
632 }
633
634
635 void
636 message_list_list_free (message_list_list_ty *mllp, int keep_level)
637 {
638   size_t j;
639
640   if (keep_level < 2)
641     for (j = 0; j < mllp->nitems; ++j)
642       message_list_free (mllp->item[j], keep_level);
643   if (mllp->item)
644     free (mllp->item);
645   free (mllp);
646 }
647
648
649 void
650 message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp)
651 {
652   if (mllp->nitems >= mllp->nitems_max)
653     {
654       size_t nbytes;
655
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);
659     }
660   mllp->item[mllp->nitems++] = mlp;
661 }
662
663
664 void
665 message_list_list_append_list (message_list_list_ty *mllp,
666                                message_list_list_ty *mllp2)
667 {
668   size_t j;
669
670   for (j = 0; j < mllp2->nitems; ++j)
671     message_list_list_append (mllp, mllp2->item[j]);
672 }
673
674
675 message_ty *
676 message_list_list_search (message_list_list_ty *mllp,
677                           const char *msgctxt, const char *msgid)
678 {
679   message_ty *best_mp;
680   int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */
681   size_t j;
682
683   best_mp = NULL;
684   best_weight = 0;
685   for (j = 0; j < mllp->nitems; ++j)
686     {
687       message_list_ty *mlp;
688       message_ty *mp;
689
690       mlp = mllp->item[j];
691       mp = message_list_search (mlp, msgctxt, msgid);
692       if (mp)
693         {
694           int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2);
695           if (weight > best_weight)
696             {
697               best_mp = mp;
698               best_weight = weight;
699             }
700         }
701     }
702   return best_mp;
703 }
704
705
706 #if 0 /* unused */
707 message_ty *
708 message_list_list_search_fuzzy (message_list_list_ty *mllp,
709                                 const char *msgctxt, const char *msgid)
710 {
711   size_t j;
712   double best_weight;
713   message_ty *best_mp;
714
715   best_weight = FUZZY_THRESHOLD;
716   best_mp = NULL;
717   for (j = 0; j < mllp->nitems; ++j)
718     {
719       message_list_ty *mlp;
720       message_ty *mp;
721
722       mlp = mllp->item[j];
723       mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight);
724       if (mp)
725         best_mp = mp;
726     }
727   return best_mp;
728 }
729 #endif
730
731
732 msgdomain_ty*
733 msgdomain_alloc (const char *domain, bool use_hashtable)
734 {
735   msgdomain_ty *mdp;
736
737   mdp = XMALLOC (msgdomain_ty);
738   mdp->domain = domain;
739   mdp->messages = message_list_alloc (use_hashtable);
740   return mdp;
741 }
742
743
744 void
745 msgdomain_free (msgdomain_ty *mdp)
746 {
747   message_list_free (mdp->messages, 0);
748   free (mdp);
749 }
750
751
752 msgdomain_list_ty *
753 msgdomain_list_alloc (bool use_hashtable)
754 {
755   msgdomain_list_ty *mdlp;
756
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.  */
760   mdlp->nitems = 1;
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;
766   return mdlp;
767 }
768
769
770 void
771 msgdomain_list_free (msgdomain_list_ty *mdlp)
772 {
773   size_t j;
774
775   for (j = 0; j < mdlp->nitems; ++j)
776     msgdomain_free (mdlp->item[j]);
777   if (mdlp->item)
778     free (mdlp->item);
779   free (mdlp);
780 }
781
782
783 void
784 msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp)
785 {
786   if (mdlp->nitems >= mdlp->nitems_max)
787     {
788       size_t nbytes;
789
790       mdlp->nitems_max = mdlp->nitems_max * 2 + 4;
791       nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *);
792       mdlp->item = xrealloc (mdlp->item, nbytes);
793     }
794   mdlp->item[mdlp->nitems++] = mdp;
795 }
796
797
798 #if 0 /* unused */
799 void
800 msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2)
801 {
802   size_t j;
803
804   for (j = 0; j < mdlp2->nitems; ++j)
805     msgdomain_list_append (mdlp, mdlp2->item[j]);
806 }
807 #endif
808
809
810 message_list_ty *
811 msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain,
812                         bool create)
813 {
814   size_t j;
815
816   for (j = 0; j < mdlp->nitems; j++)
817     if (strcmp (mdlp->item[j]->domain, domain) == 0)
818       return mdlp->item[j]->messages;
819
820   if (create)
821     {
822       msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable);
823       msgdomain_list_append (mdlp, mdp);
824       return mdp->messages;
825     }
826   else
827     return NULL;
828 }
829
830
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.  */
834 msgdomain_list_ty *
835 msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level)
836 {
837   msgdomain_list_ty *result;
838   size_t j;
839
840   result = XMALLOC (msgdomain_list_ty);
841   result->nitems = 0;
842   result->nitems_max = 0;
843   result->item = NULL;
844   result->use_hashtable = mdlp->use_hashtable;
845   result->encoding = mdlp->encoding;
846
847   for (j = 0; j < mdlp->nitems; j++)
848     {
849       msgdomain_ty *mdp = mdlp->item[j];
850
851       if (copy_level < 2)
852         {
853           msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty);
854
855           result_mdp->domain = mdp->domain;
856           result_mdp->messages = message_list_copy (mdp->messages, copy_level);
857
858           msgdomain_list_append (result, result_mdp);
859         }
860       else
861         msgdomain_list_append (result, mdp);
862     }
863
864   return result;
865 }
866
867
868 #if 0 /* unused */
869 message_ty *
870 msgdomain_list_search (msgdomain_list_ty *mdlp,
871                        const char *msgctxt, const char *msgid)
872 {
873   size_t j;
874
875   for (j = 0; j < mdlp->nitems; ++j)
876     {
877       msgdomain_ty *mdp;
878       message_ty *mp;
879
880       mdp = mdlp->item[j];
881       mp = message_list_search (mdp->messages, msgctxt, msgid);
882       if (mp)
883         return mp;
884     }
885   return NULL;
886 }
887 #endif
888
889
890 #if 0 /* unused */
891 message_ty *
892 msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp,
893                              const char *msgctxt, const char *msgid)
894 {
895   size_t j;
896   double best_weight;
897   message_ty *best_mp;
898
899   best_weight = FUZZY_THRESHOLD;
900   best_mp = NULL;
901   for (j = 0; j < mdlp->nitems; ++j)
902     {
903       msgdomain_ty *mdp;
904       message_ty *mp;
905
906       mdp = mdlp->item[j];
907       mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid,
908                                             &best_weight);
909       if (mp)
910         best_mp = mp;
911     }
912   return best_mp;
913 }
914 #endif