summary refs log tree commit diff
path: root/intl
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2000-05-04 02:46:54 +0000
committerUlrich Drepper <drepper@redhat.com>2000-05-04 02:46:54 +0000
commit17c389fc2b8bdc74ae136c933988ad5d89f6350e (patch)
treed9f06e66be27ed5a2b1b021684de02745b3e594b /intl
parent160016c945d49cfe43f6d34aa077ea04309c983b (diff)
downloadglibc-17c389fc2b8bdc74ae136c933988ad5d89f6350e.tar.gz
glibc-17c389fc2b8bdc74ae136c933988ad5d89f6350e.tar.xz
glibc-17c389fc2b8bdc74ae136c933988ad5d89f6350e.zip
Update.
	* libio/stdio.h: Make fseeko and ftello prototypes available is
	__USE_LARGEFILE.  Patch by Paul Eggert <eggert@twinsun.com>.

	* sysdeps/generic/dl-environ.c (unsetenv): Follow change to the
	real unsetenv implementation from 1999-07-29 [PR libc/1714].

2000-05-03  Bruno Haible  <haible@clisp.cons.org>

	* intl/dcigettext.c (dcigettext): Do the defaulting of 'domainname'
	before calling tfind.

2000-03-05  Jakub Jelinek  <jakub@redhat.com>

	* resolv/resolv.h (res_querydomain): Remove redefinition to
	__res_querydomain (reported by Owen Taylor <otaylor@redhat.com>).

2000-05-03  Ulrich Drepper  <drepper@redhat.com>

	* po/gl.po: Update from translation team.

	* manual/intro.texi (Program Basics): Change section title.
	* manual/process.texi: Fix reference.
	(Executing a File): Add reference exec in other section.
	* manual/signal.texi: Fix reference.
	* manual/startup.texi: Document syscall function.
	Patches by Bryan Henderson <bryanh@giraffe-data.com>.

2000-04-29  Bruno Haible  <haible@clisp.cons.org>

	* intl/libintl.h (bind_textdomain_codeset): New declaration.
	* intl/bindtextdom.c (set_binding_values): New function.
	(bindtextdomain): Call it.
	(bind_textdomain_codeset): New function.
	* intl/dcigettext.c (dcigettext): Pass binding to _nl_find_domain.
	(free_mem): Free each binding's codeset.
	* intl/gettextP.h (struct binding): Add codeset field.
	(_nl_find_domain): Add domainbinding argument.
	* intl/finddomain.c (_nl_find_domain): Add domainbinding argument.
	Pass it to _nl_make_l10nflist.
	* intl/loadinfo.h (struct loaded_l10nfile): Add domainbinding field.
	(_nl_make_l10nflist): Add domainbinding argument.
	* intl/l10nflist.c (_nl_make_l10nflist): Add domainbinding argument.
	* intl/loadmsgcat.c (_nl_load_domain): Look at the domainbinding's
	codeset when determining outcharset. If !_LIBC && HAVE_ICONV, call
	locale_charset().
	* manual/message.texi: New node "Charset conversion in gettext".

2000-04-30  Bruno Haible  <haible@clisp.cons.org>

	* catgets/open_catalog.c (__open_catalog): Use __builtin_expect where
	appropriate.  Handle possible __read error.

2000-04-29  Bruno Haible  <haible@clisp.cons.org>

	* intl/gettextP.h (__builtin_expect): Define as empty if not a
	compiler builtin.
	* intl/loadinfo.h (__builtin_expect): Likewise.
	* intl/dcigettext.c (dcigettext, _nl_find_msg): Use
	__builtin_expect where appropriate.
	* intl/loadmsgcat.c (_nl_load_domain): Likewise.
	* intl/localealias.c (extend_alias_table): Return an error indicator.
	(read_alias_file): Bail out if extend_alias_table fails.

2000-04-29  Bruno Haible  <haible@clisp.cons.org>

	* intl/loadmsgcat.c: Define _GNU_SOURCE as early as possible.
	* intl/localealias.c: Likewise.

2000-05-01  Bruno Haible  <haible@clisp.cons.org>

	* intl/loadmsgcat.c (_nl_load_domain): Initialize domain->conv_tab.
	Initialize domain->plural and domain->nplurals even if there is no
	nullentry.

2000-05-01  Bruno Haible  <haible@clisp.cons.org>

	* intl/dcigettext.c (_nl_find_msg): Terminate __gconv loop if return
	value is == __GCONV_OK or == __GCONV_EMPTY_INPUT, not != __GCONV_OK.
	In case of failure, goto converted.

2000-05-01  Bruno Haible  <haible@clisp.cons.org>

	* wcsmbs/wcsmbsload.c (norm_add_slashes): Move away.
	* iconv/gconv_int.h (norm_add_slashes): Move to here.
	* intl/loadmsgcat.c (_nl_load_domain): Normalize strings passed to
	__gconv_open.

2000-04-29  Bruno Haible  <haible@clisp.cons.org>

	* intl/dcigettext.c (transcmp): Compare the domains as well.
	(dcigettext): Call strlen (msgid1) after testing msgid1 against NULL,
	not before.
	* intl/loadmsgcat.c (_nl_load_domain): Deal with EINTR. Include
	<errno.h>.

2000-05-03  Ulrich Drepper  <drepper@redhat.com>
Diffstat (limited to 'intl')
-rw-r--r--intl/bindtextdom.c268
-rw-r--r--intl/dcigettext.c103
-rw-r--r--intl/finddomain.c9
-rw-r--r--intl/gettextP.h10
-rw-r--r--intl/l10nflist.c7
-rw-r--r--intl/libintl.h5
-rw-r--r--intl/loadinfo.h10
-rw-r--r--intl/loadmsgcat.c89
-rw-r--r--intl/localealias.c23
9 files changed, 377 insertions, 147 deletions
diff --git a/intl/bindtextdom.c b/intl/bindtextdom.c
index c5d4901a85..3d49344aaf 100644
--- a/intl/bindtextdom.c
+++ b/intl/bindtextdom.c
@@ -75,29 +75,44 @@ __libc_rwlock_define (extern, _nl_state_lock)
    prefix.  So we have to make a difference here.  */
 #ifdef _LIBC
 # define BINDTEXTDOMAIN __bindtextdomain
+# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
 # ifndef strdup
 #  define strdup(str) __strdup (str)
 # endif
 #else
 # define BINDTEXTDOMAIN bindtextdomain__
+# define BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset__
 #endif
 
-/* Specify that the DOMAINNAME message catalog will be found
-   in DIRNAME rather than in the system locale data base.  */
-char *
-BINDTEXTDOMAIN (domainname, dirname)
+/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
+   to be used for the DOMAINNAME message catalog.
+   If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
+   modified, only the current value is returned.
+   If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
+   modified nor returned.  */
+static void
+set_binding_values (domainname, dirnamep, codesetp)
      const char *domainname;
-     const char *dirname;
+     const char **dirnamep;
+     const char **codesetp;
 {
   struct binding *binding;
-  char *result;
+  int modified;
 
   /* Some sanity checks.  */
   if (domainname == NULL || domainname[0] == '\0')
-    return NULL;
+    {
+      if (dirnamep)
+	*dirnamep = NULL;
+      if (codesetp)
+	*codesetp = NULL;
+      return;
+    }
 
   __libc_rwlock_wrlock (_nl_state_lock);
 
+  modified = 0;
+
   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
     {
       int compare = strcmp (domainname, binding->domainname);
@@ -112,40 +127,96 @@ BINDTEXTDOMAIN (domainname, dirname)
 	}
     }
 
-  if (dirname == NULL)
-    /* The current binding has be to returned.  */
-    result = binding == NULL ? (char *) _nl_default_dirname : binding->dirname;
-  else if (binding != NULL)
+  if (binding != NULL)
     {
-      /* The domain is already bound.  If the new value and the old
-	 one are equal we simply do nothing.  Otherwise replace the
-	 old binding.  */
-      result = binding->dirname;
-      if (strcmp (dirname, result) != 0)
+      if (dirnamep)
 	{
-	  if (strcmp (dirname, _nl_default_dirname) == 0)
-	    result = (char *) _nl_default_dirname;
+	  const char *dirname = *dirnamep;
+
+	  if (dirname == NULL)
+	    /* The current binding has be to returned.  */
+	    *dirnamep = binding->dirname;
 	  else
 	    {
+	      /* The domain is already bound.  If the new value and the old
+		 one are equal we simply do nothing.  Otherwise replace the
+		 old binding.  */
+	      char *result = binding->dirname;
+	      if (strcmp (dirname, result) != 0)
+		{
+		  if (strcmp (dirname, _nl_default_dirname) == 0)
+		    result = (char *) _nl_default_dirname;
+		  else
+		    {
 #if defined _LIBC || defined HAVE_STRDUP
-	      result = strdup (dirname);
+		      result = strdup (dirname);
 #else
-	      size_t len = strlen (dirname) + 1;
-	      result = (char *) malloc (len);
-	      if (result != NULL)
-		memcpy (result, dirname, len);
+		      size_t len = strlen (dirname) + 1;
+		      result = (char *) malloc (len);
+		      if (__builtin_expect (result != NULL, 1))
+			memcpy (result, dirname, len);
 #endif
+		    }
+
+		  if (__builtin_expect (result != NULL, 1))
+		    {
+		      if (binding->dirname != _nl_default_dirname)
+			free (binding->dirname);
+
+		      binding->dirname = result;
+		      modified = 1;
+		    }
+		}
+	      *dirnamep = result;
 	    }
+	}
+
+      if (codesetp)
+	{
+	  const char *codeset = *codesetp;
 
-	  if (result != NULL)
+	  if (codeset == NULL)
+	    /* The current binding has be to returned.  */
+	    *codesetp = binding->codeset;
+	  else
 	    {
-	      if (binding->dirname != _nl_default_dirname)
-		free (binding->dirname);
+	      /* The domain is already bound.  If the new value and the old
+		 one are equal we simply do nothing.  Otherwise replace the
+		 old binding.  */
+	      char *result = binding->codeset;
+	      if (result == NULL || strcmp (codeset, result) != 0)
+		{
+#if defined _LIBC || defined HAVE_STRDUP
+		  result = strdup (codeset);
+#else
+		  size_t len = strlen (codeset) + 1;
+		  result = (char *) malloc (len);
+		  if (__builtin_expect (result != NULL, 1))
+		    memcpy (result, codeset, len);
+#endif
 
-	      binding->dirname = result;
+		  if (__builtin_expect (result != NULL, 1))
+		    {
+		      if (binding->codeset != NULL)
+			free (binding->codeset);
+
+		      binding->codeset = result;
+		      modified = 1;
+		    }
+		}
+	      *codesetp = result;
 	    }
 	}
     }
+  else if ((dirnamep == NULL || *dirnamep == NULL)
+	   && (codesetp == NULL || *codesetp == NULL))
+    {
+      /* Simply return the default values.  */
+      if (dirnamep)
+	*dirnamep = _nl_default_dirname;
+      if (codesetp)
+	*codesetp = NULL;
+    }
   else
     {
       /* We have to create a new binding.  */
@@ -153,61 +224,140 @@ BINDTEXTDOMAIN (domainname, dirname)
       struct binding *new_binding =
 	(struct binding *) malloc (sizeof (*new_binding) + len);
 
-      if (new_binding == NULL)
-	result = NULL;
-      else
+      if (__builtin_expect (new_binding == NULL, 0))
+	goto failed;
+
+      memcpy (new_binding->domainname, domainname, len);
+
+      if (dirnamep)
 	{
-	  memcpy (new_binding->domainname, domainname, len);
+	  const char *dirname = *dirnamep;
 
-	  if (strcmp (dirname, _nl_default_dirname) == 0)
-	    result = new_binding->dirname = (char *) _nl_default_dirname;
+	  if (dirname == NULL)
+	    /* The default value.  */
+	    dirname = _nl_default_dirname;
 	  else
 	    {
+	      if (strcmp (dirname, _nl_default_dirname) == 0)
+		dirname = _nl_default_dirname;
+	      else
+		{
+		  char *result;
 #if defined _LIBC || defined HAVE_STRDUP
-	      result = new_binding->dirname = strdup (dirname);
+		  result = strdup (dirname);
+		  if (__builtin_expect (result == NULL, 0))
+		    goto failed_dirname;
 #else
-	      len = strlen (dirname) + 1;
-	      result = new_binding->dirname = (char *) malloc (len);
-	      if (result != NULL)
-		memcpy (new_binding->dirname, dirname, len);
+		  size_t len = strlen (dirname) + 1;
+		  result = (char *) malloc (len);
+		  if (__builtin_expect (result == NULL, 0))
+		    goto failed_dirname;
+		  memcpy (result, dirname, len);
 #endif
+		  dirname = result;
+		}
 	    }
+	  *dirnamep = dirname;
+	  new_binding->dirname = (char *) dirname;
 	}
+      else
+	/* The default value.  */
+	new_binding->dirname = (char *) _nl_default_dirname;
 
-      if (result != NULL)
+      if (codesetp)
 	{
-	  /* Now enqueue it.  */
-	  if (_nl_domain_bindings == NULL
-	      || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
-	    {
-	      new_binding->next = _nl_domain_bindings;
-	      _nl_domain_bindings = new_binding;
-	    }
-	  else
+	  const char *codeset = *codesetp;
+
+	  if (codeset != NULL)
 	    {
-	      binding = _nl_domain_bindings;
-	      while (binding->next != NULL
-		     && strcmp (domainname, binding->next->domainname) > 0)
-		binding = binding->next;
+	      char *result;
 
-	      new_binding->next = binding->next;
-	      binding->next = new_binding;
+#if defined _LIBC || defined HAVE_STRDUP
+	      result = strdup (codeset);
+	      if (__builtin_expect (result == NULL, 0))
+		goto failed_codeset;
+#else
+	      size_t len = strlen (codeset) + 1;
+	      result = (char *) malloc (len);
+	      if (__builtin_expect (result == NULL, 0))
+		goto failed_codeset;
+	      memcpy (result, codeset, len);
+#endif
+	      codeset = result;
 	    }
+	  *codesetp = codeset;
+	  new_binding->codeset = (char *) codeset;
+	}
+      else
+	new_binding->codeset = NULL;
+
+      /* Now enqueue it.  */
+      if (_nl_domain_bindings == NULL
+	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
+	{
+	  new_binding->next = _nl_domain_bindings;
+	  _nl_domain_bindings = new_binding;
+	}
+      else
+	{
+	  binding = _nl_domain_bindings;
+	  while (binding->next != NULL
+		 && strcmp (domainname, binding->next->domainname) > 0)
+	    binding = binding->next;
+
+	  new_binding->next = binding->next;
+	  binding->next = new_binding;
+	}
+
+      modified = 1;
+
+      /* Here we deal with memory allocation failures.  */
+      if (0)
+	{
+	failed_codeset:
+	  if (new_binding->dirname != _nl_default_dirname)
+	    free (new_binding->dirname);
+	failed_dirname:
+	  free (new_binding);
+	failed:
+	  if (dirnamep)
+	    *dirnamep = NULL;
+	  if (codesetp)
+	    *codesetp = NULL;
 	}
-      else if (new_binding != NULL)
-	free (new_binding);
     }
 
-  /* For a succesful call we flush the caches.  */
-  if (result != NULL)
+  /* If we modified any binding, we flush the caches.  */
+  if (modified)
     ++_nl_msg_cat_cntr;
 
   __libc_rwlock_unlock (_nl_state_lock);
+}
+
+/* Specify that the DOMAINNAME message catalog will be found
+   in DIRNAME rather than in the system locale data base.  */
+char *
+BINDTEXTDOMAIN (domainname, dirname)
+     const char *domainname;
+     const char *dirname;
+{
+  set_binding_values (domainname, &dirname, NULL);
+  return (char *) dirname;
+}
 
-  return result;
+/* Specify the character encoding in which the messages from the
+   DOMAINNAME message catalog will be returned.  */
+char *
+BIND_TEXTDOMAIN_CODESET (domainname, codeset)
+     const char *domainname;
+     const char *codeset;
+{
+  set_binding_values (domainname, NULL, &codeset);
+  return (char *) codeset;
 }
 
 #ifdef _LIBC
-/* Alias for function name in GNU C Library.  */
+/* Aliases for function names in GNU C Library.  */
 weak_alias (__bindtextdomain, bindtextdomain);
+weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
 #endif
diff --git a/intl/dcigettext.c b/intl/dcigettext.c
index 4f27cbb3d8..92732582f9 100644
--- a/intl/dcigettext.c
+++ b/intl/dcigettext.c
@@ -230,7 +230,7 @@ transcmp (const void *p1, const void *p2)
   result = strcmp (s1->msgid, s2->msgid);
   if (result == 0)
     {
-      result = strcmp (s1->msgid, s2->msgid);
+      result = strcmp (s1->domain, s2->domain);
       if (result == 0)
 	{
 	  result = s1->plindex - s2->plindex;
@@ -362,7 +362,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 #if defined HAVE_TSEARCH || defined _LIBC
   struct known_translation_t *search;
   struct known_translation_t **foundp = NULL;
-  size_t msgid_len = strlen (msgid1) + 1;
+  size_t msgid_len;
 #endif
   size_t domainname_len;
 
@@ -372,7 +372,15 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 
   __libc_rwlock_rdlock (_nl_state_lock);
 
+  /* If DOMAINNAME is NULL, we are interested in the default domain.  If
+     CATEGORY is not LC_MESSAGES this might not make much sense but the
+     definition left this undefined.  */
+  if (domainname == NULL)
+    domainname = _nl_current_default_domain;
+
 #if defined HAVE_TSEARCH || defined _LIBC
+  msgid_len = strlen (msgid1) + 1;
+
   if (plural == 0)
     {
       /* Try to find the translation among those which we found at
@@ -399,12 +407,6 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
   /* See whether this is a SUID binary or not.  */
   DETERMINE_SECURE;
 
-  /* If DOMAINNAME is NULL, we are interested in the default domain.  If
-     CATEGORY is not LC_MESSAGES this might not make much sense but the
-     definition left this undefined.  */
-  if (domainname == NULL)
-    domainname = _nl_current_default_domain;
-
   /* First find matching binding.  */
   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
     {
@@ -529,7 +531,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 
       /* Find structure describing the message catalog matching the
 	 DOMAINNAME and CATEGORY.  */
-      domain = _nl_find_domain (dirname, single_locale, xdomainname);
+      domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
 
       if (domain != NULL)
 	{
@@ -605,7 +607,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
 		      /* Insert the entry in the search tree.  */
 		      foundp = (struct known_translation_t **)
 			tsearch (newp, &root, transcmp);
-		      if (&newp != foundp)
+		      if (__builtin_expect (&newp != foundp, 0))
 			/* The insert failed.  */
 			free (newp);
 		    }
@@ -751,7 +753,7 @@ _nl_find_msg (domain_file, msgid, index)
 	/* Mark that we didn't succeed allocating a table.  */
 	domain->conv_tab = (char **) -1;
 
-      if (domain->conv_tab == (char **) -1)
+      if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
 	/* Nothing we can do, no more memory.  */
 	goto converted;
 
@@ -787,59 +789,61 @@ _nl_find_msg (domain_file, msgid, index)
 
 	  __libc_lock_lock (lock);
 
+	  while (1)
+	    {
 # ifdef _LIBC
-	  {
-	    size_t written;
-	    int res;
-
-	    while ((res = __gconv (domain->conv,
-				   &inbuf, inbuf + resultlen,
-				   &outbuf, outbuf + freemem_size,
-				   &written)) == __GCONV_OK)
-	      {
-		if (res != __GCONV_FULL_OUTPUT)
-		  goto out;
-
-		/* We must resize the buffer.  */
-		freemem_size = MAX (2 * freemem_size, 4064);
-		freemem = (char *) malloc (freemem_size);
-		if (freemem == NULL)
-		  goto out;
-
-		inbuf = result;
-		outbuf = freemem + 4;
-	      }
-	  }
+	      size_t non_reversible;
+	      int res;
+
+	      res = __gconv (domain->conv,
+			     &inbuf, inbuf + resultlen,
+			     &outbuf, outbuf + freemem_size,
+			     &non_reversible);
+
+	      if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
+		break;
+
+	      if (res != __GCONV_FULL_OUTPUT)
+		{
+		  __libc_lock_unlock (lock);
+		  goto converted;
+		}
+
+	      inbuf = result;
 # else
 #  if HAVE_ICONV
-	  for (;;)
-	    {
 	      const char *inptr = (const char *) inbuf;
 	      size_t inleft = resultlen;
 	      char *outptr = (char *) outbuf;
 	      size_t outleft = freemem_size;
 
 	      if (iconv (domain->conv, &inptr, &inleft, &outptr, &outleft)
-		  != (size_t)(-1))
+		  != (size_t) (-1))
 		{
 		  outbuf = (unsigned char *) outptr;
 		  break;
 		}
 	      if (errno != E2BIG)
-		goto out;
+		{
+		  __libc_lock_unlock (lock);
+		  goto converted;
+		}
+#  endif
+# endif
 
 	      /* We must resize the buffer.  */
 	      freemem_size = 2 * freemem_size;
 	      if (freemem_size < 4064)
 		freemem_size = 4064;
 	      freemem = (char *) malloc (freemem_size);
-	      if (freemem == NULL)
-		goto out;
+	      if (__builtin_expect (freemem == NULL, 0))
+		{
+		  __libc_lock_unlock (lock);
+		  goto converted;
+		}
 
 	      outbuf = freemem + 4;
 	    }
-#  endif
-# endif
 
 	  /* We have now in our buffer a converted string.  Put this
 	     into the table of conversions.  */
@@ -848,10 +852,9 @@ _nl_find_msg (domain_file, msgid, index)
 	  /* Shrink freemem, but keep it aligned.  */
 	  freemem_size -= outbuf - freemem;
 	  freemem = outbuf;
-	  freemem += freemem_size & 3;
-	  freemem_size = freemem_size & ~3;
+	  freemem += freemem_size & (__alignof__ (nls_uint32) - 1);
+	  freemem_size = freemem_size & ~ (__alignof__ (nls_uint32) - 1);
 
-	out:
 	  __libc_lock_unlock (lock);
 	}
 
@@ -1070,15 +1073,19 @@ free_mem (void)
   struct binding *runp;
 
   for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
-    if (runp->dirname != _nl_default_dirname)
-      /* Yes, this is a pointer comparison.  */
-      free (runp->dirname);
+    {
+      if (runp->dirname != _nl_default_dirname)
+	/* Yes, this is a pointer comparison.  */
+	free (runp->dirname);
+      if (runp->codeset != NULL)
+	free (runp->codeset);
+    }
 
   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.  */
+  /* Remove the search tree with the known translations.  */
   __tdestroy (root, free);
 }
 
diff --git a/intl/finddomain.c b/intl/finddomain.c
index 09f6a91fc2..dc215ad126 100644
--- a/intl/finddomain.c
+++ b/intl/finddomain.c
@@ -67,10 +67,11 @@ static struct loaded_l10nfile *_nl_loaded_domains;
    established bindings.  */
 struct loaded_l10nfile *
 internal_function
-_nl_find_domain (dirname, locale, domainname)
+_nl_find_domain (dirname, locale, domainname, domainbinding)
      const char *dirname;
      char *locale;
      const char *domainname;
+     struct binding *domainbinding;
 {
   struct loaded_l10nfile *retval;
   const char *language;
@@ -109,7 +110,8 @@ _nl_find_domain (dirname, locale, domainname)
      be one data set in the list of loaded domains.  */
   retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
 			       strlen (dirname) + 1, 0, locale, NULL, NULL,
-			       NULL, NULL, NULL, NULL, NULL, domainname, 0);
+			       NULL, NULL, NULL, NULL, NULL, domainname,
+			       domainbinding, 0);
   if (retval != NULL)
     {
       /* We know something about this locale.  */
@@ -165,7 +167,8 @@ _nl_find_domain (dirname, locale, domainname)
   retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
 			       strlen (dirname) + 1, mask, language, territory,
 			       codeset, normalized_codeset, modifier, special,
-			       sponsor, revision, domainname, 1);
+			       sponsor, revision, domainname, domainbinding,
+			       1);
   if (retval == NULL)
     /* This means we are out of core.  */
     return NULL;
diff --git a/intl/gettextP.h b/intl/gettextP.h
index 26d9de0e77..8dcbaf2859 100644
--- a/intl/gettextP.h
+++ b/intl/gettextP.h
@@ -44,6 +44,12 @@
 # define internal_function
 #endif
 
+/* Tell the compiler when a conditional or integer expression is
+   almost always true or almost always false.  */
+#ifndef HAVE_BUILTIN_EXPECT
+# define __builtin_expect(expr, val) (expr)
+#endif
+
 #ifndef W
 # define W(flag, data) ((flag) ? SWAP (data) : (data))
 #endif
@@ -137,6 +143,7 @@ struct binding
 {
   struct binding *next;
   char *dirname;
+  char *codeset;
 #ifdef __GNUC__
   char domainname[0];
 #else
@@ -148,7 +155,8 @@ extern int _nl_msg_cat_cntr;
 
 struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname,
 						 char *__locale,
-						 const char *__domainname))
+						 const char *__domainname,
+					      struct binding *__domainbinding))
      internal_function;
 void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain))
      internal_function;
diff --git a/intl/l10nflist.c b/intl/l10nflist.c
index 01e7e74956..4cd6615bac 100644
--- a/intl/l10nflist.c
+++ b/intl/l10nflist.c
@@ -175,7 +175,7 @@ pop (x)
 struct loaded_l10nfile *
 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
 		    territory, codeset, normalized_codeset, modifier, special,
-		    sponsor, revision, filename, do_allocate)
+		    sponsor, revision, filename, domainbinding, do_allocate)
      struct loaded_l10nfile **l10nfile_list;
      const char *dirlist;
      size_t dirlist_len;
@@ -189,6 +189,7 @@ _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
      const char *sponsor;
      const char *revision;
      const char *filename;
+     struct binding *domainbinding;
      int do_allocate;
 {
   char *abs_filename;
@@ -309,6 +310,7 @@ _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
     return NULL;
 
   retval->filename = abs_filename;
+  retval->domainbinding = domainbinding;
   retval->decided = (__argz_count (dirlist, dirlist_len) != 1
 		     || ((mask & XPG_CODESET) != 0
 			 && (mask & XPG_NORM_CODESET) != 0));
@@ -344,7 +346,8 @@ _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
 	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
 				  language, territory, codeset,
 				  normalized_codeset, modifier, special,
-				  sponsor, revision, filename, 1);
+				  sponsor, revision, filename, domainbinding,
+				  1);
       }
   retval->successor[entries] = NULL;
 
diff --git a/intl/libintl.h b/intl/libintl.h
index 0f147228df..967a8cafcd 100644
--- a/intl/libintl.h
+++ b/intl/libintl.h
@@ -78,6 +78,11 @@ extern char *textdomain (__const char *__domainname) __THROW;
 extern char *bindtextdomain (__const char *__domainname,
 			     __const char *__dirname) __THROW;
 
+/* Specify the character encoding in which the messages from the
+   DOMAINNAME message catalog will be returned.  */
+extern char *bind_textdomain_codeset (__const char *__domainname,
+				      __const char *__codeset) __THROW;
+
 
 /* Optimized version of the function above.  */
 #if defined __OPTIMIZE__
diff --git a/intl/loadinfo.h b/intl/loadinfo.h
index 585ed11c5e..1013470f43 100644
--- a/intl/loadinfo.h
+++ b/intl/loadinfo.h
@@ -32,6 +32,12 @@
 # define internal_function
 #endif
 
+/* Tell the compiler when a conditional or integer expression is
+   almost always true or almost always false.  */
+#ifndef HAVE_BUILTIN_EXPECT
+# define __builtin_expect(expr, val) (expr)
+#endif
+
 /* Encoding of locale name parts.  */
 #define CEN_REVISION		1
 #define CEN_SPONSOR		2
@@ -49,6 +55,7 @@
 struct loaded_l10nfile
 {
   const char *filename;
+  struct binding *domainbinding;
   int decided;
 
   const void *data;
@@ -73,7 +80,8 @@ _nl_make_l10nflist PARAMS ((struct loaded_l10nfile **l10nfile_list,
 			    const char *normalized_codeset,
 			    const char *modifier, const char *special,
 			    const char *sponsor, const char *revision,
-			    const char *filename, int do_allocate));
+			    const char *filename,
+			    struct binding *domainbinding, int do_allocate));
 
 
 extern const char *_nl_expand_alias PARAMS ((const char *name));
diff --git a/intl/loadmsgcat.c b/intl/loadmsgcat.c
index 75265c84a8..4009525fe8 100644
--- a/intl/loadmsgcat.c
+++ b/intl/loadmsgcat.c
@@ -16,11 +16,19 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE    1
+#endif
+
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
 
 #include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -30,9 +38,6 @@
 #endif
 
 #if defined HAVE_STRING_H || defined _LIBC
-# ifndef _GNU_SOURCE
-#  define _GNU_SOURCE	1
-# endif
 # include <string.h>
 #else
 # include <strings.h>
@@ -138,9 +143,9 @@ _nl_load_domain (domain_file)
     return;
 
   /* We must know about the size of the file.  */
-  if (fstat (fd, &st) != 0
-      || (size = (size_t) st.st_size) != st.st_size
-      || size < sizeof (struct mo_file_header))
+  if (__builtin_expect (fstat (fd, &st) != 0, 0)
+      || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0)
+      || __builtin_expect (size < sizeof (struct mo_file_header), 0))
     {
       /* Something went wrong.  */
       close (fd);
@@ -153,7 +158,7 @@ _nl_load_domain (domain_file)
   data = (struct mo_file_header *) mmap (NULL, size, PROT_READ,
 					 MAP_PRIVATE, fd, 0);
 
-  if (data != (struct mo_file_header *) -1)
+  if (__builtin_expect (data != (struct mo_file_header *) -1, 1))
     {
       /* mmap() call was successful.  */
       close (fd);
@@ -177,12 +182,15 @@ _nl_load_domain (domain_file)
       do
 	{
 	  long int nb = (long int) read (fd, read_ptr, to_read);
-	  if (nb == -1)
+	  if (nb <= 0)
 	    {
+#ifdef EINTR
+	      if (nb == -1 && errno == EINTR)
+		continue;
+#endif
 	      close (fd);
 	      return;
 	    }
-
 	  read_ptr += nb;
 	  to_read -= nb;
 	}
@@ -193,7 +201,8 @@ _nl_load_domain (domain_file)
 
   /* Using the magic number we can test whether it really is a message
      catalog file.  */
-  if (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED)
+  if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED,
+			0))
     {
       /* The magic number is wrong: not a message catalog file.  */
 #ifdef HAVE_MMAP
@@ -254,14 +263,13 @@ _nl_load_domain (domain_file)
   domain->conv = (iconv_t) -1;
 # endif
 #endif
+  domain->conv_tab = NULL;
   nullentry = _nl_find_msg (domain_file, "", 0);
   if (nullentry != NULL)
     {
+#if defined _LIBC || HAVE_ICONV
       const char *charsetstr;
-      const char *plural;
-      const char *nplurals;
 
-#if defined _LIBC || HAVE_ICONV
       charsetstr = strstr (nullentry, "charset=");
       if (charsetstr != NULL)
 	{
@@ -282,12 +290,33 @@ _nl_load_domain (domain_file)
 
 	  /* The output charset should normally be determined by the
 	     locale.  But sometimes the locale is not used or not correctly
-	     set up so we provide a possibility to override this.  */
-	  outcharset = getenv ("OUTPUT_CHARSET");
-	  if (outcharset == NULL || outcharset[0] == '\0')
-	    outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
+	     set up, so we provide a possibility for the user to override
+	     this.  Moreover, the value specified through
+	     bind_textdomain_codeset overrides both.  */
+	  if (domain_file->domainbinding != NULL
+	      && domain_file->domainbinding->codeset != NULL)
+	    outcharset = domain_file->domainbinding->codeset;
+	  else
+	    {
+	      outcharset = getenv ("OUTPUT_CHARSET");
+	      if (outcharset == NULL || outcharset[0] == '\0')
+		{
+# ifdef _LIBC
+		  outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
+# else
+#  if HAVE_ICONV
+		  extern const char *locale_charset (void);
+		  outcharset = locale_charset ();
+		  if (outcharset == NULL)
+		    outcharset = "";
+#  endif
+# endif
+		}
+	    }
 
 # ifdef _LIBC
+	  outcharset = norm_add_slashes (outcharset);
+	  charset = norm_add_slashes (charset);
 	  if (__gconv_open (outcharset, charset, &domain->conv,
 			    GCONV_AVOID_NOCONV)
 	      != __GCONV_OK)
@@ -299,19 +328,18 @@ _nl_load_domain (domain_file)
 # endif
 	}
 #endif /* _LIBC || HAVE_ICONV */
+    }
+
+  /* Also look for a plural specification.  */
+  if (nullentry != NULL)
+    {
+      const char *plural;
+      const char *nplurals;
 
-      /* Also look for a plural specification.  */
       plural = strstr (nullentry, "plural=");
       nplurals = strstr (nullentry, "nplurals=");
       if (plural == NULL || nplurals == NULL)
-	{
-	  /* By default we are using the Germanic form: singular form only
-	     for `one', the plural form otherwise.  Yes, this is also what
-	     English is using since English is a Germanic language.  */
-	no_plural:
-	  domain->plural = &germanic_plural;
-	  domain->nplurals = 2;
-	}
+	goto no_plural;
       else
 	{
 	  /* First get the number.  */
@@ -336,6 +364,15 @@ _nl_load_domain (domain_file)
 	  domain->plural = args.res;
 	}
     }
+  else
+    {
+      /* By default we are using the Germanic form: singular form only
+         for `one', the plural form otherwise.  Yes, this is also what
+         English is using since English is a Germanic language.  */
+    no_plural:
+      domain->plural = &germanic_plural;
+      domain->nplurals = 2;
+    }
 }
 
 
diff --git a/intl/localealias.c b/intl/localealias.c
index d2b5ae8895..57c693dcde 100644
--- a/intl/localealias.c
+++ b/intl/localealias.c
@@ -16,6 +16,13 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE    1
+#endif
+
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
@@ -53,9 +60,6 @@ void free ();
 #endif
 
 #if defined HAVE_STRING_H || defined _LIBC
-# ifndef _GNU_SOURCE
-#  define _GNU_SOURCE	1
-# endif
 # include <string.h>
 #else
 # include <strings.h>
@@ -158,7 +162,7 @@ static size_t maxmap;
 /* Prototypes for local functions.  */
 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
      internal_function;
-static void extend_alias_table PARAMS ((void));
+static int extend_alias_table PARAMS ((void));
 static int alias_compare PARAMS ((const struct alias_map *map1,
 				  const struct alias_map *map2));
 
@@ -326,7 +330,11 @@ read_alias_file (fname, fname_len)
 		*cp++ = '\0';
 
 	      if (nmap >= maxmap)
-		extend_alias_table ();
+		if (__builtin_expect (extend_alias_table (), 0))
+		  {
+		    FREE_BLOCKS (block_list);
+		    return added;
+		  }
 
 	      alias_len = strlen (alias) + 1;
 	      value_len = strlen (value) + 1;
@@ -374,7 +382,7 @@ read_alias_file (fname, fname_len)
 }
 
 
-static void
+static int
 extend_alias_table ()
 {
   size_t new_size;
@@ -385,10 +393,11 @@ extend_alias_table ()
 						* sizeof (struct alias_map)));
   if (new_map == NULL)
     /* Simply don't extend: we don't have any more core.  */
-    return;
+    return -1;
 
   map = new_map;
   maxmap = new_size;
+  return 0;
 }