]> git.wh0rd.org - fontconfig.git/blobdiff - src/fccache.c
Workaround for stat() brokenness in Microsoft's C library (bug 8526)
[fontconfig.git] / src / fccache.c
index c24b06184a120a6571cee4e749473fc81e47fe34..241ebd089d6e52fe8d09ea35ff5311dce1f0072f 100644 (file)
@@ -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);