diff --git a/include/programs/xasprintf.h b/include/programs/xasprintf.h
new file mode 100644
index 0000000..53193ba
--- /dev/null
+++ b/include/programs/xasprintf.h
@@ -0,0 +1,24 @@
+/* asprintf with out of memory checking
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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; version 2 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/>.  */
+
+#ifndef _XASPRINTF_H
+#define _XASPRINTF_H	1
+
+extern char *xasprintf (const char *format, ...)
+    __attribute__ ((__format__ (__printf__, 1, 2), __warn_unused_result__));
+
+#endif /* xasprintf.h */
diff --git a/locale/Makefile b/locale/Makefile
index 1a19b6f..943df35 100644
--- a/locale/Makefile
+++ b/locale/Makefile
@@ -56,7 +56,7 @@
 localedef-aux		:= md5
 locale-modules		:= locale locale-spec
 lib-modules		:= charmap-dir simple-hash xmalloc xstrdup \
-			   record-status
+			   record-status xasprintf
 
 
 GPERF = gperf
diff --git a/locale/programs/localedef.c b/locale/programs/localedef.c
index 3dcf15f..b926117 100644
--- a/locale/programs/localedef.c
+++ b/locale/programs/localedef.c
@@ -434,20 +434,16 @@
     {
     case ARGP_KEY_HELP_EXTRA:
       /* We print some extra information.  */
-      if (asprintf (&tp, gettext ("\
+      tp = xasprintf (gettext ("\
 For bug reporting instructions, please see:\n\
-%s.\n"), REPORT_BUGS_TO) < 0)
-	return NULL;
-      if (asprintf (&cp, gettext ("\
+%s.\n"), REPORT_BUGS_TO);
+      cp = xasprintf (gettext ("\
 System's directory for character maps : %s\n\
 		       repertoire maps: %s\n\
 		       locale path    : %s\n\
 %s"),
-		    CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp) < 0)
-	{
-	  free (tp);
-	  return NULL;
-	}
+		    CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp);
+      free (tp);
       return cp;
     default:
       break;
@@ -477,15 +473,13 @@
 }
 
 
-/* The parameter to localedef describes the output path.  If it does
-   contain a '/' character it is a relative path.  Otherwise it names the
-   locale this definition is for.  */
+/* The parameter to localedef describes the output path.  If it does contain a
+   '/' character it is a relative path.  Otherwise it names the locale this
+   definition is for.   The returned path must be freed by the caller. */
 static const char *
 construct_output_path (char *path)
 {
-  const char *normal = NULL;
   char *result;
-  char *endp;
 
   if (strchr (path, '/') == NULL)
     {
@@ -493,50 +487,44 @@
 	 contains a reference to the codeset.  This should be
 	 normalized.  */
       char *startp;
+      char *endp = NULL;
+      const char *normal = NULL;
 
       startp = path;
-      /* We must be prepared for finding a CEN name or a location of
-	 the introducing `.' where it is not possible anymore.  */
+      /* Either we have a '@' which starts a CEN name or '.' which starts the
+	 codeset specification.  The CEN name starts with '@' and may also have
+	 a codeset specification, but we do not normalize the string after '@'.
+	 If we only find the codeset specification then we normalize only the codeset
+	 specification (but not anything after a subsequent '@').  */
       while (*startp != '\0' && *startp != '@' && *startp != '.')
 	++startp;
       if (*startp == '.')
 	{
 	  /* We found a codeset specification.  Now find the end.  */
 	  endp = ++startp;
+
+	  /* Stop at the first '@', and don't normalize anything past that.  */
 	  while (*endp != '\0' && *endp != '@')
 	    ++endp;
 
 	  if (endp > startp)
 	    normal = normalize_codeset (startp, endp - startp);
 	}
-      else
-	/* This is to keep gcc quiet.  */
-	endp = NULL;
 
-      /* We put an additional '\0' at the end of the string because at
-	 the end of the function we need another byte for the trailing
-	 '/'.  */
-      ssize_t n;
       if (normal == NULL)
-	n = asprintf (&result, "%s%s/%s%c", output_prefix ?: "",
-		      COMPLOCALEDIR, path, '\0');
+	result = xasprintf ("%s%s/%s/", output_prefix ?: "",
+			    COMPLOCALEDIR, path);
       else
-	n = asprintf (&result, "%s%s/%.*s%s%s%c",
-		      output_prefix ?: "", COMPLOCALEDIR,
-		      (int) (startp - path), path, normal, endp, '\0');
-
-      if (n < 0)
-	return NULL;
-
-      endp = result + n - 1;
+	result = xasprintf ("%s%s/%.*s%s%s/",
+			    output_prefix ?: "", COMPLOCALEDIR,
+			    (int) (startp - path), path, normal, endp ?: "");
+      /* Free the allocated normalized codeset name.  */
+      free ((char *) normal);
     }
   else
     {
-      /* This is a user path.  Please note the additional byte in the
-	 memory allocation.  */
-      size_t len = strlen (path) + 1;
-      result = xmalloc (len + 1);
-      endp = mempcpy (result, path, len) - 1;
+      /* This is a user path.  */
+      result = xasprintf ("%s/", path);
 
       /* If the user specified an output path we cannot add the output
 	 to the archive.  */
@@ -546,24 +534,40 @@
   errno = 0;
 
   if (no_archive && euidaccess (result, W_OK) == -1)
-    /* Perhaps the directory does not exist now.  Try to create it.  */
-    if (errno == ENOENT)
-      {
-	errno = 0;
-	if (mkdir (result, 0777) < 0)
-	  return NULL;
-      }
-
-  *endp++ = '/';
-  *endp = '\0';
+    {
+      /* Perhaps the directory does not exist now.  Try to create it.  */
+      if (errno == ENOENT)
+	{
+	  errno = 0;
+	  if (mkdir (result, 0777) < 0)
+	    {
+	      record_verbose (stderr,
+			      _("cannot create output path \"%s\": %s"),
+			      result, strerror (errno));
+	      free (result);
+	      return NULL;
+	    }
+	}
+      else
+	record_verbose (stderr,
+			_("no write permission to output path \"%s\": %s"),
+			result, strerror (errno));
+    }
 
   return result;
 }
 
 
-/* Normalize codeset name.  There is no standard for the codeset
-   names.  Normalization allows the user to use any of the common
-   names.  */
+/* Normalize codeset name.  There is no standard for the codeset names.
+   Normalization allows the user to use any of the common names e.g. UTF-8,
+   utf-8, utf8, UTF8 etc.
+
+   We normalize using the following rules:
+   - Remove all non-alpha-numeric characters
+   - Lowercase all cahracters.
+   - If there are only digits assume it's an ISO standard and prefix with 'iso'
+
+   We return the normalized string which needs to be freed by free.  */
 static const char *
 normalize_codeset (const char *codeset, size_t name_len)
 {
@@ -573,6 +577,7 @@
   char *wp;
   size_t cnt;
 
+  /* Compute the length of only the alpha-numeric characters.  */
   for (cnt = 0; cnt < name_len; ++cnt)
     if (isalnum (codeset[cnt]))
       {
@@ -582,6 +587,8 @@
 	  only_digit = 0;
       }
 
+  /* If there were only digits we assume it's an ISO standard and we will
+     prefix with 'iso' so include space for that.  */
   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
 
   if (retval != NULL)
@@ -592,6 +599,7 @@
 	wp = retval;
 
       for (cnt = 0; cnt < name_len; ++cnt)
+	/* Lowercase all characters. */
 	if (isalpha (codeset[cnt]))
 	  *wp++ = tolower (codeset[cnt]);
 	else if (isdigit (codeset[cnt]))
@@ -600,6 +608,7 @@
       *wp = '\0';
     }
 
+  /* Return allocated and converted name for caller to free.  */
   return (const char *) retval;
 }
 
diff --git a/locale/programs/localedef.h b/locale/programs/localedef.h
index 80da0b0..ad2a927 100644
--- a/locale/programs/localedef.h
+++ b/locale/programs/localedef.h
@@ -123,6 +123,7 @@
 
 /* Prototypes for a few program-wide used functions.  */
 #include <programs/xmalloc.h>
+#include <programs/xasprintf.h>
 
 
 /* Mark given locale as to be read.  */
diff --git a/locale/programs/xasprintf.c b/locale/programs/xasprintf.c
new file mode 100644
index 0000000..efc91a9
--- /dev/null
+++ b/locale/programs/xasprintf.c
@@ -0,0 +1,34 @@
+/* asprintf with out of memory checking
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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; version 2 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/>.  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <libintl.h>
+#include <error.h>
+
+char *
+xasprintf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  char *result;
+  if (vasprintf (&result, format, ap) < 0)
+    error (EXIT_FAILURE, 0, _("memory exhausted"));
+  va_end (ap);
+  return result;
+}
