0b26f7
commit 3fc51f35b4f32e1bb99d85c1578e930e725ff929
0b26f7
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
0b26f7
Date:   Mon Sep 13 20:48:35 2021 +0530
0b26f7
0b26f7
    iconvconfig: Fix behaviour with --prefix [BZ #28199]
0b26f7
    
0b26f7
    The consolidation of configuration parsing broke behaviour with
0b26f7
    --prefix, where the prefix bled into the modules cache.  Accept a
0b26f7
    prefix which, when non-NULL, is prepended to the path when looking for
0b26f7
    configuration files but only the original directory is added to the
0b26f7
    modules cache.
0b26f7
    
0b26f7
    This has no effect on the codegen of gconv_conf since it passes NULL.
0b26f7
    
0b26f7
    Reported-by: Patrick McCarty <patrick.mccarty@intel.com>
0b26f7
    Reported-by: Michael Hudson-Doyle <michael.hudson@canonical.com>
0b26f7
    Reviewed-by: Andreas Schwab <schwab@linux-m68k.org>
0b26f7
    (cherry picked from commit 43cea6d5652b6b9e61ac6ecc69419c909b504f47)
0b26f7
0b26f7
diff --git a/iconv/gconv_conf.c b/iconv/gconv_conf.c
0b26f7
index 62bee28769deb979..cc391d8f936687f3 100644
0b26f7
--- a/iconv/gconv_conf.c
0b26f7
+++ b/iconv/gconv_conf.c
0b26f7
@@ -478,7 +478,7 @@ __gconv_read_conf (void)
0b26f7
   __gconv_get_path ();
0b26f7
 
0b26f7
   for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt)
0b26f7
-    gconv_parseconfdir (__gconv_path_elem[cnt].name,
0b26f7
+    gconv_parseconfdir (NULL, __gconv_path_elem[cnt].name,
0b26f7
 			__gconv_path_elem[cnt].len);
0b26f7
 #endif
0b26f7
 
0b26f7
diff --git a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h
0b26f7
index 2f062689ecc72749..a586268abc103abd 100644
0b26f7
--- a/iconv/gconv_parseconfdir.h
0b26f7
+++ b/iconv/gconv_parseconfdir.h
0b26f7
@@ -39,7 +39,6 @@
0b26f7
 /* Name of the file containing the module information in the directories
0b26f7
    along the path.  */
0b26f7
 static const char gconv_conf_filename[] = "gconv-modules";
0b26f7
-static const char gconv_conf_dirname[] = "gconv-modules.d";
0b26f7
 
0b26f7
 static void add_alias (char *);
0b26f7
 static void add_module (char *, const char *, size_t, int);
0b26f7
@@ -110,19 +109,28 @@ read_conf_file (const char *filename, const char *directory, size_t dir_len)
0b26f7
   return true;
0b26f7
 }
0b26f7
 
0b26f7
+/* Prefix DIR (with length DIR_LEN) with PREFIX if the latter is non-NULL and
0b26f7
+   parse configuration in it.  */
0b26f7
+
0b26f7
 static __always_inline bool
0b26f7
-gconv_parseconfdir (const char *dir, size_t dir_len)
0b26f7
+gconv_parseconfdir (const char *prefix, const char *dir, size_t dir_len)
0b26f7
 {
0b26f7
-  /* No slash needs to be inserted between dir and gconv_conf_filename;
0b26f7
-     dir already ends in a slash.  */
0b26f7
-  char *buf = malloc (dir_len + sizeof (gconv_conf_dirname));
0b26f7
+  /* No slash needs to be inserted between dir and gconv_conf_filename; dir
0b26f7
+     already ends in a slash.  The additional 2 is to accommodate the ".d"
0b26f7
+     when looking for configuration files in gconv-modules.d.  */
0b26f7
+  size_t buflen = dir_len + sizeof (gconv_conf_filename) + 2;
0b26f7
+  char *buf = malloc (buflen + (prefix != NULL ? strlen (prefix) : 0));
0b26f7
+  char *cp = buf;
0b26f7
   bool found = false;
0b26f7
 
0b26f7
   if (buf == NULL)
0b26f7
     return false;
0b26f7
 
0b26f7
-  char *cp = mempcpy (mempcpy (buf, dir, dir_len), gconv_conf_filename,
0b26f7
-		      sizeof (gconv_conf_filename));
0b26f7
+  if (prefix != NULL)
0b26f7
+    cp = stpcpy (cp, prefix);
0b26f7
+
0b26f7
+  cp = mempcpy (mempcpy (cp, dir, dir_len), gconv_conf_filename,
0b26f7
+		sizeof (gconv_conf_filename));
0b26f7
 
0b26f7
   /* Read the gconv-modules configuration file first.  */
0b26f7
   found = read_conf_file (buf, dir, dir_len);
0b26f7
diff --git a/iconv/iconvconfig.c b/iconv/iconvconfig.c
0b26f7
index 783b2bbdbb684ac6..273a71f67315f670 100644
0b26f7
--- a/iconv/iconvconfig.c
0b26f7
+++ b/iconv/iconvconfig.c
0b26f7
@@ -653,13 +653,21 @@ add_module (char *rp, const char *directory,
0b26f7
 static int
0b26f7
 handle_dir (const char *dir)
0b26f7
 {
0b26f7
+  char *newp = NULL;
0b26f7
   size_t dirlen = strlen (dir);
0b26f7
   bool found = false;
0b26f7
 
0b26f7
-  char *fulldir = xasprintf ("%s%s%s", dir[0] == '/' ? prefix : "",
0b26f7
-			     dir, dir[dirlen - 1] != '/' ? "/" : "");
0b26f7
+  /* End directory path with a '/' if it doesn't already.  */
0b26f7
+  if (dir[dirlen - 1] != '/')
0b26f7
+    {
0b26f7
+      newp = xmalloc (dirlen + 2);
0b26f7
+      memcpy (newp, dir, dirlen);
0b26f7
+      newp[dirlen++] = '/';
0b26f7
+      newp[dirlen] = '\0';
0b26f7
+      dir = newp;
0b26f7
+    }
0b26f7
 
0b26f7
-  found = gconv_parseconfdir (fulldir, strlen (fulldir));
0b26f7
+  found = gconv_parseconfdir (dir[0] == '/' ? prefix : NULL, dir, dirlen);
0b26f7
 
0b26f7
   if (!found)
0b26f7
     {
0b26f7
@@ -671,7 +679,7 @@ handle_dir (const char *dir)
0b26f7
 	     "configuration files with names ending in .conf.");
0b26f7
     }
0b26f7
 
0b26f7
-  free (fulldir);
0b26f7
+  free (newp);
0b26f7
 
0b26f7
   return found ? 0 : 1;
0b26f7
 }