fc-cache.c revision 898dab68
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 10898dab68Smrg * documentation, and that the name of the author(s) not be used in 112c393a42Smrg * advertising or publicity pertaining to distribution of the software without 12898dab68Smrg * specific, written prior permission. The authors make 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#ifdef HAVE_CONFIG_H 262c393a42Smrg#include <config.h> 272c393a42Smrg#else 282c393a42Smrg#ifdef linux 292c393a42Smrg#define HAVE_GETOPT_LONG 1 302c393a42Smrg#endif 312c393a42Smrg#define HAVE_GETOPT 1 322c393a42Smrg#endif 332c393a42Smrg 342c393a42Smrg#include <fontconfig/fontconfig.h> 352c393a42Smrg#include <stdio.h> 362c393a42Smrg#include <stdlib.h> 372c393a42Smrg#include <unistd.h> 382c393a42Smrg#include <sys/types.h> 392c393a42Smrg#include <sys/stat.h> 402c393a42Smrg#include <errno.h> 412c393a42Smrg#include <fcntl.h> 422c393a42Smrg#include <dirent.h> 432c393a42Smrg#include <string.h> 442c393a42Smrg 452c393a42Smrg#if defined (_WIN32) 462c393a42Smrg#define STRICT 472c393a42Smrg#include <windows.h> 482c393a42Smrg#define sleep(x) Sleep((x) * 1000) 492c393a42Smrg#undef STRICT 502c393a42Smrg#endif 512c393a42Smrg 522c393a42Smrg#ifndef O_BINARY 532c393a42Smrg#define O_BINARY 0 542c393a42Smrg#endif 552c393a42Smrg 562c393a42Smrg#ifndef HAVE_GETOPT 572c393a42Smrg#define HAVE_GETOPT 0 582c393a42Smrg#endif 592c393a42Smrg#ifndef HAVE_GETOPT_LONG 602c393a42Smrg#define HAVE_GETOPT_LONG 0 612c393a42Smrg#endif 622c393a42Smrg 632c393a42Smrg#if HAVE_GETOPT_LONG 642c393a42Smrg#undef _GNU_SOURCE 652c393a42Smrg#define _GNU_SOURCE 662c393a42Smrg#include <getopt.h> 672c393a42Smrgconst struct option longopts[] = { 682c393a42Smrg {"force", 0, 0, 'f'}, 697f785a56Sjmcneill {"quick", 0, 0, 'q'}, 702c393a42Smrg {"really-force", 0, 0, 'r'}, 712c393a42Smrg {"system-only", 0, 0, 's'}, 722c393a42Smrg {"version", 0, 0, 'V'}, 732c393a42Smrg {"verbose", 0, 0, 'v'}, 74a6844aabSmrg {"help", 0, 0, 'h'}, 752c393a42Smrg {NULL,0,0,0}, 762c393a42Smrg}; 772c393a42Smrg#else 782c393a42Smrg#if HAVE_GETOPT 792c393a42Smrgextern char *optarg; 802c393a42Smrgextern int optind, opterr, optopt; 812c393a42Smrg#endif 822c393a42Smrg#endif 832c393a42Smrg 842c393a42Smrgstatic void 85a6844aabSmrgusage (char *program, int error) 862c393a42Smrg{ 87a6844aabSmrg FILE *file = error ? stderr : stdout; 882c393a42Smrg#if HAVE_GETOPT_LONG 897f785a56Sjmcneill fprintf (file, "usage: %s [-fqrsvVh] [--quick] [--force|--really-force] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 902c393a42Smrg program); 912c393a42Smrg#else 927f785a56Sjmcneill fprintf (file, "usage: %s [-fqrsvVh] [dirs]\n", 932c393a42Smrg program); 942c393a42Smrg#endif 95a6844aabSmrg fprintf (file, "Build font information caches in [dirs]\n" 962c393a42Smrg "(all directories in font configuration by default).\n"); 97a6844aabSmrg fprintf (file, "\n"); 982c393a42Smrg#if HAVE_GETOPT_LONG 99a6844aabSmrg fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 1007f785a56Sjmcneill fprintf (file, " -q, --quick don't sleep before exiting\n"); 101a6844aabSmrg fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 102a6844aabSmrg fprintf (file, " -s, --system-only scan system-wide directories only\n"); 103a6844aabSmrg fprintf (file, " -v, --verbose display status information while busy\n"); 104a6844aabSmrg fprintf (file, " -V, --version display font config version and exit\n"); 105a6844aabSmrg fprintf (file, " -h, --help display this help and exit\n"); 1062c393a42Smrg#else 107a6844aabSmrg fprintf (file, " -f (force) scan directories with apparently valid caches\n"); 1087f785a56Sjmcneill fprintf (file, " -q (quick) don't sleep before exiting\n"); 109a6844aabSmrg fprintf (file, " -r, (really force) erase all existing caches, then rescan\n"); 110a6844aabSmrg fprintf (file, " -s (system) scan system-wide directories only\n"); 111a6844aabSmrg fprintf (file, " -v (verbose) display status information while busy\n"); 112a6844aabSmrg fprintf (file, " -V (version) display font config version and exit\n"); 113a6844aabSmrg fprintf (file, " -h (help) display this help and exit\n"); 1142c393a42Smrg#endif 115a6844aabSmrg exit (error); 1162c393a42Smrg} 1172c393a42Smrg 1182c393a42Smrgstatic FcStrSet *processed_dirs; 1192c393a42Smrg 1202c393a42Smrgstatic int 121898dab68SmrgscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, int *changed) 1222c393a42Smrg{ 1232c393a42Smrg int ret = 0; 1242c393a42Smrg const FcChar8 *dir; 1252c393a42Smrg FcStrSet *subdirs; 1262c393a42Smrg FcStrList *sublist; 1272c393a42Smrg FcCache *cache; 1282c393a42Smrg struct stat statb; 1292c393a42Smrg FcBool was_valid; 1302c393a42Smrg int i; 1312c393a42Smrg 1322c393a42Smrg /* 1332c393a42Smrg * Now scan all of the directories into separate databases 1342c393a42Smrg * and write out the results 1352c393a42Smrg */ 1362c393a42Smrg while ((dir = FcStrListNext (list))) 1372c393a42Smrg { 1382c393a42Smrg if (verbose) 1392c393a42Smrg { 1402c393a42Smrg printf ("%s: ", dir); 1412c393a42Smrg fflush (stdout); 1422c393a42Smrg } 1432c393a42Smrg 1442c393a42Smrg if (!dir) 1452c393a42Smrg { 1462c393a42Smrg if (verbose) 1472c393a42Smrg printf ("skipping, no such directory\n"); 1482c393a42Smrg continue; 1492c393a42Smrg } 1502c393a42Smrg 1512c393a42Smrg if (FcStrSetMember (processed_dirs, dir)) 1522c393a42Smrg { 1532c393a42Smrg if (verbose) 1542c393a42Smrg printf ("skipping, looped directory detected\n"); 1552c393a42Smrg continue; 1562c393a42Smrg } 1572c393a42Smrg 1582c393a42Smrg if (stat ((char *) dir, &statb) == -1) 1592c393a42Smrg { 1602c393a42Smrg switch (errno) { 1612c393a42Smrg case ENOENT: 1622c393a42Smrg case ENOTDIR: 1632c393a42Smrg if (verbose) 1642c393a42Smrg printf ("skipping, no such directory\n"); 1652c393a42Smrg break; 1662c393a42Smrg default: 1672c393a42Smrg fprintf (stderr, "\"%s\": ", dir); 1682c393a42Smrg perror (""); 1692c393a42Smrg ret++; 1702c393a42Smrg break; 1712c393a42Smrg } 1722c393a42Smrg continue; 1732c393a42Smrg } 1742c393a42Smrg 1752c393a42Smrg if (!S_ISDIR (statb.st_mode)) 1762c393a42Smrg { 1772c393a42Smrg fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 1782c393a42Smrg continue; 1792c393a42Smrg } 1802c393a42Smrg 1812c393a42Smrg if (really_force) 1822c393a42Smrg FcDirCacheUnlink (dir, config); 1832c393a42Smrg 1842c393a42Smrg cache = NULL; 1852c393a42Smrg was_valid = FcFalse; 1862c393a42Smrg if (!force) { 1872c393a42Smrg cache = FcDirCacheLoad (dir, config, NULL); 1882c393a42Smrg if (cache) 1892c393a42Smrg was_valid = FcTrue; 1902c393a42Smrg } 1912c393a42Smrg 1922c393a42Smrg if (!cache) 1932c393a42Smrg { 194898dab68Smrg (*changed)++; 1952c393a42Smrg cache = FcDirCacheRead (dir, FcTrue, config); 1962c393a42Smrg if (!cache) 1972c393a42Smrg { 1982c393a42Smrg fprintf (stderr, "%s: error scanning\n", dir); 1992c393a42Smrg ret++; 2002c393a42Smrg continue; 2012c393a42Smrg } 2022c393a42Smrg } 2032c393a42Smrg 2042c393a42Smrg if (was_valid) 2052c393a42Smrg { 2062c393a42Smrg if (verbose) 2072c393a42Smrg printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 2082c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2092c393a42Smrg } 2102c393a42Smrg else 2112c393a42Smrg { 2122c393a42Smrg if (verbose) 2132c393a42Smrg printf ("caching, new cache contents: %d fonts, %d dirs\n", 2142c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2152c393a42Smrg 2162c393a42Smrg if (!FcDirCacheValid (dir)) 2172c393a42Smrg { 2182c393a42Smrg fprintf (stderr, "%s: failed to write cache\n", dir); 2192c393a42Smrg (void) FcDirCacheUnlink (dir, config); 2202c393a42Smrg ret++; 2212c393a42Smrg } 2222c393a42Smrg } 2232c393a42Smrg 2242c393a42Smrg subdirs = FcStrSetCreate (); 2252c393a42Smrg if (!subdirs) 2262c393a42Smrg { 2272c393a42Smrg fprintf (stderr, "%s: Can't create subdir set\n", dir); 2282c393a42Smrg ret++; 2292c393a42Smrg FcDirCacheUnload (cache); 2302c393a42Smrg continue; 2312c393a42Smrg } 2322c393a42Smrg for (i = 0; i < FcCacheNumSubdir (cache); i++) 2332c393a42Smrg FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2342c393a42Smrg 2352c393a42Smrg FcDirCacheUnload (cache); 2362c393a42Smrg 2372c393a42Smrg sublist = FcStrListCreate (subdirs); 2382c393a42Smrg FcStrSetDestroy (subdirs); 2392c393a42Smrg if (!sublist) 2402c393a42Smrg { 2412c393a42Smrg fprintf (stderr, "%s: Can't create subdir list\n", dir); 2422c393a42Smrg ret++; 2432c393a42Smrg continue; 2442c393a42Smrg } 2452c393a42Smrg FcStrSetAdd (processed_dirs, dir); 246898dab68Smrg ret += scanDirs (sublist, config, force, really_force, verbose, changed); 2472c393a42Smrg } 2482c393a42Smrg FcStrListDone (list); 2492c393a42Smrg return ret; 2502c393a42Smrg} 2512c393a42Smrg 2522c393a42Smrgstatic FcBool 2532c393a42SmrgcleanCacheDirectories (FcConfig *config, FcBool verbose) 2542c393a42Smrg{ 2552c393a42Smrg FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 2562c393a42Smrg FcChar8 *cache_dir; 2572c393a42Smrg FcBool ret = FcTrue; 2582c393a42Smrg 2592c393a42Smrg if (!cache_dirs) 2602c393a42Smrg return FcFalse; 2612c393a42Smrg while ((cache_dir = FcStrListNext (cache_dirs))) 2622c393a42Smrg { 263898dab68Smrg if (!FcDirCacheClean (cache_dir, verbose)) 2642c393a42Smrg { 2652c393a42Smrg ret = FcFalse; 2662c393a42Smrg break; 2672c393a42Smrg } 2682c393a42Smrg } 2692c393a42Smrg FcStrListDone (cache_dirs); 2702c393a42Smrg return ret; 2712c393a42Smrg} 2722c393a42Smrg 2732c393a42Smrgint 2742c393a42Smrgmain (int argc, char **argv) 2752c393a42Smrg{ 2762c393a42Smrg FcStrSet *dirs; 2772c393a42Smrg FcStrList *list; 2782c393a42Smrg FcBool verbose = FcFalse; 2797f785a56Sjmcneill FcBool quick = FcFalse; 2802c393a42Smrg FcBool force = FcFalse; 2812c393a42Smrg FcBool really_force = FcFalse; 2822c393a42Smrg FcBool systemOnly = FcFalse; 2832c393a42Smrg FcConfig *config; 2842c393a42Smrg int i; 285898dab68Smrg int changed; 2862c393a42Smrg int ret; 2872c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT 2882c393a42Smrg int c; 2892c393a42Smrg 2902c393a42Smrg#if HAVE_GETOPT_LONG 2917f785a56Sjmcneill while ((c = getopt_long (argc, argv, "fqrsVvh", longopts, NULL)) != -1) 2922c393a42Smrg#else 2937f785a56Sjmcneill while ((c = getopt (argc, argv, "fqrsVvh")) != -1) 2942c393a42Smrg#endif 2952c393a42Smrg { 2962c393a42Smrg switch (c) { 2972c393a42Smrg case 'r': 2982c393a42Smrg really_force = FcTrue; 2992c393a42Smrg /* fall through */ 3002c393a42Smrg case 'f': 3012c393a42Smrg force = FcTrue; 3022c393a42Smrg break; 3037f785a56Sjmcneill case 'q': 3047f785a56Sjmcneill quick = FcTrue; 3057f785a56Sjmcneill break; 3062c393a42Smrg case 's': 3072c393a42Smrg systemOnly = FcTrue; 3082c393a42Smrg break; 3092c393a42Smrg case 'V': 3102c393a42Smrg fprintf (stderr, "fontconfig version %d.%d.%d\n", 3112c393a42Smrg FC_MAJOR, FC_MINOR, FC_REVISION); 3122c393a42Smrg exit (0); 3132c393a42Smrg case 'v': 3142c393a42Smrg verbose = FcTrue; 3152c393a42Smrg break; 316a6844aabSmrg case 'h': 317a6844aabSmrg usage (argv[0], 0); 3182c393a42Smrg default: 319a6844aabSmrg usage (argv[0], 1); 3202c393a42Smrg } 3212c393a42Smrg } 3222c393a42Smrg i = optind; 3232c393a42Smrg#else 3242c393a42Smrg i = 1; 3252c393a42Smrg#endif 3262c393a42Smrg 3272c393a42Smrg if (systemOnly) 3282c393a42Smrg FcConfigEnableHome (FcFalse); 3292c393a42Smrg config = FcInitLoadConfig (); 3302c393a42Smrg if (!config) 3312c393a42Smrg { 3322c393a42Smrg fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 3332c393a42Smrg return 1; 3342c393a42Smrg } 3352c393a42Smrg FcConfigSetCurrent (config); 3362c393a42Smrg 3372c393a42Smrg if (argv[i]) 3382c393a42Smrg { 3392c393a42Smrg dirs = FcStrSetCreate (); 3402c393a42Smrg if (!dirs) 3412c393a42Smrg { 3422c393a42Smrg fprintf (stderr, "%s: Can't create list of directories\n", 3432c393a42Smrg argv[0]); 3442c393a42Smrg return 1; 3452c393a42Smrg } 3462c393a42Smrg while (argv[i]) 3472c393a42Smrg { 3482c393a42Smrg if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 3492c393a42Smrg { 3502c393a42Smrg fprintf (stderr, "%s: Can't add directory\n", argv[0]); 3512c393a42Smrg return 1; 3522c393a42Smrg } 3532c393a42Smrg i++; 3542c393a42Smrg } 3552c393a42Smrg list = FcStrListCreate (dirs); 3562c393a42Smrg FcStrSetDestroy (dirs); 3572c393a42Smrg } 3582c393a42Smrg else 3592c393a42Smrg list = FcConfigGetConfigDirs (config); 3602c393a42Smrg 3612c393a42Smrg if ((processed_dirs = FcStrSetCreate()) == NULL) { 3622c393a42Smrg fprintf(stderr, "Cannot malloc\n"); 3632c393a42Smrg return 1; 3642c393a42Smrg } 3652c393a42Smrg 366898dab68Smrg changed = 0; 367898dab68Smrg ret = scanDirs (list, config, force, really_force, verbose, &changed); 368898dab68Smrg 369898dab68Smrg /* 370898dab68Smrg * Try to create CACHEDIR.TAG anyway. 371898dab68Smrg * This expects the fontconfig cache directory already exists. 372898dab68Smrg * If it doesn't, it won't be simply created. 373898dab68Smrg */ 374898dab68Smrg FcCacheCreateTagFile (config); 3752c393a42Smrg 3762c393a42Smrg FcStrSetDestroy (processed_dirs); 3772c393a42Smrg 3782c393a42Smrg cleanCacheDirectories (config, verbose); 3792c393a42Smrg 3802c393a42Smrg /* 3812c393a42Smrg * Now we need to sleep a second (or two, to be extra sure), to make 3822c393a42Smrg * sure that timestamps for changes after this run of fc-cache are later 3832c393a42Smrg * then any timestamps we wrote. We don't use gettimeofday() because 3842c393a42Smrg * sleep(3) can't be interrupted by a signal here -- this isn't in the 3852c393a42Smrg * library, and there aren't any signals flying around here. 3862c393a42Smrg */ 3872c393a42Smrg FcConfigDestroy (config); 3882c393a42Smrg FcFini (); 389898dab68Smrg if (!quick && changed) 390898dab68Smrg sleep (2); 3912c393a42Smrg if (verbose) 3922c393a42Smrg printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 3932c393a42Smrg return ret; 3942c393a42Smrg} 395