+ FcStrSet *subdirs;
+ FcStrList *sublist;
+ struct stat statb;
+ FcBool was_valid;
+
+ /*
+ * Now scan all of the directories into separate databases
+ * and write out the results
+ */
+ while ((dir = FcStrListNext (list)))
+ {
+ if (verbose)
+ {
+ printf ("%s: \"%s\": ", program, dir);
+ fflush (stdout);
+ }
+
+ if (!dir)
+ {
+ if (verbose)
+ printf ("skipping, no such directory\n");
+ continue;
+ }
+
+ if (FcStrSetMember (processed_dirs, dir))
+ {
+ if (verbose)
+ printf ("skipping, looped directory detected\n");
+ continue;
+ }
+
+ set = FcFontSetCreate ();
+ if (!set)
+ {
+ fprintf (stderr, "%s: Can't create font set\n", dir);
+ ret++;
+ continue;
+ }
+ subdirs = FcStrSetCreate ();
+ if (!subdirs)
+ {
+ fprintf (stderr, "%s: Can't create directory set\n", dir);
+ ret++;
+ FcFontSetDestroy (set);
+ continue;
+ }
+
+ if (access ((char *) dir, W_OK) < 0)
+ {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ if (verbose)
+ printf ("skipping, no such directory\n");
+ FcFontSetDestroy (set);
+ FcStrSetDestroy (subdirs);
+ continue;
+ case EACCES:
+ case EROFS:
+ /* That's ok, caches go to /var anyway. */
+ /* Ideally we'd do an access on the hashed_name. */
+ /* But we hid that behind an abstraction barrier. */
+ break;
+ default:
+ fprintf (stderr, "\"%s\": ", dir);
+ perror ("");
+ ret++;
+
+ FcFontSetDestroy (set);
+ FcStrSetDestroy (subdirs);
+ continue;
+ }
+ }
+ if (stat ((char *) dir, &statb) == -1)
+ {
+ fprintf (stderr, "\"%s\": ", dir);
+ perror ("");
+ FcFontSetDestroy (set);
+ FcStrSetDestroy (subdirs);
+ ret++;
+ continue;
+ }
+ if (!S_ISDIR (statb.st_mode))
+ {
+ fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
+ FcFontSetDestroy (set);
+ FcStrSetDestroy (subdirs);
+ continue;
+ }
+
+ if (really_force)
+ FcDirCacheUnlink (dir, config);
+
+ if (!force)
+ was_valid = FcDirCacheValid (dir);
+
+ if (!FcDirScanConfig (set, subdirs, FcConfigGetBlanks (config), dir, force, config))
+ {
+ fprintf (stderr, "%s: error scanning\n", dir);
+ FcFontSetDestroy (set);
+ FcStrSetDestroy (subdirs);
+ ret++;
+ continue;
+ }
+ if (!force && was_valid)
+ {
+ if (verbose)
+ printf ("skipping, %d fonts, %d dirs\n",
+ set->nfont, nsubdirs(subdirs));
+ }
+ else
+ {
+ if (verbose)
+ printf ("caching, %d fonts, %d dirs\n",
+ set->nfont, nsubdirs (subdirs));
+
+ if (!FcDirCacheValid (dir))
+ {
+ fprintf (stderr, "%s: failed to write cache\n", dir);
+ (void) FcDirCacheUnlink (dir, config);
+ ret++;
+ }
+ }
+ FcFontSetDestroy (set);
+ sublist = FcStrListCreate (subdirs);
+ FcStrSetDestroy (subdirs);
+ if (!sublist)
+ {
+ fprintf (stderr, "%s: Can't create subdir list\n", dir);
+ ret++;
+ continue;
+ }
+ FcStrSetAdd (processed_dirs, dir);
+ ret += scanDirs (sublist, config, program, force, really_force, verbose);
+ }
+ FcStrListDone (list);
+ return ret;
+}
+
+FcCache *
+FcCacheFileMap (const FcChar8 *file, struct stat *file_stat)
+{
+ FcCache *cache;
+ int fd;
+
+ fd = open (file, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return NULL;
+ if (fstat (fd, file_stat) < 0) {
+ close (fd);
+ return NULL;
+ }
+ if (FcDirCacheLoad (fd, file_stat->st_size, &cache)) {
+ close (fd);
+ return cache;
+ }
+ close (fd);
+ return NULL;
+}
+
+static FcBool
+cleanCacheDirectory (FcConfig *config, FcChar8 *dir, FcBool verbose)
+{
+ DIR *d;
+ struct dirent *ent;
+ char *dir_base;
+ FcBool ret = FcTrue;
+ FcBool remove;
+ FcCache *cache;
+ struct stat file_stat;
+ struct stat target_stat;
+
+ dir_base = FcStrPlus (dir, "/");
+ if (access ((char *) dir, W_OK|X_OK) != 0)
+ {
+ if (verbose)
+ printf ("%s: skipping unwritable cache directory\n", dir);
+ return FcTrue;
+ }
+ d = opendir (dir);
+ if (!d)
+ {
+ perror (dir);
+ return FcFalse;
+ }
+ while ((ent = readdir (d)))
+ {
+ FcChar8 *file_name;
+ FcChar8 *target_dir;
+
+ if (ent->d_name[0] == '.')
+ continue;
+ file_name = FcStrPlus (dir_base, ent->d_name);
+ if (!file_name)
+ {
+ fprintf (stderr, "%s: allocation failure\n", dir);
+ ret = FcFalse;
+ break;
+ }
+ cache = FcCacheFileMap (file_name, &file_stat);
+ if (!cache)
+ {
+ fprintf (stderr, "%s: invalid cache file: %s\n", dir, ent->d_name);
+ FcStrFree (file_name);
+ ret = FcFalse;
+ continue;
+ }
+ target_dir = FcCacheDir (cache);
+ remove = FcFalse;
+ if (stat (target_dir, &target_stat) < 0)
+ {
+ if (verbose)
+ printf ("%s: %s: missing directory: %s \n",
+ dir, ent->d_name, target_dir);
+ remove = FcTrue;
+ }
+ else if (target_stat.st_mtime > file_stat.st_mtime)
+ {
+ if (verbose)
+ printf ("%s: %s: cache outdated: %s\n",
+ dir, ent->d_name, target_dir);
+ remove = FcTrue;
+ }
+ if (remove)
+ {
+ if (unlink (file_name) < 0)
+ {
+ perror (file_name);
+ ret = FcFalse;
+ }
+ }
+ FcStrFree (file_name);
+ }
+
+ closedir (d);
+ return ret;
+}
+
+static FcBool
+cleanCacheDirectories (FcConfig *config, FcBool verbose)
+{
+ FcStrList *cache_dirs = FcConfigGetCacheDirs (config);
+ FcChar8 *cache_dir;
+ FcBool ret = FcTrue;
+
+ if (!cache_dirs)
+ return FcFalse;
+ while ((cache_dir = FcStrListNext (cache_dirs)))
+ {
+ if (!cleanCacheDirectory (config, cache_dir, verbose))
+ {
+ ret = FcFalse;
+ break;
+ }
+ }
+ FcStrListDone (cache_dirs);
+ return ret;
+}
+
+int
+main (int argc, char **argv)
+{
+ FcStrSet *dirs;
+ FcStrList *list;
+ FcBool verbose = FcFalse;
+ FcBool force = FcFalse;
+ FcBool really_force = FcFalse;
+ FcBool systemOnly = FcFalse;
+ FcConfig *config;