/* * $RCSId: xc/lib/fontconfig/fc-cache/fc-cache.c,v 1.8tsi Exp $ * * Copyright © 2002 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #else #ifdef linux #define HAVE_GETOPT_LONG 1 #endif #define HAVE_GETOPT 1 #endif #include "fcint.h" #include #include #include #include #include #include #include #include #if defined (_WIN32) #define STRICT #include #define sleep(x) Sleep((x) * 1000) #undef STRICT #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef HAVE_GETOPT #define HAVE_GETOPT 0 #endif #ifndef HAVE_GETOPT_LONG #define HAVE_GETOPT_LONG 0 #endif #if HAVE_GETOPT_LONG #undef _GNU_SOURCE #define _GNU_SOURCE #include const struct option longopts[] = { {"force", 0, 0, 'f'}, {"really-force", 0, 0, 'r'}, {"system-only", 0, 0, 's'}, {"version", 0, 0, 'V'}, {"verbose", 0, 0, 'v'}, {"help", 0, 0, '?'}, {NULL,0,0,0}, }; #else #if HAVE_GETOPT extern char *optarg; extern int optind, opterr, optopt; #endif #endif static void usage (char *program) { #if HAVE_GETOPT_LONG fprintf (stderr, "usage: %s [-frsvV?] [--force|--really-force] [--system-only] [--verbose] [--version] [--help] [dirs]\n", program); #else fprintf (stderr, "usage: %s [-frsvV?] [dirs]\n", program); #endif fprintf (stderr, "Build font information caches in [dirs]\n" "(all directories in font configuration by default).\n"); fprintf (stderr, "\n"); #if HAVE_GETOPT_LONG fprintf (stderr, " -f, --force scan directories with apparently valid caches\n"); fprintf (stderr, " -r, --really-force erase all existing caches, then rescan\n"); fprintf (stderr, " -s, --system-only scan system-wide directories only\n"); fprintf (stderr, " -v, --verbose display status information while busy\n"); fprintf (stderr, " -V, --version display font config version and exit\n"); fprintf (stderr, " -?, --help display this help and exit\n"); #else fprintf (stderr, " -f (force) scan directories with apparently valid caches\n"); fprintf (stderr, " -r, (really force) erase all existing caches, then rescan\n"); fprintf (stderr, " -s (system) scan system-wide directories only\n"); fprintf (stderr, " -v (verbose) display status information while busy\n"); fprintf (stderr, " -V (version) display font config version and exit\n"); fprintf (stderr, " -? (help) display this help and exit\n"); #endif exit (1); } static FcStrSet *processed_dirs; static int nsubdirs (FcStrSet *set) { FcStrList *list; int n = 0; list = FcStrListCreate (set); if (!list) return 0; while (FcStrListNext (list)) n++; FcStrListDone (list); return n; } static int scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose) { int ret = 0; const FcChar8 *dir; FcFontSet *set; FcStrSet *subdirs; FcStrList *sublist; FcCache *cache; struct stat statb; FcBool was_valid; int i; /* * Now scan all of the directories into separate databases * and write out the results */ while ((dir = FcStrListNext (list))) { if (verbose) { printf ("%s: ", 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; } if (access ((char *) dir, W_OK) < 0) { switch (errno) { case ENOENT: case ENOTDIR: if (verbose) printf ("skipping, no such directory\n"); 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++; continue; } } if (stat ((char *) dir, &statb) == -1) { fprintf (stderr, "\"%s\": ", dir); perror (""); ret++; continue; } if (!S_ISDIR (statb.st_mode)) { fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); continue; } if (really_force) FcDirCacheUnlink (dir, config); cache = NULL; was_valid = FcFalse; if (!force) { cache = FcDirCacheLoad (dir, config, NULL); if (cache) was_valid = FcTrue; } if (!cache) { cache = FcDirCacheRead (dir, FcTrue, config); if (!cache) { fprintf (stderr, "%s: error scanning\n", dir); ret++; continue; } } set = FcCacheSet (cache); if (was_valid) { if (verbose) printf ("skipping, %d fonts, %d dirs\n", set->nfont, cache->dirs_count); } else { if (verbose) printf ("caching, %d fonts, %d dirs\n", set->nfont, cache->dirs_count); if (!FcDirCacheValid (dir)) { fprintf (stderr, "%s: failed to write cache\n", dir); (void) FcDirCacheUnlink (dir, config); ret++; } } subdirs = FcStrSetCreate (); if (!subdirs) { fprintf (stderr, "%s: Can't create subdir set\n", dir); ret++; FcDirCacheUnload (cache); continue; } for (i = 0; i < cache->dirs_count; i++) FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); FcDirCacheUnload (cache); sublist = FcStrListCreate (subdirs); FcStrSetDestroy (subdirs); if (!sublist) { fprintf (stderr, "%s: Can't create subdir list\n", dir); ret++; FcDirCacheUnload (cache); continue; } FcStrSetAdd (processed_dirs, dir); ret += scanDirs (sublist, config, force, really_force, verbose); } FcStrListDone (list); return ret; } 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 (!dir_base) { fprintf (stderr, "%s: out of memory\n", dir); return FcFalse; } if (access ((char *) dir, W_OK|X_OK) != 0) { if (verbose) printf ("%s: not cleaning unwritable cache directory\n", dir); FcStrFree (dir_base); return FcTrue; } if (verbose) printf ("%s: cleaning cache directory\n", dir); d = opendir (dir); if (!d) { perror (dir); FcStrFree (dir_base); 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 = FcDirCacheLoadFile (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; } } FcDirCacheUnload (cache); FcStrFree (file_name); } closedir (d); FcStrFree (dir_base); 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; int i; int ret; #if HAVE_GETOPT_LONG || HAVE_GETOPT int c; #if HAVE_GETOPT_LONG while ((c = getopt_long (argc, argv, "frsVv?", longopts, NULL)) != -1) #else while ((c = getopt (argc, argv, "frsVv?")) != -1) #endif { switch (c) { case 'r': really_force = FcTrue; /* fall through */ case 'f': force = FcTrue; break; case 's': systemOnly = FcTrue; break; case 'V': fprintf (stderr, "fontconfig version %d.%d.%d\n", FC_MAJOR, FC_MINOR, FC_REVISION); exit (0); case 'v': verbose = FcTrue; break; default: usage (argv[0]); } } i = optind; #else i = 1; #endif if (systemOnly) FcConfigEnableHome (FcFalse); config = FcInitLoadConfig (); if (!config) { fprintf (stderr, "%s: Can't init font config library\n", argv[0]); return 1; } FcConfigSetCurrent (config); if (argv[i]) { dirs = FcStrSetCreate (); if (!dirs) { fprintf (stderr, "%s: Can't create list of directories\n", argv[0]); return 1; } while (argv[i]) { if (!FcStrSetAdd (dirs, (FcChar8 *) argv[i])) { fprintf (stderr, "%s: Can't add directory\n", argv[0]); return 1; } i++; } list = FcStrListCreate (dirs); FcStrSetDestroy (dirs); } else list = FcConfigGetConfigDirs (config); if ((processed_dirs = FcStrSetCreate()) == NULL) { fprintf(stderr, "Cannot malloc\n"); return 1; } ret = scanDirs (list, config, force, really_force, verbose); FcStrSetDestroy (processed_dirs); cleanCacheDirectories (config, verbose); /* * Now we need to sleep a second (or two, to be extra sure), to make * sure that timestamps for changes after this run of fc-cache are later * then any timestamps we wrote. We don't use gettimeofday() because * sleep(3) can't be interrupted by a signal here -- this isn't in the * library, and there aren't any signals flying around here. */ FcConfigDestroy (config); sleep (2); if (verbose) printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); return ret; }