aboutsummaryrefslogtreecommitdiff
path: root/intl/dcgettext.c
diff options
context:
space:
mode:
Diffstat (limited to 'intl/dcgettext.c')
-rw-r--r--intl/dcgettext.c175
1 files changed, 155 insertions, 20 deletions
diff --git a/intl/dcgettext.c b/intl/dcgettext.c
index d482da3..8e51970 100644
--- a/intl/dcgettext.c
+++ b/intl/dcgettext.c
@@ -121,6 +121,9 @@ char *getcwd ();
# ifndef HAVE_STPCPY
static char *stpcpy PARAMS ((char *dest, const char *src));
# endif
+# ifndef HAVE_MEMPCPY
+static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
+# endif
#endif
/* Amount to increase buffer size by in each try. */
@@ -130,7 +133,7 @@ static char *stpcpy PARAMS ((char *dest, const char *src));
/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
PATH_MAX but might cause redefinition warnings when sys/param.h is
later included (as on MORE/BSD 4.3). */
-#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
# include <limits.h>
#endif
@@ -138,16 +141,16 @@ static char *stpcpy PARAMS ((char *dest, const char *src));
# define _POSIX_PATH_MAX 255
#endif
-#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
+#if !defined PATH_MAX && defined _PC_PATH_MAX
# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
#endif
/* Don't include sys/param.h if it already has been. */
-#if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
# include <sys/param.h>
#endif
-#if !defined(PATH_MAX) && defined(MAXPATHLEN)
+#if !defined PATH_MAX && defined MAXPATHLEN
# define PATH_MAX MAXPATHLEN
#endif
@@ -165,6 +168,68 @@ static char *stpcpy PARAMS ((char *dest, const char *src));
# define HAVE_LOCALE_NULL
#endif
+/* We want to allocate a string at the end of the struct. gcc makes
+ this easy. */
+#ifdef __GNUC__
+# define ZERO 0
+#else
+# define ZERO 1
+#endif
+
+/* This is the type used for the search tree where known translations
+ are stored. */
+struct known_translation_t
+{
+ /* Domain in which to search. */
+ char *domain;
+
+ /* The category. */
+ int category;
+
+ /* State of the catalog counter at the point the string was found. */
+ int counter;
+
+ /* And finally the translation. */
+ const char *translation;
+
+ /* Pointer to the string in question. */
+ char msgid[ZERO];
+};
+
+/* Root of the search tree with known translations. We can use this
+ only if the system provides the `tsearch' function family. */
+#if defined HAVE_TSEARCH || defined _LIBC
+# include <search.h>
+
+static void *root;
+
+# ifdef _LIBC
+# define tsearch __tsearch
+# endif
+
+/* Function to compare two entries in the table of known translations. */
+static int
+transcmp (const void *p1, const void *p2)
+{
+ struct known_translation_t *s1 = (struct known_translation_t *) p1;
+ struct known_translation_t *s2 = (struct known_translation_t *) p2;
+ int result;
+
+ result = strcmp (s1->msgid, s2->msgid);
+ if (result == 0)
+ {
+ result = strcmp (s1->msgid, s2->msgid);
+ if (result == 0)
+ /* We compare the category last (though this is the cheapest
+ operation) since it is hopefully always the same (namely
+ LC_MESSAGES). */
+ result = s1->category - s2->category;
+ }
+
+ return result;
+}
+#endif
+
/* Name of the default domain used for gettext(3) prior any call to
textdomain(3). The default value for this is "messages". */
const char _nl_default_default_domain[] = "messages";
@@ -268,12 +333,34 @@ DCGETTEXT (domainname, msgid, category)
char *dirname, *xdomainname;
char *single_locale;
char *retval;
- int saved_errno = errno;
+ int saved_errno;
+#if defined HAVE_TSEARCH || defined _LIBC
+ struct known_translation_t *search;
+ struct known_translation_t **foundp;
+ size_t msgid_len = strlen (msgid) + 1;
+#endif
+ size_t domainname_len;
/* If no real MSGID is given return NULL. */
if (msgid == NULL)
return NULL;
+#if defined HAVE_TSEARCH || defined _LIBC
+ /* Try to find the translation among those which we found at some time. */
+ search = (struct known_translation_t *) alloca (sizeof (*search)
+ + msgid_len);
+ memcpy (search->msgid, msgid, msgid_len);
+ search->domain = (char *) domainname;
+ search->category = category;
+
+ foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
+ if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
+ return (char *) (*foundp)->translation;
+#endif
+
+ /* Preserve the `errno' value. */
+ saved_errno = errno;
+
/* See whether this is a SUID binary or not. */
DETERMINE_SECURE;
@@ -340,12 +427,13 @@ DCGETTEXT (domainname, msgid, category)
categoryname = category_to_name (category);
categoryvalue = guess_category_value (category, categoryname);
+ domainname_len = strlen (domainname);
xdomainname = (char *) alloca (strlen (categoryname)
- + strlen (domainname) + 5);
+ + domainname_len + 5);
ADD_BLOCK (block_list, xdomainname);
- stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
- domainname),
+ stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
+ domainname, domainname_len),
".mo");
/* Creating working area. */
@@ -422,6 +510,38 @@ DCGETTEXT (domainname, msgid, category)
{
FREE_BLOCKS (block_list);
__set_errno (saved_errno);
+#if defined HAVE_TSEARCH || defined _LIBC
+ if (foundp == NULL)
+ {
+ /* Create a new entry and add it to the search tree. */
+ struct known_translation_t *newp;
+
+ newp = (struct known_translation_t *)
+ malloc (sizeof (*newp) + msgid_len
+ + domainname_len + 1 - ZERO);
+ if (newp != NULL)
+ {
+ newp->domain = mempcpy (newp->msgid, msgid, msgid_len);
+ memcpy (newp->domain, domainname, domainname_len + 1);
+ newp->category = category;
+ newp->counter = _nl_msg_cat_cntr;
+ newp->translation = retval;
+
+ /* Insert the entry in the search tree. */
+ foundp = (struct known_translation_t **)
+ tsearch (newp, &root, transcmp);
+ if (&newp != foundp)
+ /* The insert failed. */
+ free (newp);
+ }
+ }
+ else
+ {
+ /* We can update the existing entry. */
+ (*foundp)->counter = _nl_msg_cat_cntr;
+ (*foundp)->translation = retval;
+ }
+#endif
return retval;
}
}
@@ -571,12 +691,13 @@ _nl_find_msg (domain_file, msgid)
return NULL;
if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
- && strcmp (msgid,
- domain->data + W (domain->must_swap,
- domain->orig_tab[nstr - 1].offset))
- == 0)
- return (char *) domain->data
- + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
+ && (strcmp (msgid,
+ domain->data + W (domain->must_swap,
+ domain->orig_tab[nstr - 1].offset))
+ == 0))
+ return ((char *) domain->data
+ + W (domain->must_swap,
+ domain->trans_tab[nstr - 1].offset));
}
/* NOTREACHED */
}
@@ -590,9 +711,9 @@ _nl_find_msg (domain_file, msgid)
int cmp_val;
act = (bottom + top) / 2;
- cmp_val = strcmp (msgid, domain->data
- + W (domain->must_swap,
- domain->orig_tab[act].offset));
+ cmp_val = strcmp (msgid, (domain->data
+ + W (domain->must_swap,
+ domain->orig_tab[act].offset)));
if (cmp_val < 0)
top = act;
else if (cmp_val > 0)
@@ -602,9 +723,9 @@ _nl_find_msg (domain_file, msgid)
}
/* If an translation is found return this. */
- return bottom >= top ? NULL : (char *) domain->data
- + W (domain->must_swap,
- domain->trans_tab[act].offset);
+ return bottom >= top ? NULL : ((char *) domain->data
+ + W (domain->must_swap,
+ domain->trans_tab[act].offset));
}
@@ -728,6 +849,17 @@ stpcpy (dest, src)
}
#endif
+#if !_LIBC && !HAVE_MEMPCPY
+static void *
+mempcpy (dest, src, n)
+ void *dest;
+ const void *src;
+ size_t n;
+{
+ return (void *) ((char *) memcpy (dst, src, n) + n);
+}
+#endif
+
#ifdef _LIBC
/* If we want to free all resources we have to do some work at
@@ -748,6 +880,9 @@ free_mem (void)
if (_nl_current_default_domain != _nl_default_default_domain)
/* Yes, again a pointer comparison. */
free ((char *) _nl_current_default_domain);
+
+ /* Remove the search tree with the know translations. */
+ __tdestroy (root, free);
}
text_set_element (__libc_subfreeres, free_mem);