/* GNU gettext - internationalization aids Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. This file was written by Peter Miller 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 2, 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif /* Specification. */ #include "message.h" #include #include #include "fstrcmp.h" #include "hash.h" #include "xmalloc.h" /* Prototypes for local functions. Needed to ensure compiler checking of function argument counts despite of K&R C function definition syntax. */ static message_ty *message_list_search_fuzzy_inner PARAMS (( message_list_ty *mlp, const char *msgid, double *best_weight_p)); const char *const format_language[NFORMATS] = { /* format_c */ "c", /* format_python */ "python", /* format_lisp */ "lisp", /* format_elisp */ "elisp", /* format_librep */ "librep", /* format_smalltalk */ "smalltalk", /* format_java */ "java", /* format_awk */ "awk", /* format_pascal */ "object-pascal", /* format_ycp */ "ycp", /* format_tcl */ "tcl" }; const char *const format_language_pretty[NFORMATS] = { /* format_c */ "C", /* format_python */ "Python", /* format_lisp */ "Lisp", /* format_elisp */ "Emacs Lisp", /* format_librep */ "librep", /* format_smalltalk */ "Smalltalk", /* format_java */ "Java", /* format_awk */ "awk", /* format_pascal */ "Object Pascal", /* format_ycp */ "YCP", /* format_tcl */ "Tcl" }; bool possible_format_p (is_format) enum is_format is_format; { return is_format == possible || is_format == yes; } message_ty * message_alloc (msgid, msgid_plural, msgstr, msgstr_len, pp) 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 = (message_ty *) xmalloc (sizeof (message_ty)); 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->do_wrap = undecided; mp->used = 0; mp->obsolete = false; return mp; } void message_free (mp) 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); free (mp); } void message_comment_append (mp, s) 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 (mp, s) 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 (mp, name, line) 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 (mp) message_ty *mp; { message_ty *result; size_t j, i; result = message_alloc (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->do_wrap = mp->do_wrap; 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); } return result; } message_list_ty * message_list_alloc (use_hashtable) bool use_hashtable; { message_list_ty *mlp; mlp = (message_list_ty *) xmalloc (sizeof (message_list_ty)); mlp->nitems = 0; mlp->nitems_max = 0; mlp->item = NULL; if ((mlp->use_hashtable = use_hashtable)) init_hash (&mlp->htable, 10); return mlp; } void message_list_free (mlp) message_list_ty *mlp; { size_t j; for (j = 0; j < mlp->nitems; ++j) message_free (mlp->item[j]); if (mlp->item) free (mlp->item); if (mlp->use_hashtable) delete_hash (&mlp->htable); free (mlp); } void message_list_append (mlp, mp) 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 (insert_entry (&mlp->htable, mp->msgid, strlen (mp->msgid) + 1, 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 (mlp, mp) 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 (insert_entry (&mlp->htable, mp->msgid, strlen (mp->msgid) + 1, 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 (mlp, n) 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. */ delete_hash (&mlp->htable); mlp->use_hashtable = false; } } #endif void message_list_remove_if_not (mlp, predicate) 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. */ delete_hash (&mlp->htable); mlp->use_hashtable = false; } mlp->nitems = i; } message_ty * message_list_search (mlp, msgid) message_list_ty *mlp; const char *msgid; { if (mlp->use_hashtable) { message_ty *mp; if (find_entry (&mlp->htable, msgid, strlen (msgid) + 1, (void **) &mp)) return NULL; else return mp; } else { size_t j; for (j = 0; j < mlp->nitems; ++j) { message_ty *mp; mp = mlp->item[j]; if (strcmp (msgid, mp->msgid) == 0) return mp; } return NULL; } } static message_ty * message_list_search_fuzzy_inner (mlp, msgid, best_weight_p) message_list_ty *mlp; 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 = fstrcmp (msgid, mp->msgid); if (weight > *best_weight_p) { *best_weight_p = weight; best_mp = mp; } } } return best_mp; } message_ty * message_list_search_fuzzy (mlp, msgid) message_list_ty *mlp; const char *msgid; { double best_weight; best_weight = 0.6; return message_list_search_fuzzy_inner (mlp, msgid, &best_weight); } message_list_list_ty * message_list_list_alloc () { message_list_list_ty *mllp; mllp = (message_list_list_ty *) xmalloc (sizeof (message_list_list_ty)); mllp->nitems = 0; mllp->nitems_max = 0; mllp->item = NULL; return mllp; } #if 0 /* unused */ void message_list_list_free (mllp) message_list_list_ty *mllp; { size_t j; for (j = 0; j < mllp->nitems; ++j) message_list_free (mllp->item[j]); if (mllp->item) free (mllp->item); free (mllp); } #endif void message_list_list_append (mllp, mlp) 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 (mllp, mllp2) 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 (mllp, msgid) message_list_list_ty *mllp; 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, 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; } message_ty * message_list_list_search_fuzzy (mllp, msgid) message_list_list_ty *mllp; const char *msgid; { size_t j; double best_weight; message_ty *best_mp; best_weight = 0.6; 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, msgid, &best_weight); if (mp) best_mp = mp; } return best_mp; } msgdomain_ty* msgdomain_alloc (domain, use_hashtable) const char *domain; bool use_hashtable; { msgdomain_ty *mdp; mdp = (msgdomain_ty *) xmalloc (sizeof (msgdomain_ty)); mdp->domain = domain; mdp->messages = message_list_alloc (use_hashtable); return mdp; } void msgdomain_free (mdp) msgdomain_ty *mdp; { message_list_free (mdp->messages); free (mdp); } msgdomain_list_ty * msgdomain_list_alloc (use_hashtable) bool use_hashtable; { msgdomain_list_ty *mdlp; mdlp = (msgdomain_list_ty *) xmalloc (sizeof (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 = (msgdomain_ty **) xmalloc (mdlp->nitems_max * sizeof (msgdomain_ty *)); mdlp->item[0] = msgdomain_alloc (MESSAGE_DOMAIN_DEFAULT, use_hashtable); mdlp->use_hashtable = use_hashtable; return mdlp; } #if 0 /* unused */ void msgdomain_list_free (mdlp) 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); } #endif void msgdomain_list_append (mdlp, mdp) 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 (mdlp, mdlp2) 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 (mdlp, domain, create) 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; } #if 0 /* unused */ message_ty * msgdomain_list_search (mdlp, msgid) msgdomain_list_ty *mdlp; 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, msgid); if (mp) return mp; } return NULL; } #endif #if 0 /* unused */ message_ty * msgdomain_list_search_fuzzy (mdlp, msgid) msgdomain_list_ty *mdlp; const char *msgid; { size_t j; double best_weight; message_ty *best_mp; best_weight = 0.6; 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, msgid, &best_weight); if (mp) best_mp = mp; } return best_mp; } #endif