| /* GNU gettext - internationalization aids |
| Copyright (C) 1995-1998, 2000-2010, 2012-2013, 2015-2016, 2019-2020 Free Software Foundation, Inc. |
| |
| This file was written by Peter Miller <millerp@canb.auug.org.au> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| /* Specification. */ |
| #include "message.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "fstrcmp.h" |
| #include "mem-hash-map.h" |
| #include "xalloc.h" |
| #include "xmalloca.h" |
| |
| |
| const char *const format_language[NFORMATS] = |
| { |
| /* format_c */ "c", |
| /* format_objc */ "objc", |
| /* format_python */ "python", |
| /* format_python_brace */ "python-brace", |
| /* format_java */ "java", |
| /* format_java_printf */ "java-printf", |
| /* format_csharp */ "csharp", |
| /* format_javascript */ "javascript", |
| /* format_scheme */ "scheme", |
| /* format_lisp */ "lisp", |
| /* format_elisp */ "elisp", |
| /* format_librep */ "librep", |
| /* format_ruby */ "ruby", |
| /* format_sh */ "sh", |
| /* format_awk */ "awk", |
| /* format_lua */ "lua", |
| /* format_pascal */ "object-pascal", |
| /* format_smalltalk */ "smalltalk", |
| /* format_qt */ "qt", |
| /* format_qt_plursl */ "qt-plural", |
| /* format_kde */ "kde", |
| /* format_kde_kuit */ "kde-kuit", |
| /* format_boost */ "boost", |
| /* format_tcl */ "tcl", |
| /* format_perl */ "perl", |
| /* format_perl_brace */ "perl-brace", |
| /* format_php */ "php", |
| /* format_gcc_internal */ "gcc-internal", |
| /* format_gfc_internal */ "gfc-internal", |
| /* format_ycp */ "ycp" |
| }; |
| |
| const char *const format_language_pretty[NFORMATS] = |
| { |
| /* format_c */ "C", |
| /* format_objc */ "Objective C", |
| /* format_python */ "Python", |
| /* format_python_brace */ "Python brace", |
| /* format_java */ "Java MessageFormat", |
| /* format_java_printf */ "Java printf", |
| /* format_csharp */ "C#", |
| /* format_javascript */ "JavaScript", |
| /* format_scheme */ "Scheme", |
| /* format_lisp */ "Lisp", |
| /* format_elisp */ "Emacs Lisp", |
| /* format_librep */ "librep", |
| /* format_ruby */ "Ruby", |
| /* format_sh */ "Shell", |
| /* format_awk */ "awk", |
| /* format_lua */ "Lua", |
| /* format_pascal */ "Object Pascal", |
| /* format_smalltalk */ "Smalltalk", |
| /* format_qt */ "Qt", |
| /* format_qt_plural */ "Qt plural", |
| /* format_kde */ "KDE", |
| /* format_kde_kuit */ "KDE KUIT", |
| /* format_boost */ "Boost", |
| /* format_tcl */ "Tcl", |
| /* format_perl */ "Perl", |
| /* format_perl_brace */ "Perl brace", |
| /* format_php */ "PHP", |
| /* format_gcc_internal */ "GCC internal", |
| /* format_gfc_internal */ "GFC internal", |
| /* format_ycp */ "YCP" |
| }; |
| |
| |
| bool |
| possible_format_p (enum is_format is_format) |
| { |
| return is_format == possible |
| || is_format == yes_according_to_context |
| || is_format == yes; |
| } |
| |
| |
| const char *const syntax_check_name[NSYNTAXCHECKS] = |
| { |
| /* sc_ellipsis_unicode */ "ellipsis-unicode", |
| /* sc_space_ellipsis */ "space-ellipsis", |
| /* sc_quote_unicode */ "quote-unicode", |
| /* sc_bullet_unicode */ "bullet-unicode" |
| }; |
| |
| |
| 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) |
| { |
| message_ty *mp; |
| size_t i; |
| |
| mp = XMALLOC (message_ty); |
| mp->msgctxt = msgctxt; |
| mp->msgid = msgid; |
| mp->msgid_plural = (msgid_plural != NULL ? xstrdup (msgid_plural) : NULL); |
| mp->msgstr = msgstr; |
| mp->msgstr_len = msgstr_len; |
| mp->pos = *pp; |
| mp->comment = NULL; |
| mp->comment_dot = NULL; |
| mp->filepos_count = 0; |
| mp->filepos = NULL; |
| mp->is_fuzzy = false; |
| for (i = 0; i < NFORMATS; i++) |
| mp->is_format[i] = undecided; |
| mp->range.min = -1; |
| mp->range.max = -1; |
| mp->do_wrap = undecided; |
| for (i = 0; i < NSYNTAXCHECKS; i++) |
| mp->do_syntax_check[i] = undecided; |
| mp->prev_msgctxt = NULL; |
| mp->prev_msgid = NULL; |
| mp->prev_msgid_plural = NULL; |
| mp->used = 0; |
| mp->obsolete = false; |
| return mp; |
| } |
| |
| |
| void |
| message_free (message_ty *mp) |
| { |
| size_t j; |
| |
| free ((char *) mp->msgid); |
| if (mp->msgid_plural != NULL) |
| free ((char *) mp->msgid_plural); |
| free ((char *) mp->msgstr); |
| if (mp->comment != NULL) |
| string_list_free (mp->comment); |
| if (mp->comment_dot != NULL) |
| string_list_free (mp->comment_dot); |
| for (j = 0; j < mp->filepos_count; ++j) |
| free ((char *) mp->filepos[j].file_name); |
| if (mp->filepos != NULL) |
| free (mp->filepos); |
| if (mp->prev_msgctxt != NULL) |
| free ((char *) mp->prev_msgctxt); |
| if (mp->prev_msgid != NULL) |
| free ((char *) mp->prev_msgid); |
| if (mp->prev_msgid_plural != NULL) |
| free ((char *) mp->prev_msgid_plural); |
| free (mp); |
| } |
| |
| |
| void |
| message_comment_append (message_ty *mp, const char *s) |
| { |
| if (mp->comment == NULL) |
| mp->comment = string_list_alloc (); |
| string_list_append (mp->comment, s); |
| } |
| |
| |
| void |
| message_comment_dot_append (message_ty *mp, const char *s) |
| { |
| if (mp->comment_dot == NULL) |
| mp->comment_dot = string_list_alloc (); |
| string_list_append (mp->comment_dot, s); |
| } |
| |
| |
| void |
| message_comment_filepos (message_ty *mp, const char *name, size_t line) |
| { |
| size_t j; |
| size_t nbytes; |
| lex_pos_ty *pp; |
| |
| /* See if we have this position already. */ |
| for (j = 0; j < mp->filepos_count; j++) |
| { |
| pp = &mp->filepos[j]; |
| if (strcmp (pp->file_name, name) == 0 && pp->line_number == line) |
| return; |
| } |
| |
| /* Extend the list so that we can add a position to it. */ |
| nbytes = (mp->filepos_count + 1) * sizeof (mp->filepos[0]); |
| mp->filepos = xrealloc (mp->filepos, nbytes); |
| |
| /* Insert the position at the end. Don't sort the file positions here. */ |
| pp = &mp->filepos[mp->filepos_count++]; |
| pp->file_name = xstrdup (name); |
| pp->line_number = line; |
| } |
| |
| |
| message_ty * |
| message_copy (message_ty *mp) |
| { |
| message_ty *result; |
| size_t j, i; |
| |
| result = message_alloc (mp->msgctxt != NULL ? xstrdup (mp->msgctxt) : NULL, |
| xstrdup (mp->msgid), mp->msgid_plural, |
| mp->msgstr, mp->msgstr_len, &mp->pos); |
| |
| if (mp->comment) |
| { |
| for (j = 0; j < mp->comment->nitems; ++j) |
| message_comment_append (result, mp->comment->item[j]); |
| } |
| if (mp->comment_dot) |
| { |
| for (j = 0; j < mp->comment_dot->nitems; ++j) |
| message_comment_dot_append (result, mp->comment_dot->item[j]); |
| } |
| result->is_fuzzy = mp->is_fuzzy; |
| for (i = 0; i < NFORMATS; i++) |
| result->is_format[i] = mp->is_format[i]; |
| result->range = mp->range; |
| result->do_wrap = mp->do_wrap; |
| for (i = 0; i < NSYNTAXCHECKS; i++) |
| result->do_syntax_check[i] = mp->do_syntax_check[i]; |
| for (j = 0; j < mp->filepos_count; ++j) |
| { |
| lex_pos_ty *pp = &mp->filepos[j]; |
| message_comment_filepos (result, pp->file_name, pp->line_number); |
| } |
| result->prev_msgctxt = |
| (mp->prev_msgctxt != NULL ? xstrdup (mp->prev_msgctxt) : NULL); |
| result->prev_msgid = |
| (mp->prev_msgid != NULL ? xstrdup (mp->prev_msgid) : NULL); |
| result->prev_msgid_plural = |
| (mp->prev_msgid_plural != NULL ? xstrdup (mp->prev_msgid_plural) : NULL); |
| return result; |
| } |
| |
| |
| message_list_ty * |
| message_list_alloc (bool use_hashtable) |
| { |
| message_list_ty *mlp; |
| |
| mlp = XMALLOC (message_list_ty); |
| mlp->nitems = 0; |
| mlp->nitems_max = 0; |
| mlp->item = NULL; |
| if ((mlp->use_hashtable = use_hashtable)) |
| hash_init (&mlp->htable, 10); |
| return mlp; |
| } |
| |
| |
| void |
| message_list_free (message_list_ty *mlp, int keep_messages) |
| { |
| size_t j; |
| |
| if (keep_messages == 0) |
| for (j = 0; j < mlp->nitems; ++j) |
| message_free (mlp->item[j]); |
| if (mlp->item) |
| free (mlp->item); |
| if (mlp->use_hashtable) |
| hash_destroy (&mlp->htable); |
| free (mlp); |
| } |
| |
| |
| static int |
| message_list_hash_insert_entry (hash_table *htable, message_ty *mp) |
| { |
| char *alloced_key; |
| const char *key; |
| size_t keylen; |
| int found; |
| |
| if (mp->msgctxt != NULL) |
| { |
| /* Concatenate mp->msgctxt and mp->msgid, to form the hash table key. */ |
| size_t msgctxt_len = strlen (mp->msgctxt); |
| size_t msgid_len = strlen (mp->msgid); |
| keylen = msgctxt_len + 1 + msgid_len + 1; |
| alloced_key = (char *) xmalloca (keylen); |
| memcpy (alloced_key, mp->msgctxt, msgctxt_len); |
| alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR; |
| memcpy (alloced_key + msgctxt_len + 1, mp->msgid, msgid_len + 1); |
| key = alloced_key; |
| } |
| else |
| { |
| alloced_key = NULL; |
| key = mp->msgid; |
| keylen = strlen (mp->msgid) + 1; |
| } |
| |
| found = (hash_insert_entry (htable, key, keylen, mp) == NULL); |
| |
| if (mp->msgctxt != NULL) |
| freea (alloced_key); |
| |
| return found; |
| } |
| |
| |
| void |
| message_list_append (message_list_ty *mlp, message_ty *mp) |
| { |
| if (mlp->nitems >= mlp->nitems_max) |
| { |
| size_t nbytes; |
| |
| mlp->nitems_max = mlp->nitems_max * 2 + 4; |
| nbytes = mlp->nitems_max * sizeof (message_ty *); |
| mlp->item = xrealloc (mlp->item, nbytes); |
| } |
| mlp->item[mlp->nitems++] = mp; |
| |
| if (mlp->use_hashtable) |
| if (message_list_hash_insert_entry (&mlp->htable, mp)) |
| /* A message list has duplicates, although it was allocated with the |
| assertion that it wouldn't have duplicates. It is a bug. */ |
| abort (); |
| } |
| |
| |
| void |
| message_list_prepend (message_list_ty *mlp, message_ty *mp) |
| { |
| size_t j; |
| |
| if (mlp->nitems >= mlp->nitems_max) |
| { |
| size_t nbytes; |
| |
| mlp->nitems_max = mlp->nitems_max * 2 + 4; |
| nbytes = mlp->nitems_max * sizeof (message_ty *); |
| mlp->item = xrealloc (mlp->item, nbytes); |
| } |
| for (j = mlp->nitems; j > 0; j--) |
| mlp->item[j] = mlp->item[j - 1]; |
| mlp->item[0] = mp; |
| mlp->nitems++; |
| |
| if (mlp->use_hashtable) |
| if (message_list_hash_insert_entry (&mlp->htable, mp)) |
| /* A message list has duplicates, although it was allocated with the |
| assertion that it wouldn't have duplicates. It is a bug. */ |
| abort (); |
| } |
| |
| |
| void |
| message_list_insert_at (message_list_ty *mlp, size_t n, message_ty *mp) |
| { |
| size_t j; |
| |
| if (mlp->nitems >= mlp->nitems_max) |
| { |
| size_t nbytes; |
| |
| mlp->nitems_max = mlp->nitems_max * 2 + 4; |
| nbytes = mlp->nitems_max * sizeof (message_ty *); |
| mlp->item = xrealloc (mlp->item, nbytes); |
| } |
| for (j = mlp->nitems; j > n; j--) |
| mlp->item[j] = mlp->item[j - 1]; |
| mlp->item[j] = mp; |
| mlp->nitems++; |
| |
| if (mlp->use_hashtable) |
| if (message_list_hash_insert_entry (&mlp->htable, mp)) |
| /* A message list has duplicates, although it was allocated with the |
| assertion that it wouldn't have duplicates. It is a bug. */ |
| abort (); |
| } |
| |
| |
| #if 0 /* unused */ |
| void |
| message_list_delete_nth (message_list_ty *mlp, size_t n) |
| { |
| size_t j; |
| |
| if (n >= mlp->nitems) |
| return; |
| message_free (mlp->item[n]); |
| for (j = n + 1; j < mlp->nitems; ++j) |
| mlp->item[j - 1] = mlp->item[j]; |
| mlp->nitems--; |
| |
| if (mlp->use_hashtable) |
| { |
| /* Our simple-minded hash tables don't support removal. */ |
| hash_destroy (&mlp->htable); |
| mlp->use_hashtable = false; |
| } |
| } |
| #endif |
| |
| |
| void |
| message_list_remove_if_not (message_list_ty *mlp, |
| message_predicate_ty *predicate) |
| { |
| size_t i, j; |
| |
| for (j = 0, i = 0; j < mlp->nitems; j++) |
| if (predicate (mlp->item[j])) |
| mlp->item[i++] = mlp->item[j]; |
| if (mlp->use_hashtable && i < mlp->nitems) |
| { |
| /* Our simple-minded hash tables don't support removal. */ |
| hash_destroy (&mlp->htable); |
| mlp->use_hashtable = false; |
| } |
| mlp->nitems = i; |
| } |
| |
| |
| bool |
| message_list_msgids_changed (message_list_ty *mlp) |
| { |
| if (mlp->use_hashtable) |
| { |
| unsigned long int size = mlp->htable.size; |
| size_t j; |
| |
| hash_destroy (&mlp->htable); |
| hash_init (&mlp->htable, size); |
| |
| for (j = 0; j < mlp->nitems; j++) |
| { |
| message_ty *mp = mlp->item[j]; |
| |
| if (message_list_hash_insert_entry (&mlp->htable, mp)) |
| /* A message list has duplicates, although it was allocated with |
| the assertion that it wouldn't have duplicates, and before the |
| msgids changed it indeed didn't have duplicates. */ |
| { |
| hash_destroy (&mlp->htable); |
| mlp->use_hashtable = false; |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| message_list_ty * |
| message_list_copy (message_list_ty *mlp, int copy_level) |
| { |
| message_list_ty *result; |
| size_t j; |
| |
| result = message_list_alloc (mlp->use_hashtable); |
| for (j = 0; j < mlp->nitems; j++) |
| { |
| message_ty *mp = mlp->item[j]; |
| |
| message_list_append (result, copy_level ? mp : message_copy (mp)); |
| } |
| |
| return result; |
| } |
| |
| |
| message_ty * |
| message_list_search (message_list_ty *mlp, |
| const char *msgctxt, const char *msgid) |
| { |
| if (mlp->use_hashtable) |
| { |
| char *alloced_key; |
| const char *key; |
| size_t keylen; |
| |
| if (msgctxt != NULL) |
| { |
| /* Concatenate the msgctxt and msgid, to form the hash table key. */ |
| size_t msgctxt_len = strlen (msgctxt); |
| size_t msgid_len = strlen (msgid); |
| keylen = msgctxt_len + 1 + msgid_len + 1; |
| alloced_key = (char *) xmalloca (keylen); |
| memcpy (alloced_key, msgctxt, msgctxt_len); |
| alloced_key[msgctxt_len] = MSGCTXT_SEPARATOR; |
| memcpy (alloced_key + msgctxt_len + 1, msgid, msgid_len + 1); |
| key = alloced_key; |
| } |
| else |
| { |
| alloced_key = NULL; |
| key = msgid; |
| keylen = strlen (msgid) + 1; |
| } |
| |
| { |
| void *htable_value; |
| int found = !hash_find_entry (&mlp->htable, key, keylen, &htable_value); |
| |
| if (msgctxt != NULL) |
| freea (alloced_key); |
| |
| if (found) |
| return (message_ty *) htable_value; |
| else |
| return NULL; |
| } |
| } |
| else |
| { |
| size_t j; |
| |
| for (j = 0; j < mlp->nitems; ++j) |
| { |
| message_ty *mp; |
| |
| mp = mlp->item[j]; |
| if ((msgctxt != NULL |
| ? mp->msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0 |
| : mp->msgctxt == NULL) |
| && strcmp (msgid, mp->msgid) == 0) |
| return mp; |
| } |
| return NULL; |
| } |
| } |
| |
| |
| double |
| fuzzy_search_goal_function (const message_ty *mp, |
| const char *msgctxt, const char *msgid, |
| double lower_bound) |
| { |
| double bonus = 0.0; |
| /* A translation for a context is a good proposal also for another. But |
| give mp a small advantage if mp is valid regardless of any context or |
| has the same context as the one being looked up. */ |
| if (mp->msgctxt == NULL |
| || (msgctxt != NULL && strcmp (msgctxt, mp->msgctxt) == 0)) |
| { |
| bonus = 0.00001; |
| /* Since we will consider (weight + bonus) at the end, we are only |
| interested in weights that are >= lower_bound - bonus. Subtract |
| a little more than the bonus, in order to avoid trouble due to |
| rounding errors. */ |
| lower_bound -= bonus * 1.01; |
| } |
| |
| { |
| /* The use of 'volatile' guarantees that excess precision bits are dropped |
| before the addition and before the following comparison at the caller's |
| site. It is necessary on x86 systems where double-floats are not IEEE |
| compliant by default, to avoid that msgmerge results become platform and |
| compiler option dependent. 'volatile' is a portable alternative to |
| gcc's -ffloat-store option. */ |
| volatile double weight = fstrcmp_bounded (msgid, mp->msgid, lower_bound); |
| |
| weight += bonus; |
| |
| return weight; |
| } |
| } |
| |
| |
| static message_ty * |
| message_list_search_fuzzy_inner (message_list_ty *mlp, |
| const char *msgctxt, const char *msgid, |
| double *best_weight_p) |
| { |
| size_t j; |
| message_ty *best_mp; |
| |
| best_mp = NULL; |
| for (j = 0; j < mlp->nitems; ++j) |
| { |
| message_ty *mp; |
| |
| mp = mlp->item[j]; |
| |
| if (mp->msgstr != NULL && mp->msgstr[0] != '\0') |
| { |
| double weight = |
| fuzzy_search_goal_function (mp, msgctxt, msgid, *best_weight_p); |
| if (weight > *best_weight_p) |
| { |
| *best_weight_p = weight; |
| best_mp = mp; |
| } |
| } |
| } |
| return best_mp; |
| } |
| |
| |
| message_ty * |
| message_list_search_fuzzy (message_list_ty *mlp, |
| const char *msgctxt, const char *msgid) |
| { |
| double best_weight; |
| |
| best_weight = FUZZY_THRESHOLD; |
| return message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight); |
| } |
| |
| |
| message_list_list_ty * |
| message_list_list_alloc () |
| { |
| message_list_list_ty *mllp; |
| |
| mllp = XMALLOC (message_list_list_ty); |
| mllp->nitems = 0; |
| mllp->nitems_max = 0; |
| mllp->item = NULL; |
| return mllp; |
| } |
| |
| |
| void |
| message_list_list_free (message_list_list_ty *mllp, int keep_level) |
| { |
| size_t j; |
| |
| if (keep_level < 2) |
| for (j = 0; j < mllp->nitems; ++j) |
| message_list_free (mllp->item[j], keep_level); |
| if (mllp->item) |
| free (mllp->item); |
| free (mllp); |
| } |
| |
| |
| void |
| message_list_list_append (message_list_list_ty *mllp, message_list_ty *mlp) |
| { |
| if (mllp->nitems >= mllp->nitems_max) |
| { |
| size_t nbytes; |
| |
| mllp->nitems_max = mllp->nitems_max * 2 + 4; |
| nbytes = mllp->nitems_max * sizeof (message_list_ty *); |
| mllp->item = xrealloc (mllp->item, nbytes); |
| } |
| mllp->item[mllp->nitems++] = mlp; |
| } |
| |
| |
| void |
| message_list_list_append_list (message_list_list_ty *mllp, |
| message_list_list_ty *mllp2) |
| { |
| size_t j; |
| |
| for (j = 0; j < mllp2->nitems; ++j) |
| message_list_list_append (mllp, mllp2->item[j]); |
| } |
| |
| |
| message_ty * |
| message_list_list_search (message_list_list_ty *mllp, |
| const char *msgctxt, const char *msgid) |
| { |
| message_ty *best_mp; |
| int best_weight; /* 0: not found, 1: found without msgstr, 2: translated */ |
| size_t j; |
| |
| best_mp = NULL; |
| best_weight = 0; |
| for (j = 0; j < mllp->nitems; ++j) |
| { |
| message_list_ty *mlp; |
| message_ty *mp; |
| |
| mlp = mllp->item[j]; |
| mp = message_list_search (mlp, msgctxt, msgid); |
| if (mp) |
| { |
| int weight = (mp->msgstr_len == 1 && mp->msgstr[0] == '\0' ? 1 : 2); |
| if (weight > best_weight) |
| { |
| best_mp = mp; |
| best_weight = weight; |
| } |
| } |
| } |
| return best_mp; |
| } |
| |
| |
| #if 0 /* unused */ |
| message_ty * |
| message_list_list_search_fuzzy (message_list_list_ty *mllp, |
| const char *msgctxt, const char *msgid) |
| { |
| size_t j; |
| double best_weight; |
| message_ty *best_mp; |
| |
| best_weight = FUZZY_THRESHOLD; |
| best_mp = NULL; |
| for (j = 0; j < mllp->nitems; ++j) |
| { |
| message_list_ty *mlp; |
| message_ty *mp; |
| |
| mlp = mllp->item[j]; |
| mp = message_list_search_fuzzy_inner (mlp, msgctxt, msgid, &best_weight); |
| if (mp) |
| best_mp = mp; |
| } |
| return best_mp; |
| } |
| #endif |
| |
| |
| msgdomain_ty* |
| msgdomain_alloc (const char *domain, bool use_hashtable) |
| { |
| msgdomain_ty *mdp; |
| |
| mdp = XMALLOC (msgdomain_ty); |
| mdp->domain = domain; |
| mdp->messages = message_list_alloc (use_hashtable); |
| return mdp; |
| } |
| |
| |
| void |
| msgdomain_free (msgdomain_ty *mdp) |
| { |
| message_list_free (mdp->messages, 0); |
| free (mdp); |
| } |
| |
| |
| msgdomain_list_ty * |
| msgdomain_list_alloc (bool use_hashtable) |
| { |
| msgdomain_list_ty *mdlp; |
| |
| mdlp = XMALLOC (msgdomain_list_ty); |
| /* Put the default domain first, so that when we output it, |
| we can omit the 'domain' directive. */ |
| mdlp->nitems = 1; |
| mdlp->nitems_max = 1; |
| mdlp->item = XNMALLOC (mdlp->nitems_max, msgdomain_ty *); |
| mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable); |
| mdlp->use_hashtable = use_hashtable; |
| mdlp->encoding = NULL; |
| return mdlp; |
| } |
| |
| |
| void |
| msgdomain_list_free (msgdomain_list_ty *mdlp) |
| { |
| size_t j; |
| |
| for (j = 0; j < mdlp->nitems; ++j) |
| msgdomain_free (mdlp->item[j]); |
| if (mdlp->item) |
| free (mdlp->item); |
| free (mdlp); |
| } |
| |
| |
| void |
| msgdomain_list_append (msgdomain_list_ty *mdlp, msgdomain_ty *mdp) |
| { |
| if (mdlp->nitems >= mdlp->nitems_max) |
| { |
| size_t nbytes; |
| |
| mdlp->nitems_max = mdlp->nitems_max * 2 + 4; |
| nbytes = mdlp->nitems_max * sizeof (msgdomain_ty *); |
| mdlp->item = xrealloc (mdlp->item, nbytes); |
| } |
| mdlp->item[mdlp->nitems++] = mdp; |
| } |
| |
| |
| #if 0 /* unused */ |
| void |
| msgdomain_list_append_list (msgdomain_list_ty *mdlp, msgdomain_list_ty *mdlp2) |
| { |
| size_t j; |
| |
| for (j = 0; j < mdlp2->nitems; ++j) |
| msgdomain_list_append (mdlp, mdlp2->item[j]); |
| } |
| #endif |
| |
| |
| message_list_ty * |
| msgdomain_list_sublist (msgdomain_list_ty *mdlp, const char *domain, |
| bool create) |
| { |
| size_t j; |
| |
| for (j = 0; j < mdlp->nitems; j++) |
| if (strcmp (mdlp->item[j]->domain, domain) == 0) |
| return mdlp->item[j]->messages; |
| |
| if (create) |
| { |
| msgdomain_ty *mdp = msgdomain_alloc (domain, mdlp->use_hashtable); |
| msgdomain_list_append (mdlp, mdp); |
| return mdp->messages; |
| } |
| else |
| return NULL; |
| } |
| |
| |
| /* Copy a message domain list. |
| If copy_level = 0, also copy the messages. If copy_level = 1, share the |
| messages but copy the domains. If copy_level = 2, share the domains. */ |
| msgdomain_list_ty * |
| msgdomain_list_copy (msgdomain_list_ty *mdlp, int copy_level) |
| { |
| msgdomain_list_ty *result; |
| size_t j; |
| |
| result = XMALLOC (msgdomain_list_ty); |
| result->nitems = 0; |
| result->nitems_max = 0; |
| result->item = NULL; |
| result->use_hashtable = mdlp->use_hashtable; |
| result->encoding = mdlp->encoding; |
| |
| for (j = 0; j < mdlp->nitems; j++) |
| { |
| msgdomain_ty *mdp = mdlp->item[j]; |
| |
| if (copy_level < 2) |
| { |
| msgdomain_ty *result_mdp = XMALLOC (msgdomain_ty); |
| |
| result_mdp->domain = mdp->domain; |
| result_mdp->messages = message_list_copy (mdp->messages, copy_level); |
| |
| msgdomain_list_append (result, result_mdp); |
| } |
| else |
| msgdomain_list_append (result, mdp); |
| } |
| |
| return result; |
| } |
| |
| |
| #if 0 /* unused */ |
| message_ty * |
| msgdomain_list_search (msgdomain_list_ty *mdlp, |
| const char *msgctxt, const char *msgid) |
| { |
| size_t j; |
| |
| for (j = 0; j < mdlp->nitems; ++j) |
| { |
| msgdomain_ty *mdp; |
| message_ty *mp; |
| |
| mdp = mdlp->item[j]; |
| mp = message_list_search (mdp->messages, msgctxt, msgid); |
| if (mp) |
| return mp; |
| } |
| return NULL; |
| } |
| #endif |
| |
| |
| #if 0 /* unused */ |
| message_ty * |
| msgdomain_list_search_fuzzy (msgdomain_list_ty *mdlp, |
| const char *msgctxt, const char *msgid) |
| { |
| size_t j; |
| double best_weight; |
| message_ty *best_mp; |
| |
| best_weight = FUZZY_THRESHOLD; |
| best_mp = NULL; |
| for (j = 0; j < mdlp->nitems; ++j) |
| { |
| msgdomain_ty *mdp; |
| message_ty *mp; |
| |
| mdp = mdlp->item[j]; |
| mp = message_list_search_fuzzy_inner (mdp->messages, msgctxt, msgid, |
| &best_weight); |
| if (mp) |
| best_mp = mp; |
| } |
| return best_mp; |
| } |
| #endif |