[Fontconfig] fontconfig: Branch 'master'

Keith Packard keithp at kemper.freedesktop.org
Tue Nov 13 16:42:03 PST 2007


 configure.in         |    6 ++-
 fc-cache/Makefile.am |    3 +
 src/fccache.c        |   93 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/fcxml.c          |   24 +++++++++++++
 4 files changed, 124 insertions(+), 2 deletions(-)

New commits:
commit 8a3dc4880c1182ea446cdbc0885e956c6517cf83
Author: Tor Lillqvist <tml at iki.fi>
Date:   Tue Nov 13 16:41:55 2007 -0800

    Workaround for stat() brokenness in Microsoft's C library (bug 8526)
    
    Fix a couple of longstanding problems with fontconfig on Windows that
    manifest themselves especially in GIMP. The root cause to the problems is in
    Microsoft's incredibly stupid stat() implementation. Basically, stat()
    returns wrong timestamp fields for files on NTFS filesystems on machines
    that use automatic DST switching.
    
    See for instance http://bugzilla.gnome.org/show_bug.cgi?id=154968 and
    http://www.codeproject.com/datetime/dstbugs.asp
    
    As fccache.c now looks at more fields in the stat struct I fill in them all.
    I noticed that fstat() is used only on a fd just after opening it, so on
    Win32 I just call my stat() replacement before opening instead...
    Implementing a good replacement for fstat() would be harder because the code
    in fccache.c wants to compare inode numbers. There are no (readily
    accessible) inode numbers on Win32, so I fake it with the hash of the full
    file name, in the case as it is on disk. And fstat() doesn't know the full
    file name, so it would be rather hard to come up with a inode number to
    identify the file.
    
    The patch also adds similar handling for the cache directory as for the fonts
    directory: If a cachedir element in fonts.conf contains the magic string
    "WINDOWSTEMPDIR_FONTCONFIG_CACHE" it is replaced at runtime with a path under
    the machine's (or user's) temp folder as returned by GetTempPath(). I don't
    want to hardcode any pathnames in a fonts.conf intended to be distributed to
    end-users, most of which who wouldn't know how to edit it anyway. And
    requiring an installer to edit it gets complicated.

diff --git a/configure.in b/configure.in
index c431dcc..3f7e8bf 100644
--- a/configure.in
+++ b/configure.in
@@ -459,7 +459,11 @@ AC_ARG_WITH(cache-dir,         [ --with-cache-dir=DIR           Use DIR to store
 
 case $fc_cachedir in
 no|yes)
-	fc_cachedir=`eval echo "${localstatedir}/cache/"${PACKAGE}`
+	if test "$os_win32" = "yes"; then
+		fc_cachedir="WINDOWSTEMPDIR_FONTCONFIG_CACHE"
+	else
+		fc_cachedir=`eval echo "${localstatedir}/cache/"${PACKAGE}`
+	fi
 	;;
 *)
 	;;
diff --git a/fc-cache/Makefile.am b/fc-cache/Makefile.am
index 3a6ffe1..538e197 100644
--- a/fc-cache/Makefile.am
+++ b/fc-cache/Makefile.am
@@ -27,11 +27,14 @@ FC_CACHE_SRC=${top_srcdir}/fc-cache
 
 SGML = ${FC_CACHE_SRC}/fc-cache.sgml
 
+if OS_WIN32
+else
 install-data-local:
 	-$(mkinstalldirs) "$(DESTDIR)$(fc_cachedir)"
 
 uninstall-local:
 	-$(RM) -rf "$(DESTDIR)$(fc_cachedir)"
+endif
 
 INCLUDES=-I${top_srcdir} -I${top_srcdir}/src $(FREETYPE_CFLAGS) $(WARN_CFLAGS)
 
diff --git a/src/fccache.c b/src/fccache.c
index c24b061..241ebd0 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -33,6 +33,7 @@
 #  include <unistd.h>
 #  include <sys/mman.h>
 #elif defined(_WIN32)
+#  define _WIN32_WINNT 0x0500
 #  include <windows.h>
 #endif
 
@@ -53,6 +54,90 @@ static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
 
 #define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
 
+#ifdef _WIN32
+
+#include <windows.h>
+
+#ifdef __GNUC__
+typedef long long INT64;
+#define EPOCH_OFFSET 11644473600ll
+#else
+#define EPOCH_OFFSET 11644473600i64
+typedef __int64 INT64;
+#endif
+
+/* Workaround for problems in the stat() in the Microsoft C library:
+ *
+ * 1) stat() uses FindFirstFile() to get the file
+ * attributes. Unfortunately this API doesn't return correct values
+ * for modification time of a directory until some time after a file
+ * or subdirectory has been added to the directory. (This causes
+ * run-test.sh to fail, for instance.) GetFileAttributesEx() is
+ * better, it returns the updated timestamp right away.
+ *
+ * 2) stat() does some strange things related to backward
+ * compatibility with the local time timestamps on FAT volumes and
+ * daylight saving time. This causes problems after the switches
+ * to/from daylight saving time. See
+ * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially
+ * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp .
+ * We don't need any of that, FAT and Win9x are as good as dead. So
+ * just use the UTC timestamps from NTFS, converted to the Unix epoch.
+ */
+
+static int
+FcStat (const char *file, struct stat *statb)
+{
+    WIN32_FILE_ATTRIBUTE_DATA wfad;
+    char full_path_name[MAX_PATH];
+    char *basename;
+    DWORD rc;
+    
+    if (!GetFileAttributesEx (file, GetFileExInfoStandard, &wfad))
+	return -1;
+    
+    statb->st_dev = 0;
+
+    /* Calculate a pseudo inode number as a hash of the full path name.
+     * Call GetLongPathName() to get the spelling of the path name as it
+     * is on disk.
+     */
+    rc = GetFullPathName (file, sizeof (full_path_name), full_path_name, &basename);
+    if (rc == 0 || rc > sizeof (full_path_name))
+	return -1;
+
+    rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name));
+    statb->st_ino = FcStringHash (full_path_name);
+    
+    statb->st_mode = _S_IREAD | _S_IWRITE;
+    statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6);
+
+    if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+	statb->st_mode |= _S_IFDIR;
+    else
+	statb->st_mode |= _S_IFREG;
+    
+    statb->st_nlink = 1;
+    statb->st_uid = statb->st_gid = 0;
+    statb->st_rdev = 0;
+    
+    if (wfad.nFileSizeHigh > 0)
+	return -1;
+    statb->st_size = wfad.nFileSizeLow;
+    
+    statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime)/10000000 - EPOCH_OFFSET;
+    statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime)/10000000 - EPOCH_OFFSET;
+    statb->st_ctime = statb->st_mtime;
+    
+    return 0;
+}
+
+#else
+
+#define FcStat stat
+
+#endif
+
 static const char bin2hex[] = { '0', '1', '2', '3',
 				'4', '5', '6', '7',
 				'8', '9', 'a', 'b',
@@ -118,14 +203,20 @@ FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat)
 {
     int	fd;
 
+#ifdef _WIN32
+    if (FcStat (cache_file, file_stat) < 0)
+        return -1;
+#endif
     fd = open((char *) cache_file, O_RDONLY | O_BINARY);
     if (fd < 0)
 	return fd;
+#ifndef _WIN32
     if (fstat (fd, file_stat) < 0)
     {
 	close (fd);
 	return -1;
     }
+#endif
     return fd;
 }
 
@@ -147,7 +238,7 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
     struct stat file_stat, dir_stat;
     FcBool	ret = FcFalse;
 
-    if (stat ((char *) dir, &dir_stat) < 0)
+    if (FcStat ((char *) dir, &dir_stat) < 0)
         return FcFalse;
 
     FcDirCacheBasename (dir, cache_base);
diff --git a/src/fcxml.c b/src/fcxml.c
index e8cfe1f..cec9f6f 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -2090,6 +2090,30 @@ FcEndElement(void *userData, const XML_Char *name)
 	    FcConfigMessage (parse, FcSevereError, "out of memory");
 	    break;
 	}
+#ifdef _WIN32
+	if (strcmp (data, "WINDOWSTEMPDIR_FONTCONFIG_CACHE") == 0)
+	{
+	    int rc;
+	    FcStrFree (data);
+	    data = malloc (1000);
+	    if (!data)
+	    {
+		FcConfigMessage (parse, FcSevereError, "out of memory");
+		break;
+	    }
+	    FcMemAlloc (FC_MEM_STRING, 1000);
+	    rc = GetTempPath (800, data);
+	    if (rc == 0 || rc > 800)
+	    {
+		FcConfigMessage (parse, FcSevereError, "GetWindowsDirectory failed");
+		FcStrFree (data);
+		break;
+	    }
+	    if (data [strlen (data) - 1] != '\\')
+		strcat (data, "\\");
+	    strcat (data, "fontconfig\\cache");
+	}
+#endif
 	if (!FcStrUsesHome (data) || FcConfigHome ())
 	{
 	    if (!FcConfigAddCacheDir (parse->config, data))


More information about the Fontconfig mailing list