Add support for XDG_DATA_DIRS

Add dirs from XDG_DATA_DIRS when <dir prefix="xdg"> appears in fonts.conf

Fixes https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/271
diff --git a/src/fccfg.c b/src/fccfg.c
index 462c423..21fc9b1 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -2614,6 +2614,65 @@
     return ret;
 }
 
+FcStrSet *
+FcConfigXdgDataDirs (void)
+{
+    const char *env = getenv ("XDG_DATA_DIRS");
+    FcStrSet *ret = FcStrSetCreate ();
+
+    if (env)
+    {
+	FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *) env);
+
+	/* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
+	 *   The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
+	 * in doc.
+	 */
+	while (e)
+	{
+	    FcChar8 *p = (FcChar8 *) strchr ((const char *) e, ':');
+	    FcChar8 *s;
+	    size_t len;
+
+	    if (!p)
+	    {
+		s = FcStrCopy (e);
+		e = NULL;
+	    }
+	    else
+	    {
+		*p = 0;
+		s = FcStrCopy (e);
+		e = p + 1;
+	    }
+	    len = strlen ((const char *) s);
+	    if (s[len - 1] == FC_DIR_SEPARATOR)
+	    {
+		do
+		{
+		    len--;
+		}
+		while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
+		s[len] = 0;
+	    }
+	    FcStrSetAdd (ret, s);
+	    FcStrFree (s);
+	}
+	FcStrFree (ee);
+    }
+    else
+    {
+	/* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
+	 *
+	 * If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
+	 */
+	FcStrSetAdd (ret, (const FcChar8 *) "/usr/local/share");
+	FcStrSetAdd (ret, (const FcChar8 *) "/usr/share");
+    }
+
+    return ret;
+}
+
 FcBool
 FcConfigEnableHome (FcBool enable)
 {
diff --git a/src/fcint.h b/src/fcint.h
index 2d4a2c4..4150a05 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -664,6 +664,9 @@
 FcPrivate FcChar8 *
 FcConfigXdgDataHome (void);
 
+FcPrivate FcStrSet *
+FcConfigXdgDataDirs (void);
+
 FcPrivate FcExpr *
 FcConfigAllocExpr (FcConfig *config);
 
@@ -1258,6 +1261,9 @@
 FcStrSetCreateEx (unsigned int control);
 
 FcPrivate FcBool
+FcStrSetInsert (FcStrSet *set, const FcChar8 *s, int pos);
+
+FcPrivate FcBool
 FcStrSetAddLangs (FcStrSet *strs, const char *languages);
 
 FcPrivate void
diff --git a/src/fcstr.c b/src/fcstr.c
index 8971b71..765f711 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -1252,7 +1252,7 @@
 }
 
 static FcBool
-_FcStrSetAppend (FcStrSet *set, FcChar8 *s)
+_FcStrSetInsert (FcStrSet *set, FcChar8 *s, int pos)
 {
     if (!FcStrSetHasControlBit (set, FCSS_ALLOW_DUPLICATES))
     {
@@ -1268,8 +1268,21 @@
         if (!_FcStrSetGrow(set, growElements))
             return FcFalse;
     }
-    set->strs[set->num++] = s;
-    set->strs[set->num] = 0;
+    if (pos >= set->num)
+    {
+	set->strs[set->num++] = s;
+	set->strs[set->num] = 0;
+    }
+    else
+    {
+	int i;
+
+	set->num++;
+	set->strs[set->num] = 0;
+	for (i = set->num - 1; i > pos; i--)
+	    set->strs[i] = set->strs[i - 1];
+	set->strs[pos] = s;
+    }
     return FcTrue;
 }
 
@@ -1354,7 +1367,21 @@
     FcChar8 *new = FcStrCopy (s);
     if (!new)
 	return FcFalse;
-    if (!_FcStrSetAppend (set, new))
+    if (!_FcStrSetInsert (set, new, set->num))
+    {
+	FcStrFree (new);
+	return FcFalse;
+    }
+    return FcTrue;
+}
+
+FcBool
+FcStrSetInsert (FcStrSet *set, const FcChar8 *s, int pos)
+{
+    FcChar8 *new = FcStrCopy (s);
+    if (!new)
+	return FcFalse;
+    if (!_FcStrSetInsert (set, new, pos))
     {
 	FcStrFree (new);
 	return FcFalse;
@@ -1368,7 +1395,7 @@
     FcChar8 *new = FcStrMakeTriple (a, b, c);
     if (!new)
 	return FcFalse;
-    if (!_FcStrSetAppend (set, new))
+    if (!_FcStrSetInsert (set, new, set->num))
     {
 	FcStrFree (new);
 	return FcFalse;
@@ -1403,7 +1430,7 @@
     FcChar8 *new = FcStrCopyFilename (s);
     if (!new)
 	return FcFalse;
-    if (!_FcStrSetAppend (set, new))
+    if (!_FcStrSetInsert (set, new, set->num))
     {
 	FcStrFree (new);
 	return FcFalse;
diff --git a/src/fcxml.c b/src/fcxml.c
index 9efe157..0db318c 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -1285,20 +1285,22 @@
     return 0;
 }
 
-static FcChar8 *
-_get_real_path_from_prefix(FcConfigParse *parse, const FcChar8 *path, const FcChar8 *prefix)
+static FcStrSet *
+_get_real_paths_from_prefix(FcConfigParse *parse, const FcChar8 *path, const FcChar8 *prefix)
 {
 #ifdef _WIN32
     FcChar8 buffer[1000] = { 0 };
 #endif
     FcChar8 *parent = NULL, *retval = NULL;
+    FcStrSet *e = NULL;
 
     if (prefix)
     {
 	if (FcStrCmp (prefix, (const FcChar8 *) "xdg") == 0)
 	{
 	    parent = FcConfigXdgDataHome ();
-	    if (!parent)
+	    e = FcConfigXdgDataDirs ();
+	    if (!parent || !e)
 	    {
 		/* Home directory might be disabled */
 		return NULL;
@@ -1388,8 +1390,28 @@
     {
 	retval = FcStrdup (path);
     }
+    if (!e)
+	e = FcStrSetCreate ();
+    else
+    {
+	FcChar8 *s;
+	int i;
 
-    return retval;
+	for (i = 0; i < e->num; i++)
+	{
+	    s = FcStrBuildFilename (e->strs[i], path, NULL);
+	    FcStrFree (e->strs[i]);
+	    e->strs[i] = s;
+	}
+    }
+    if (!FcStrSetInsert (e, retval, 0))
+    {
+	FcStrSetDestroy (e);
+	e = NULL;
+    }
+    FcStrFree (retval);
+
+    return e;
 }
 
 static void
@@ -2062,7 +2084,7 @@
 FcParseRemapDir (FcConfigParse *parse)
 {
     const FcChar8 *path, *attr, *data, *salt;
-    FcChar8 *prefix = NULL;
+    FcStrSet *prefix_dirs = NULL;
 
     data = FcStrBufDoneStatic (&parse->pstack->str);
     if (!data)
@@ -2083,20 +2105,28 @@
     }
     attr = FcConfigGetAttribute (parse, "prefix");
     salt = FcConfigGetAttribute (parse, "salt");
-    prefix = _get_real_path_from_prefix (parse, data, attr);
-    if (!prefix || prefix[0] == 0)
+    prefix_dirs = _get_real_paths_from_prefix (parse, data, attr);
+    if (prefix_dirs)
     {
-	/* nop */
-    }
-    else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ()))
-    {
-	if (!FcConfigAddFontDir (parse->config, prefix, path, salt))
-	    FcConfigMessage (parse, FcSevereError, "out of memory; cannot create remap data for %s as %s", prefix, path);
-    }
-    FcStrBufDestroy (&parse->pstack->str);
+	FcStrList *l = FcStrListCreate (prefix_dirs);
+	FcChar8 *prefix;
 
-    if (prefix)
-	FcStrFree (prefix);
+	FcStrSetDestroy (prefix_dirs);
+	while ((prefix = FcStrListNext (l)))
+	{
+	    if (!prefix || prefix[0] == 0)
+	    {
+		/* nop */
+	    }
+	    else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ()))
+	    {
+		if (!FcConfigAddFontDir (parse->config, prefix, path, salt))
+		    FcConfigMessage (parse, FcSevereError, "out of memory; cannot create remap data for %s as %s", prefix, path);
+	    }
+	    FcStrBufDestroy (&parse->pstack->str);
+	}
+	FcStrListDone (l);
+    }
 }
 
 static void
@@ -2250,7 +2280,7 @@
 FcParseDir (FcConfigParse *parse)
 {
     const FcChar8 *attr, *data, *salt;
-    FcChar8 *prefix = NULL;
+    FcStrSet *prefix_dirs = NULL;
 
     data = FcStrBufDoneStatic (&parse->pstack->str);
     if (!data)
@@ -2265,20 +2295,28 @@
     }
     attr = FcConfigGetAttribute (parse, "prefix");
     salt = FcConfigGetAttribute (parse, "salt");
-    prefix = _get_real_path_from_prefix (parse, data, attr);
-    if (!prefix || prefix[0] == 0)
+    prefix_dirs = _get_real_paths_from_prefix (parse, data, attr);
+    if (prefix_dirs)
     {
-	/* nop */
-    }
-    else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ()))
-    {
-	if (!FcConfigAddFontDir (parse->config, prefix, NULL, salt))
-	    FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", prefix);
-    }
-    FcStrBufDestroy (&parse->pstack->str);
+	FcStrList *l = FcStrListCreate (prefix_dirs);
+	FcChar8 *prefix;
 
-    if (prefix)
-	FcStrFree (prefix);
+	FcStrSetDestroy (prefix_dirs);
+	while ((prefix = FcStrListNext (l)))
+	{
+	    if (!prefix || prefix[0] == 0)
+	    {
+		/* nop */
+	    }
+	    else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ()))
+	    {
+		if (!FcConfigAddFontDir (parse->config, prefix, NULL, salt))
+		    FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", prefix);
+	    }
+	    FcStrBufDestroy (&parse->pstack->str);
+	}
+	FcStrListDone (l);
+    }
 }
 
 static void