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