Home | History | Annotate | Line # | Download | only in src
      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 (at) 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
     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 *
     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
    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
    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
    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
    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 *
    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 *
    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
    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
    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
    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
    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
    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
    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
    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 *
    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
    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 *
    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 *
    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 *
    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
    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
    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
    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 *
    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*
    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
    697 msgdomain_free (msgdomain_ty *mdp)
    698 {
    699   message_list_free (mdp->messages, 0);
    700   free (mdp);
    701 }
    702 
    703 
    704 msgdomain_list_ty *
    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
    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
    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 *
    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