Return canonicalized paths from FcConfigRealFilename

FcConfigRealFilename() follows symlinks, but the link may be relative to the
directory containing the link.  For example, on my system, I have this file:

/etc/fonts/conf.d/99-language-selector-zh.conf ->
    ../conf.avail/99-language-selector-zh.conf

Since /etc/fonts/conf.d is probably not in PATH, open()ing the file would fail.
This change makes FcConfigRealFilename() return the canonicalized filename
instead.  So for the example above, it would return:

/etc/fonts/conf.avail/99-language-selector-zh.conf

This was causing bad font rendering in Chromium [1] after the regression I
introduced in 7ad010e80b.

[1] https://bugs.chromium.org/p/chromium/issues/detail?id=857511
This commit is contained in:
Tom Anderson 2018-07-11 15:50:26 -07:00 committed by Behdad Esfahbod
parent 48e9e5f4f0
commit d1f48f11d5
3 changed files with 49 additions and 30 deletions

View File

@ -2191,29 +2191,18 @@ FcConfigFilename (const FcChar8 *url)
} }
file = 0; file = 0;
#ifdef _WIN32 if (FcStrIsAbsoluteFilename(url))
if (isalpha (*url) && return FcConfigFileExists (0, url);
url[1] == ':' &&
(url[2] == '/' || url[2] == '\\'))
goto absolute_path;
#endif
switch (*url) { if (*url == '~')
case '~': {
dir = FcConfigHome (); dir = FcConfigHome ();
if (dir) if (dir)
file = FcConfigFileExists (dir, url + 1); file = FcConfigFileExists (dir, url + 1);
else else
file = 0; file = 0;
break; }
#ifdef _WIN32
case '\\':
absolute_path:
#endif
case '/':
file = FcConfigFileExists (0, url);
break;
default:
path = FcConfigGetPath (); path = FcConfigGetPath ();
if (!path) if (!path)
return NULL; return NULL;
@ -2224,9 +2213,6 @@ FcConfigFilename (const FcChar8 *url)
break; break;
} }
FcConfigFreePath (path); FcConfigFreePath (path);
break;
}
return file; return file;
} }
@ -2252,10 +2238,29 @@ FcConfigRealFilename (FcConfig *config,
if ((len = FcReadLink (nn, buf, sizeof (buf) - 1)) != -1) if ((len = FcReadLink (nn, buf, sizeof (buf) - 1)) != -1)
{ {
buf[len] = 0; buf[len] = 0;
if (!FcStrIsAbsoluteFilename (buf))
{
FcChar8 *dirname = FcStrDirname (nn);
FcStrFree (nn);
if (!dirname)
return NULL;
FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
FcStrFree (dirname);
if (!path)
return NULL;
nn = FcStrCanonFilename (path);
FcStrFree (path);
}
else
{
FcStrFree (nn); FcStrFree (nn);
nn = FcStrdup (buf); nn = FcStrdup (buf);
} }
} }
}
return nn; return nn;
} }

View File

@ -1279,6 +1279,9 @@ FcStrGlobMatch (const FcChar8 *glob,
FcPrivate FcBool FcPrivate FcBool
FcStrUsesHome (const FcChar8 *s); FcStrUsesHome (const FcChar8 *s);
FcPrivate FcBool
FcStrIsAbsoluteFilename (const FcChar8 *s);
FcPrivate FcChar8 * FcPrivate FcChar8 *
FcStrBuildFilename (const FcChar8 *path, FcStrBuildFilename (const FcChar8 *path,
...); ...);

View File

@ -867,6 +867,17 @@ FcStrUsesHome (const FcChar8 *s)
return *s == '~'; return *s == '~';
} }
FcBool
FcStrIsAbsoluteFilename (const FcChar8 *s)
{
#ifdef _WIN32
if (*s == '\\' ||
(isalpha (*s) && s[1] == ':' && (s[2] == '/' || s[2] == '\\'))
return FcTrue;
#endif
return *s == '/';
}
FcChar8 * FcChar8 *
FcStrBuildFilename (const FcChar8 *path, FcStrBuildFilename (const FcChar8 *path,
...) ...)