fc-cache.c revision 7f785a56
12c393a42Smrg/* 2a6844aabSmrg * fontconfig/fc-cache/fc-cache.c 32c393a42Smrg * 42c393a42Smrg * Copyright © 2002 Keith Packard 52c393a42Smrg * 62c393a42Smrg * Permission to use, copy, modify, distribute, and sell this software and its 72c393a42Smrg * documentation for any purpose is hereby granted without fee, provided that 82c393a42Smrg * the above copyright notice appear in all copies and that both that 92c393a42Smrg * copyright notice and this permission notice appear in supporting 102c393a42Smrg * documentation, and that the name of Keith Packard not be used in 112c393a42Smrg * advertising or publicity pertaining to distribution of the software without 122c393a42Smrg * specific, written prior permission. Keith Packard makes no 132c393a42Smrg * representations about the suitability of this software for any purpose. It 142c393a42Smrg * is provided "as is" without express or implied warranty. 152c393a42Smrg * 16a6844aabSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18a6844aabSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 192c393a42Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 202c393a42Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 212c393a42Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 222c393a42Smrg * PERFORMANCE OF THIS SOFTWARE. 232c393a42Smrg */ 242c393a42Smrg 252c393a42Smrg#include "../fc-arch/fcarch.h" 262c393a42Smrg 272c393a42Smrg#ifdef HAVE_CONFIG_H 282c393a42Smrg#include <config.h> 292c393a42Smrg#else 302c393a42Smrg#ifdef linux 312c393a42Smrg#define HAVE_GETOPT_LONG 1 322c393a42Smrg#endif 332c393a42Smrg#define HAVE_GETOPT 1 342c393a42Smrg#endif 352c393a42Smrg 362c393a42Smrg#include <fontconfig/fontconfig.h> 372c393a42Smrg#include <stdio.h> 382c393a42Smrg#include <stdlib.h> 392c393a42Smrg#include <unistd.h> 402c393a42Smrg#include <sys/types.h> 412c393a42Smrg#include <sys/stat.h> 422c393a42Smrg#include <errno.h> 432c393a42Smrg#include <fcntl.h> 442c393a42Smrg#include <dirent.h> 452c393a42Smrg#include <string.h> 462c393a42Smrg 472c393a42Smrg#if defined (_WIN32) 482c393a42Smrg#define STRICT 492c393a42Smrg#include <windows.h> 502c393a42Smrg#define sleep(x) Sleep((x) * 1000) 512c393a42Smrg#undef STRICT 522c393a42Smrg#endif 532c393a42Smrg 542c393a42Smrg#ifndef O_BINARY 552c393a42Smrg#define O_BINARY 0 562c393a42Smrg#endif 572c393a42Smrg 582c393a42Smrg#ifndef HAVE_GETOPT 592c393a42Smrg#define HAVE_GETOPT 0 602c393a42Smrg#endif 612c393a42Smrg#ifndef HAVE_GETOPT_LONG 622c393a42Smrg#define HAVE_GETOPT_LONG 0 632c393a42Smrg#endif 642c393a42Smrg 652c393a42Smrg#if HAVE_GETOPT_LONG 662c393a42Smrg#undef _GNU_SOURCE 672c393a42Smrg#define _GNU_SOURCE 682c393a42Smrg#include <getopt.h> 692c393a42Smrgconst struct option longopts[] = { 702c393a42Smrg {"force", 0, 0, 'f'}, 717f785a56Sjmcneill {"quick", 0, 0, 'q'}, 722c393a42Smrg {"really-force", 0, 0, 'r'}, 732c393a42Smrg {"system-only", 0, 0, 's'}, 742c393a42Smrg {"version", 0, 0, 'V'}, 752c393a42Smrg {"verbose", 0, 0, 'v'}, 76a6844aabSmrg {"help", 0, 0, 'h'}, 772c393a42Smrg {NULL,0,0,0}, 782c393a42Smrg}; 792c393a42Smrg#else 802c393a42Smrg#if HAVE_GETOPT 812c393a42Smrgextern char *optarg; 822c393a42Smrgextern int optind, opterr, optopt; 832c393a42Smrg#endif 842c393a42Smrg#endif 852c393a42Smrg 862c393a42Smrgstatic void 87a6844aabSmrgusage (char *program, int error) 882c393a42Smrg{ 89a6844aabSmrg FILE *file = error ? stderr : stdout; 902c393a42Smrg#if HAVE_GETOPT_LONG 917f785a56Sjmcneill fprintf (file, "usage: %s [-fqrsvVh] [--quick] [--force|--really-force] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 922c393a42Smrg program); 932c393a42Smrg#else 947f785a56Sjmcneill fprintf (file, "usage: %s [-fqrsvVh] [dirs]\n", 952c393a42Smrg program); 962c393a42Smrg#endif 97a6844aabSmrg fprintf (file, "Build font information caches in [dirs]\n" 982c393a42Smrg "(all directories in font configuration by default).\n"); 99a6844aabSmrg fprintf (file, "\n"); 1002c393a42Smrg#if HAVE_GETOPT_LONG 101a6844aabSmrg fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 1027f785a56Sjmcneill fprintf (file, " -q, --quick don't sleep before exiting\n"); 103a6844aabSmrg fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 104a6844aabSmrg fprintf (file, " -s, --system-only scan system-wide directories only\n"); 105a6844aabSmrg fprintf (file, " -v, --verbose display status information while busy\n"); 106a6844aabSmrg fprintf (file, " -V, --version display font config version and exit\n"); 107a6844aabSmrg fprintf (file, " -h, --help display this help and exit\n"); 1082c393a42Smrg#else 109a6844aabSmrg fprintf (file, " -f (force) scan directories with apparently valid caches\n"); 1107f785a56Sjmcneill fprintf (file, " -q (quick) don't sleep before exiting\n"); 111a6844aabSmrg fprintf (file, " -r, (really force) erase all existing caches, then rescan\n"); 112a6844aabSmrg fprintf (file, " -s (system) scan system-wide directories only\n"); 113a6844aabSmrg fprintf (file, " -v (verbose) display status information while busy\n"); 114a6844aabSmrg fprintf (file, " -V (version) display font config version and exit\n"); 115a6844aabSmrg fprintf (file, " -h (help) display this help and exit\n"); 1162c393a42Smrg#endif 117a6844aabSmrg exit (error); 1182c393a42Smrg} 1192c393a42Smrg 1202c393a42Smrgstatic FcStrSet *processed_dirs; 1212c393a42Smrg 1222c393a42Smrgstatic int 1232c393a42SmrgscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose) 1242c393a42Smrg{ 1252c393a42Smrg int ret = 0; 1262c393a42Smrg const FcChar8 *dir; 1272c393a42Smrg FcStrSet *subdirs; 1282c393a42Smrg FcStrList *sublist; 1292c393a42Smrg FcCache *cache; 1302c393a42Smrg struct stat statb; 1312c393a42Smrg FcBool was_valid; 1322c393a42Smrg int i; 1332c393a42Smrg 1342c393a42Smrg /* 1352c393a42Smrg * Now scan all of the directories into separate databases 1362c393a42Smrg * and write out the results 1372c393a42Smrg */ 1382c393a42Smrg while ((dir = FcStrListNext (list))) 1392c393a42Smrg { 1402c393a42Smrg if (verbose) 1412c393a42Smrg { 1422c393a42Smrg printf ("%s: ", dir); 1432c393a42Smrg fflush (stdout); 1442c393a42Smrg } 1452c393a42Smrg 1462c393a42Smrg if (!dir) 1472c393a42Smrg { 1482c393a42Smrg if (verbose) 1492c393a42Smrg printf ("skipping, no such directory\n"); 1502c393a42Smrg continue; 1512c393a42Smrg } 1522c393a42Smrg 1532c393a42Smrg if (FcStrSetMember (processed_dirs, dir)) 1542c393a42Smrg { 1552c393a42Smrg if (verbose) 1562c393a42Smrg printf ("skipping, looped directory detected\n"); 1572c393a42Smrg continue; 1582c393a42Smrg } 1592c393a42Smrg 1602c393a42Smrg if (stat ((char *) dir, &statb) == -1) 1612c393a42Smrg { 1622c393a42Smrg switch (errno) { 1632c393a42Smrg case ENOENT: 1642c393a42Smrg case ENOTDIR: 1652c393a42Smrg if (verbose) 1662c393a42Smrg printf ("skipping, no such directory\n"); 1672c393a42Smrg break; 1682c393a42Smrg default: 1692c393a42Smrg fprintf (stderr, "\"%s\": ", dir); 1702c393a42Smrg perror (""); 1712c393a42Smrg ret++; 1722c393a42Smrg break; 1732c393a42Smrg } 1742c393a42Smrg continue; 1752c393a42Smrg } 1762c393a42Smrg 1772c393a42Smrg if (!S_ISDIR (statb.st_mode)) 1782c393a42Smrg { 1792c393a42Smrg fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 1802c393a42Smrg continue; 1812c393a42Smrg } 1822c393a42Smrg 1832c393a42Smrg if (really_force) 1842c393a42Smrg FcDirCacheUnlink (dir, config); 1852c393a42Smrg 1862c393a42Smrg cache = NULL; 1872c393a42Smrg was_valid = FcFalse; 1882c393a42Smrg if (!force) { 1892c393a42Smrg cache = FcDirCacheLoad (dir, config, NULL); 1902c393a42Smrg if (cache) 1912c393a42Smrg was_valid = FcTrue; 1922c393a42Smrg } 1932c393a42Smrg 1942c393a42Smrg if (!cache) 1952c393a42Smrg { 1962c393a42Smrg cache = FcDirCacheRead (dir, FcTrue, config); 1972c393a42Smrg if (!cache) 1982c393a42Smrg { 1992c393a42Smrg fprintf (stderr, "%s: error scanning\n", dir); 2002c393a42Smrg ret++; 2012c393a42Smrg continue; 2022c393a42Smrg } 2032c393a42Smrg } 2042c393a42Smrg 2052c393a42Smrg if (was_valid) 2062c393a42Smrg { 2072c393a42Smrg if (verbose) 2082c393a42Smrg printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 2092c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2102c393a42Smrg } 2112c393a42Smrg else 2122c393a42Smrg { 2132c393a42Smrg if (verbose) 2142c393a42Smrg printf ("caching, new cache contents: %d fonts, %d dirs\n", 2152c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2162c393a42Smrg 2172c393a42Smrg if (!FcDirCacheValid (dir)) 2182c393a42Smrg { 2192c393a42Smrg fprintf (stderr, "%s: failed to write cache\n", dir); 2202c393a42Smrg (void) FcDirCacheUnlink (dir, config); 2212c393a42Smrg ret++; 2222c393a42Smrg } 2232c393a42Smrg } 2242c393a42Smrg 2252c393a42Smrg subdirs = FcStrSetCreate (); 2262c393a42Smrg if (!subdirs) 2272c393a42Smrg { 2282c393a42Smrg fprintf (stderr, "%s: Can't create subdir set\n", dir); 2292c393a42Smrg ret++; 2302c393a42Smrg FcDirCacheUnload (cache); 2312c393a42Smrg continue; 2322c393a42Smrg } 2332c393a42Smrg for (i = 0; i < FcCacheNumSubdir (cache); i++) 2342c393a42Smrg FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2352c393a42Smrg 2362c393a42Smrg FcDirCacheUnload (cache); 2372c393a42Smrg 2382c393a42Smrg sublist = FcStrListCreate (subdirs); 2392c393a42Smrg FcStrSetDestroy (subdirs); 2402c393a42Smrg if (!sublist) 2412c393a42Smrg { 2422c393a42Smrg fprintf (stderr, "%s: Can't create subdir list\n", dir); 2432c393a42Smrg ret++; 2442c393a42Smrg continue; 2452c393a42Smrg } 2462c393a42Smrg FcStrSetAdd (processed_dirs, dir); 2472c393a42Smrg ret += scanDirs (sublist, config, force, really_force, verbose); 2482c393a42Smrg } 2492c393a42Smrg FcStrListDone (list); 2502c393a42Smrg return ret; 2512c393a42Smrg} 2522c393a42Smrg 2532c393a42Smrgstatic FcBool 2542c393a42SmrgcleanCacheDirectory (FcConfig *config, FcChar8 *dir, FcBool verbose) 2552c393a42Smrg{ 2562c393a42Smrg DIR *d; 2572c393a42Smrg struct dirent *ent; 2582c393a42Smrg FcChar8 *dir_base; 2592c393a42Smrg FcBool ret = FcTrue; 2602c393a42Smrg FcBool remove; 2612c393a42Smrg FcCache *cache; 2622c393a42Smrg struct stat target_stat; 2632c393a42Smrg 2642c393a42Smrg dir_base = FcStrPlus (dir, (FcChar8 *) "/"); 2652c393a42Smrg if (!dir_base) 2662c393a42Smrg { 2672c393a42Smrg fprintf (stderr, "%s: out of memory\n", dir); 2682c393a42Smrg return FcFalse; 2692c393a42Smrg } 2702c393a42Smrg if (access ((char *) dir, W_OK) != 0) 2712c393a42Smrg { 2722c393a42Smrg if (verbose) 2732c393a42Smrg printf ("%s: not cleaning %s cache directory\n", dir, 2742c393a42Smrg access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent"); 2752c393a42Smrg FcStrFree (dir_base); 2762c393a42Smrg return FcTrue; 2772c393a42Smrg } 2782c393a42Smrg if (verbose) 2792c393a42Smrg printf ("%s: cleaning cache directory\n", dir); 2802c393a42Smrg d = opendir ((char *) dir); 2812c393a42Smrg if (!d) 2822c393a42Smrg { 2832c393a42Smrg perror ((char *) dir); 2842c393a42Smrg FcStrFree (dir_base); 2852c393a42Smrg return FcFalse; 2862c393a42Smrg } 2872c393a42Smrg while ((ent = readdir (d))) 2882c393a42Smrg { 2892c393a42Smrg FcChar8 *file_name; 2902c393a42Smrg const FcChar8 *target_dir; 2912c393a42Smrg 2922c393a42Smrg if (ent->d_name[0] == '.') 2932c393a42Smrg continue; 2942c393a42Smrg /* skip cache files for different architectures and */ 2952c393a42Smrg /* files which are not cache files at all */ 2962c393a42Smrg if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) || 2972c393a42Smrg strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX)) 2982c393a42Smrg continue; 2992c393a42Smrg 3002c393a42Smrg file_name = FcStrPlus (dir_base, (FcChar8 *) ent->d_name); 3012c393a42Smrg if (!file_name) 3022c393a42Smrg { 3032c393a42Smrg fprintf (stderr, "%s: allocation failure\n", dir); 3042c393a42Smrg ret = FcFalse; 3052c393a42Smrg break; 3062c393a42Smrg } 3072c393a42Smrg remove = FcFalse; 3082c393a42Smrg cache = FcDirCacheLoadFile (file_name, NULL); 3092c393a42Smrg if (!cache) 3102c393a42Smrg { 3112c393a42Smrg if (verbose) 3122c393a42Smrg printf ("%s: invalid cache file: %s\n", dir, ent->d_name); 3132c393a42Smrg remove = FcTrue; 3142c393a42Smrg } 3152c393a42Smrg else 3162c393a42Smrg { 3172c393a42Smrg target_dir = FcCacheDir (cache); 3182c393a42Smrg if (stat ((char *) target_dir, &target_stat) < 0) 3192c393a42Smrg { 3202c393a42Smrg if (verbose) 3212c393a42Smrg printf ("%s: %s: missing directory: %s \n", 3222c393a42Smrg dir, ent->d_name, target_dir); 3232c393a42Smrg remove = FcTrue; 3242c393a42Smrg } 3252c393a42Smrg } 3262c393a42Smrg if (remove) 3272c393a42Smrg { 3282c393a42Smrg if (unlink ((char *) file_name) < 0) 3292c393a42Smrg { 3302c393a42Smrg perror ((char *) file_name); 3312c393a42Smrg ret = FcFalse; 3322c393a42Smrg } 3332c393a42Smrg } 3342c393a42Smrg FcDirCacheUnload (cache); 3352c393a42Smrg FcStrFree (file_name); 3362c393a42Smrg } 3372c393a42Smrg 3382c393a42Smrg closedir (d); 3392c393a42Smrg FcStrFree (dir_base); 3402c393a42Smrg return ret; 3412c393a42Smrg} 3422c393a42Smrg 3432c393a42Smrgstatic FcBool 3442c393a42SmrgcleanCacheDirectories (FcConfig *config, FcBool verbose) 3452c393a42Smrg{ 3462c393a42Smrg FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 3472c393a42Smrg FcChar8 *cache_dir; 3482c393a42Smrg FcBool ret = FcTrue; 3492c393a42Smrg 3502c393a42Smrg if (!cache_dirs) 3512c393a42Smrg return FcFalse; 3522c393a42Smrg while ((cache_dir = FcStrListNext (cache_dirs))) 3532c393a42Smrg { 3542c393a42Smrg if (!cleanCacheDirectory (config, cache_dir, verbose)) 3552c393a42Smrg { 3562c393a42Smrg ret = FcFalse; 3572c393a42Smrg break; 3582c393a42Smrg } 3592c393a42Smrg } 3602c393a42Smrg FcStrListDone (cache_dirs); 3612c393a42Smrg return ret; 3622c393a42Smrg} 3632c393a42Smrg 3642c393a42Smrgint 3652c393a42Smrgmain (int argc, char **argv) 3662c393a42Smrg{ 3672c393a42Smrg FcStrSet *dirs; 3682c393a42Smrg FcStrList *list; 3692c393a42Smrg FcBool verbose = FcFalse; 3707f785a56Sjmcneill FcBool quick = FcFalse; 3712c393a42Smrg FcBool force = FcFalse; 3722c393a42Smrg FcBool really_force = FcFalse; 3732c393a42Smrg FcBool systemOnly = FcFalse; 3742c393a42Smrg FcConfig *config; 3752c393a42Smrg int i; 3762c393a42Smrg int ret; 3772c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT 3782c393a42Smrg int c; 3792c393a42Smrg 3802c393a42Smrg#if HAVE_GETOPT_LONG 3817f785a56Sjmcneill while ((c = getopt_long (argc, argv, "fqrsVvh", longopts, NULL)) != -1) 3822c393a42Smrg#else 3837f785a56Sjmcneill while ((c = getopt (argc, argv, "fqrsVvh")) != -1) 3842c393a42Smrg#endif 3852c393a42Smrg { 3862c393a42Smrg switch (c) { 3872c393a42Smrg case 'r': 3882c393a42Smrg really_force = FcTrue; 3892c393a42Smrg /* fall through */ 3902c393a42Smrg case 'f': 3912c393a42Smrg force = FcTrue; 3922c393a42Smrg break; 3937f785a56Sjmcneill case 'q': 3947f785a56Sjmcneill quick = FcTrue; 3957f785a56Sjmcneill break; 3962c393a42Smrg case 's': 3972c393a42Smrg systemOnly = FcTrue; 3982c393a42Smrg break; 3992c393a42Smrg case 'V': 4002c393a42Smrg fprintf (stderr, "fontconfig version %d.%d.%d\n", 4012c393a42Smrg FC_MAJOR, FC_MINOR, FC_REVISION); 4022c393a42Smrg exit (0); 4032c393a42Smrg case 'v': 4042c393a42Smrg verbose = FcTrue; 4052c393a42Smrg break; 406a6844aabSmrg case 'h': 407a6844aabSmrg usage (argv[0], 0); 4082c393a42Smrg default: 409a6844aabSmrg usage (argv[0], 1); 4102c393a42Smrg } 4112c393a42Smrg } 4122c393a42Smrg i = optind; 4132c393a42Smrg#else 4142c393a42Smrg i = 1; 4152c393a42Smrg#endif 4162c393a42Smrg 4172c393a42Smrg if (systemOnly) 4182c393a42Smrg FcConfigEnableHome (FcFalse); 4192c393a42Smrg config = FcInitLoadConfig (); 4202c393a42Smrg if (!config) 4212c393a42Smrg { 4222c393a42Smrg fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 4232c393a42Smrg return 1; 4242c393a42Smrg } 4252c393a42Smrg FcConfigSetCurrent (config); 4262c393a42Smrg 4272c393a42Smrg if (argv[i]) 4282c393a42Smrg { 4292c393a42Smrg dirs = FcStrSetCreate (); 4302c393a42Smrg if (!dirs) 4312c393a42Smrg { 4322c393a42Smrg fprintf (stderr, "%s: Can't create list of directories\n", 4332c393a42Smrg argv[0]); 4342c393a42Smrg return 1; 4352c393a42Smrg } 4362c393a42Smrg while (argv[i]) 4372c393a42Smrg { 4382c393a42Smrg if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 4392c393a42Smrg { 4402c393a42Smrg fprintf (stderr, "%s: Can't add directory\n", argv[0]); 4412c393a42Smrg return 1; 4422c393a42Smrg } 4432c393a42Smrg i++; 4442c393a42Smrg } 4452c393a42Smrg list = FcStrListCreate (dirs); 4462c393a42Smrg FcStrSetDestroy (dirs); 4472c393a42Smrg } 4482c393a42Smrg else 4492c393a42Smrg list = FcConfigGetConfigDirs (config); 4502c393a42Smrg 4512c393a42Smrg if ((processed_dirs = FcStrSetCreate()) == NULL) { 4522c393a42Smrg fprintf(stderr, "Cannot malloc\n"); 4532c393a42Smrg return 1; 4542c393a42Smrg } 4552c393a42Smrg 4562c393a42Smrg ret = scanDirs (list, config, force, really_force, verbose); 4572c393a42Smrg 4582c393a42Smrg FcStrSetDestroy (processed_dirs); 4592c393a42Smrg 4602c393a42Smrg cleanCacheDirectories (config, verbose); 4612c393a42Smrg 4622c393a42Smrg /* 4632c393a42Smrg * Now we need to sleep a second (or two, to be extra sure), to make 4642c393a42Smrg * sure that timestamps for changes after this run of fc-cache are later 4652c393a42Smrg * then any timestamps we wrote. We don't use gettimeofday() because 4662c393a42Smrg * sleep(3) can't be interrupted by a signal here -- this isn't in the 4672c393a42Smrg * library, and there aren't any signals flying around here. 4682c393a42Smrg */ 4692c393a42Smrg FcConfigDestroy (config); 4702c393a42Smrg FcFini (); 4717f785a56Sjmcneill if (!quick) 4727f785a56Sjmcneill sleep (2); 4732c393a42Smrg if (verbose) 4742c393a42Smrg printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 4752c393a42Smrg return ret; 4762c393a42Smrg} 477