fc-cache.c revision d91dd368
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'}, 715e61939bSmrg {"sysroot", 0, 0, 'y'}, 722c393a42Smrg {"system-only", 0, 0, 's'}, 732c393a42Smrg {"version", 0, 0, 'V'}, 742c393a42Smrg {"verbose", 0, 0, 'v'}, 75a6844aabSmrg {"help", 0, 0, 'h'}, 762c393a42Smrg {NULL,0,0,0}, 772c393a42Smrg}; 782c393a42Smrg#else 792c393a42Smrg#if HAVE_GETOPT 802c393a42Smrgextern char *optarg; 812c393a42Smrgextern int optind, opterr, optopt; 822c393a42Smrg#endif 832c393a42Smrg#endif 842c393a42Smrg 852c393a42Smrgstatic void 86a6844aabSmrgusage (char *program, int error) 872c393a42Smrg{ 88a6844aabSmrg FILE *file = error ? stderr : stdout; 892c393a42Smrg#if HAVE_GETOPT_LONG 905e61939bSmrg fprintf (file, "usage: %s [-fqrsvVh] [--quick] [-y SYSROOT] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 912c393a42Smrg program); 922c393a42Smrg#else 935e61939bSmrg fprintf (file, "usage: %s [-fqrsvVh] [-y SYSROOT] [dirs]\n", 942c393a42Smrg program); 952c393a42Smrg#endif 96a6844aabSmrg fprintf (file, "Build font information caches in [dirs]\n" 972c393a42Smrg "(all directories in font configuration by default).\n"); 98a6844aabSmrg fprintf (file, "\n"); 992c393a42Smrg#if HAVE_GETOPT_LONG 1005e61939bSmrg fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 1015e61939bSmrg fprintf (file, " -q, --quick don't sleep before exiting\n"); 1025e61939bSmrg fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 1035e61939bSmrg fprintf (file, " -s, --system-only scan system-wide directories only\n"); 1045e61939bSmrg fprintf (file, " -y, --sysroot=SYSROOT prepend SYSROOT to all paths for scanning\n"); 1055e61939bSmrg fprintf (file, " -v, --verbose display status information while busy\n"); 1065e61939bSmrg fprintf (file, " -V, --version display font config version and exit\n"); 1075e61939bSmrg 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"); 1135e61939bSmrg fprintf (file, " -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n"); 114a6844aabSmrg fprintf (file, " -v (verbose) display status information while busy\n"); 115a6844aabSmrg fprintf (file, " -V (version) display font config version and exit\n"); 116a6844aabSmrg fprintf (file, " -h (help) display this help and exit\n"); 1172c393a42Smrg#endif 118a6844aabSmrg exit (error); 1192c393a42Smrg} 1202c393a42Smrg 1212c393a42Smrgstatic FcStrSet *processed_dirs; 1222c393a42Smrg 1232c393a42Smrgstatic int 124d91dd368SmrgscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool recursive, int *changed) 1252c393a42Smrg{ 1262c393a42Smrg int ret = 0; 1272c393a42Smrg const FcChar8 *dir; 1282c393a42Smrg FcStrSet *subdirs; 1292c393a42Smrg FcStrList *sublist; 1302c393a42Smrg FcCache *cache; 1312c393a42Smrg struct stat statb; 1322c393a42Smrg FcBool was_valid; 1332c393a42Smrg int i; 1342c393a42Smrg 1352c393a42Smrg /* 1362c393a42Smrg * Now scan all of the directories into separate databases 1372c393a42Smrg * and write out the results 1382c393a42Smrg */ 1392c393a42Smrg while ((dir = FcStrListNext (list))) 1402c393a42Smrg { 1412c393a42Smrg if (verbose) 1422c393a42Smrg { 1432c393a42Smrg printf ("%s: ", dir); 1442c393a42Smrg fflush (stdout); 1452c393a42Smrg } 1462c393a42Smrg 147d91dd368Smrg if (recursive && FcStrSetMember (processed_dirs, dir)) 1482c393a42Smrg { 1492c393a42Smrg if (verbose) 1502c393a42Smrg printf ("skipping, looped directory detected\n"); 1512c393a42Smrg continue; 1522c393a42Smrg } 1532c393a42Smrg 1542c393a42Smrg if (stat ((char *) dir, &statb) == -1) 1552c393a42Smrg { 1562c393a42Smrg switch (errno) { 1572c393a42Smrg case ENOENT: 1582c393a42Smrg case ENOTDIR: 1592c393a42Smrg if (verbose) 1602c393a42Smrg printf ("skipping, no such directory\n"); 1612c393a42Smrg break; 1622c393a42Smrg default: 1632c393a42Smrg fprintf (stderr, "\"%s\": ", dir); 1642c393a42Smrg perror (""); 1652c393a42Smrg ret++; 1662c393a42Smrg break; 1672c393a42Smrg } 1682c393a42Smrg continue; 1692c393a42Smrg } 1702c393a42Smrg 1712c393a42Smrg if (!S_ISDIR (statb.st_mode)) 1722c393a42Smrg { 1732c393a42Smrg fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 1742c393a42Smrg continue; 1752c393a42Smrg } 1762c393a42Smrg 1772c393a42Smrg if (really_force) 1782c393a42Smrg FcDirCacheUnlink (dir, config); 1792c393a42Smrg 1802c393a42Smrg cache = NULL; 1812c393a42Smrg was_valid = FcFalse; 1822c393a42Smrg if (!force) { 1832c393a42Smrg cache = FcDirCacheLoad (dir, config, NULL); 1842c393a42Smrg if (cache) 1852c393a42Smrg was_valid = FcTrue; 1862c393a42Smrg } 1872c393a42Smrg 1882c393a42Smrg if (!cache) 1892c393a42Smrg { 190898dab68Smrg (*changed)++; 1912c393a42Smrg cache = FcDirCacheRead (dir, FcTrue, config); 1922c393a42Smrg if (!cache) 1932c393a42Smrg { 1942c393a42Smrg fprintf (stderr, "%s: error scanning\n", dir); 1952c393a42Smrg ret++; 1962c393a42Smrg continue; 1972c393a42Smrg } 1982c393a42Smrg } 1992c393a42Smrg 2002c393a42Smrg if (was_valid) 2012c393a42Smrg { 2022c393a42Smrg if (verbose) 2032c393a42Smrg printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 2042c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2052c393a42Smrg } 2062c393a42Smrg else 2072c393a42Smrg { 2082c393a42Smrg if (verbose) 2092c393a42Smrg printf ("caching, new cache contents: %d fonts, %d dirs\n", 2102c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2112c393a42Smrg 2122c393a42Smrg if (!FcDirCacheValid (dir)) 2132c393a42Smrg { 2142c393a42Smrg fprintf (stderr, "%s: failed to write cache\n", dir); 2152c393a42Smrg (void) FcDirCacheUnlink (dir, config); 2162c393a42Smrg ret++; 2172c393a42Smrg } 2182c393a42Smrg } 219d91dd368Smrg 220d91dd368Smrg if (recursive) 2212c393a42Smrg { 222d91dd368Smrg subdirs = FcStrSetCreate (); 223d91dd368Smrg if (!subdirs) 224d91dd368Smrg { 225d91dd368Smrg fprintf (stderr, "%s: Can't create subdir set\n", dir); 226d91dd368Smrg ret++; 227d91dd368Smrg FcDirCacheUnload (cache); 228d91dd368Smrg continue; 229d91dd368Smrg } 230d91dd368Smrg for (i = 0; i < FcCacheNumSubdir (cache); i++) 231d91dd368Smrg FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2322c393a42Smrg 233d91dd368Smrg FcDirCacheUnload (cache); 2342c393a42Smrg 235d91dd368Smrg sublist = FcStrListCreate (subdirs); 236d91dd368Smrg FcStrSetDestroy (subdirs); 237d91dd368Smrg if (!sublist) 238d91dd368Smrg { 239d91dd368Smrg fprintf (stderr, "%s: Can't create subdir list\n", dir); 240d91dd368Smrg ret++; 241d91dd368Smrg continue; 242d91dd368Smrg } 243d91dd368Smrg FcStrSetAdd (processed_dirs, dir); 244d91dd368Smrg ret += scanDirs (sublist, config, force, really_force, verbose, recursive, changed); 245d91dd368Smrg FcStrListDone (sublist); 2462c393a42Smrg } 247d91dd368Smrg else 248d91dd368Smrg FcDirCacheUnload (cache); 2492c393a42Smrg } 2502c393a42Smrg return ret; 2512c393a42Smrg} 2522c393a42Smrg 2532c393a42Smrgstatic FcBool 2542c393a42SmrgcleanCacheDirectories (FcConfig *config, FcBool verbose) 2552c393a42Smrg{ 2562c393a42Smrg FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 2572c393a42Smrg FcChar8 *cache_dir; 2582c393a42Smrg FcBool ret = FcTrue; 2592c393a42Smrg 2602c393a42Smrg if (!cache_dirs) 2612c393a42Smrg return FcFalse; 2622c393a42Smrg while ((cache_dir = FcStrListNext (cache_dirs))) 2632c393a42Smrg { 264898dab68Smrg if (!FcDirCacheClean (cache_dir, verbose)) 2652c393a42Smrg { 2662c393a42Smrg ret = FcFalse; 2672c393a42Smrg break; 2682c393a42Smrg } 2692c393a42Smrg } 2702c393a42Smrg FcStrListDone (cache_dirs); 2712c393a42Smrg return ret; 2722c393a42Smrg} 2732c393a42Smrg 2742c393a42Smrgint 2752c393a42Smrgmain (int argc, char **argv) 2762c393a42Smrg{ 2772c393a42Smrg FcStrSet *dirs; 2782c393a42Smrg FcStrList *list; 2792c393a42Smrg FcBool verbose = FcFalse; 2807f785a56Sjmcneill FcBool quick = FcFalse; 2812c393a42Smrg FcBool force = FcFalse; 2822c393a42Smrg FcBool really_force = FcFalse; 2832c393a42Smrg FcBool systemOnly = FcFalse; 2842c393a42Smrg FcConfig *config; 2855e61939bSmrg FcChar8 *sysroot = NULL; 2862c393a42Smrg int i; 287898dab68Smrg int changed; 2882c393a42Smrg int ret; 2892c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT 2902c393a42Smrg int c; 2912c393a42Smrg 2922c393a42Smrg#if HAVE_GETOPT_LONG 2935e61939bSmrg while ((c = getopt_long (argc, argv, "fqrsy:Vvh", longopts, NULL)) != -1) 2942c393a42Smrg#else 2955e61939bSmrg while ((c = getopt (argc, argv, "fqrsy:Vvh")) != -1) 2962c393a42Smrg#endif 2972c393a42Smrg { 2982c393a42Smrg switch (c) { 2992c393a42Smrg case 'r': 3002c393a42Smrg really_force = FcTrue; 3012c393a42Smrg /* fall through */ 3022c393a42Smrg case 'f': 3032c393a42Smrg force = FcTrue; 3042c393a42Smrg break; 3057f785a56Sjmcneill case 'q': 3067f785a56Sjmcneill quick = FcTrue; 3077f785a56Sjmcneill break; 3082c393a42Smrg case 's': 3092c393a42Smrg systemOnly = FcTrue; 3102c393a42Smrg break; 3115e61939bSmrg case 'y': 3125e61939bSmrg sysroot = FcStrCopy ((const FcChar8 *)optarg); 3135e61939bSmrg break; 3142c393a42Smrg case 'V': 3152c393a42Smrg fprintf (stderr, "fontconfig version %d.%d.%d\n", 3162c393a42Smrg FC_MAJOR, FC_MINOR, FC_REVISION); 3172c393a42Smrg exit (0); 3182c393a42Smrg case 'v': 3192c393a42Smrg verbose = FcTrue; 3202c393a42Smrg break; 321a6844aabSmrg case 'h': 322a6844aabSmrg usage (argv[0], 0); 3232c393a42Smrg default: 324a6844aabSmrg usage (argv[0], 1); 3252c393a42Smrg } 3262c393a42Smrg } 3272c393a42Smrg i = optind; 3282c393a42Smrg#else 3292c393a42Smrg i = 1; 3302c393a42Smrg#endif 3312c393a42Smrg 3322c393a42Smrg if (systemOnly) 3332c393a42Smrg FcConfigEnableHome (FcFalse); 3345e61939bSmrg if (sysroot) 3355e61939bSmrg { 3365e61939bSmrg FcConfigSetSysRoot (NULL, sysroot); 3375e61939bSmrg FcStrFree (sysroot); 3385e61939bSmrg config = FcConfigGetCurrent(); 3395e61939bSmrg } 3405e61939bSmrg else 3415e61939bSmrg { 3425e61939bSmrg config = FcInitLoadConfig (); 3435e61939bSmrg } 3442c393a42Smrg if (!config) 3452c393a42Smrg { 3462c393a42Smrg fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 3472c393a42Smrg return 1; 3482c393a42Smrg } 3492c393a42Smrg FcConfigSetCurrent (config); 3502c393a42Smrg 3512c393a42Smrg if (argv[i]) 3522c393a42Smrg { 3532c393a42Smrg dirs = FcStrSetCreate (); 3542c393a42Smrg if (!dirs) 3552c393a42Smrg { 3562c393a42Smrg fprintf (stderr, "%s: Can't create list of directories\n", 3572c393a42Smrg argv[0]); 3582c393a42Smrg return 1; 3592c393a42Smrg } 3602c393a42Smrg while (argv[i]) 3612c393a42Smrg { 3622c393a42Smrg if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 3632c393a42Smrg { 3642c393a42Smrg fprintf (stderr, "%s: Can't add directory\n", argv[0]); 3652c393a42Smrg return 1; 3662c393a42Smrg } 3672c393a42Smrg i++; 3682c393a42Smrg } 3692c393a42Smrg list = FcStrListCreate (dirs); 3702c393a42Smrg FcStrSetDestroy (dirs); 3712c393a42Smrg } 3722c393a42Smrg else 3732c393a42Smrg list = FcConfigGetConfigDirs (config); 3742c393a42Smrg 3752c393a42Smrg if ((processed_dirs = FcStrSetCreate()) == NULL) { 3762c393a42Smrg fprintf(stderr, "Cannot malloc\n"); 3772c393a42Smrg return 1; 3782c393a42Smrg } 3792c393a42Smrg 380898dab68Smrg changed = 0; 381d91dd368Smrg ret = scanDirs (list, config, force, really_force, verbose, FcTrue, &changed); 382d91dd368Smrg /* Update the directory cache again to avoid the race condition as much as possible */ 383d91dd368Smrg FcStrListFirst (list); 384d91dd368Smrg ret += scanDirs (list, config, FcTrue, really_force, verbose, FcFalse, &changed); 385d91dd368Smrg FcStrListDone (list); 386898dab68Smrg 387898dab68Smrg /* 388898dab68Smrg * Try to create CACHEDIR.TAG anyway. 389898dab68Smrg * This expects the fontconfig cache directory already exists. 390898dab68Smrg * If it doesn't, it won't be simply created. 391898dab68Smrg */ 392898dab68Smrg FcCacheCreateTagFile (config); 3932c393a42Smrg 3942c393a42Smrg FcStrSetDestroy (processed_dirs); 3952c393a42Smrg 3962c393a42Smrg cleanCacheDirectories (config, verbose); 3972c393a42Smrg 398d91dd368Smrg FcConfigDestroy (config); 399d91dd368Smrg FcFini (); 4002c393a42Smrg /* 4012c393a42Smrg * Now we need to sleep a second (or two, to be extra sure), to make 4022c393a42Smrg * sure that timestamps for changes after this run of fc-cache are later 4032c393a42Smrg * then any timestamps we wrote. We don't use gettimeofday() because 4042c393a42Smrg * sleep(3) can't be interrupted by a signal here -- this isn't in the 4052c393a42Smrg * library, and there aren't any signals flying around here. 4062c393a42Smrg */ 407d91dd368Smrg /* the resolution of mtime on FAT is 2 seconds */ 408898dab68Smrg if (!quick && changed) 409898dab68Smrg sleep (2); 4102c393a42Smrg if (verbose) 4112c393a42Smrg printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 4122c393a42Smrg return ret; 4132c393a42Smrg} 414