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