fc-cache.c revision 2c393a42
12c393a42Smrg/* 22c393a42Smrg * $RCSId: xc/lib/fontconfig/fc-cache/fc-cache.c,v 1.8tsi Exp $ 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 * 162c393a42Smrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 182c393a42Smrg * EVENT SHALL KEITH PACKARD 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'}, 712c393a42Smrg {"really-force", 0, 0, 'r'}, 722c393a42Smrg {"system-only", 0, 0, 's'}, 732c393a42Smrg {"version", 0, 0, 'V'}, 742c393a42Smrg {"verbose", 0, 0, 'v'}, 752c393a42Smrg {"help", 0, 0, '?'}, 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 862c393a42Smrgusage (char *program) 872c393a42Smrg{ 882c393a42Smrg#if HAVE_GETOPT_LONG 892c393a42Smrg fprintf (stderr, "usage: %s [-frsvV?] [--force|--really-force] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 902c393a42Smrg program); 912c393a42Smrg#else 922c393a42Smrg fprintf (stderr, "usage: %s [-frsvV?] [dirs]\n", 932c393a42Smrg program); 942c393a42Smrg#endif 952c393a42Smrg fprintf (stderr, "Build font information caches in [dirs]\n" 962c393a42Smrg "(all directories in font configuration by default).\n"); 972c393a42Smrg fprintf (stderr, "\n"); 982c393a42Smrg#if HAVE_GETOPT_LONG 992c393a42Smrg fprintf (stderr, " -f, --force scan directories with apparently valid caches\n"); 1002c393a42Smrg fprintf (stderr, " -r, --really-force erase all existing caches, then rescan\n"); 1012c393a42Smrg fprintf (stderr, " -s, --system-only scan system-wide directories only\n"); 1022c393a42Smrg fprintf (stderr, " -v, --verbose display status information while busy\n"); 1032c393a42Smrg fprintf (stderr, " -V, --version display font config version and exit\n"); 1042c393a42Smrg fprintf (stderr, " -?, --help display this help and exit\n"); 1052c393a42Smrg#else 1062c393a42Smrg fprintf (stderr, " -f (force) scan directories with apparently valid caches\n"); 1072c393a42Smrg fprintf (stderr, " -r, (really force) erase all existing caches, then rescan\n"); 1082c393a42Smrg fprintf (stderr, " -s (system) scan system-wide directories only\n"); 1092c393a42Smrg fprintf (stderr, " -v (verbose) display status information while busy\n"); 1102c393a42Smrg fprintf (stderr, " -V (version) display font config version and exit\n"); 1112c393a42Smrg fprintf (stderr, " -? (help) display this help and exit\n"); 1122c393a42Smrg#endif 1132c393a42Smrg exit (1); 1142c393a42Smrg} 1152c393a42Smrg 1162c393a42Smrgstatic FcStrSet *processed_dirs; 1172c393a42Smrg 1182c393a42Smrgstatic int 1192c393a42SmrgscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose) 1202c393a42Smrg{ 1212c393a42Smrg int ret = 0; 1222c393a42Smrg const FcChar8 *dir; 1232c393a42Smrg FcStrSet *subdirs; 1242c393a42Smrg FcStrList *sublist; 1252c393a42Smrg FcCache *cache; 1262c393a42Smrg struct stat statb; 1272c393a42Smrg FcBool was_valid; 1282c393a42Smrg int i; 1292c393a42Smrg 1302c393a42Smrg /* 1312c393a42Smrg * Now scan all of the directories into separate databases 1322c393a42Smrg * and write out the results 1332c393a42Smrg */ 1342c393a42Smrg while ((dir = FcStrListNext (list))) 1352c393a42Smrg { 1362c393a42Smrg if (verbose) 1372c393a42Smrg { 1382c393a42Smrg printf ("%s: ", dir); 1392c393a42Smrg fflush (stdout); 1402c393a42Smrg } 1412c393a42Smrg 1422c393a42Smrg if (!dir) 1432c393a42Smrg { 1442c393a42Smrg if (verbose) 1452c393a42Smrg printf ("skipping, no such directory\n"); 1462c393a42Smrg continue; 1472c393a42Smrg } 1482c393a42Smrg 1492c393a42Smrg if (FcStrSetMember (processed_dirs, dir)) 1502c393a42Smrg { 1512c393a42Smrg if (verbose) 1522c393a42Smrg printf ("skipping, looped directory detected\n"); 1532c393a42Smrg continue; 1542c393a42Smrg } 1552c393a42Smrg 1562c393a42Smrg if (stat ((char *) dir, &statb) == -1) 1572c393a42Smrg { 1582c393a42Smrg switch (errno) { 1592c393a42Smrg case ENOENT: 1602c393a42Smrg case ENOTDIR: 1612c393a42Smrg if (verbose) 1622c393a42Smrg printf ("skipping, no such directory\n"); 1632c393a42Smrg break; 1642c393a42Smrg default: 1652c393a42Smrg fprintf (stderr, "\"%s\": ", dir); 1662c393a42Smrg perror (""); 1672c393a42Smrg ret++; 1682c393a42Smrg break; 1692c393a42Smrg } 1702c393a42Smrg continue; 1712c393a42Smrg } 1722c393a42Smrg 1732c393a42Smrg if (!S_ISDIR (statb.st_mode)) 1742c393a42Smrg { 1752c393a42Smrg fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 1762c393a42Smrg continue; 1772c393a42Smrg } 1782c393a42Smrg 1792c393a42Smrg if (really_force) 1802c393a42Smrg FcDirCacheUnlink (dir, config); 1812c393a42Smrg 1822c393a42Smrg cache = NULL; 1832c393a42Smrg was_valid = FcFalse; 1842c393a42Smrg if (!force) { 1852c393a42Smrg cache = FcDirCacheLoad (dir, config, NULL); 1862c393a42Smrg if (cache) 1872c393a42Smrg was_valid = FcTrue; 1882c393a42Smrg } 1892c393a42Smrg 1902c393a42Smrg if (!cache) 1912c393a42Smrg { 1922c393a42Smrg cache = FcDirCacheRead (dir, FcTrue, config); 1932c393a42Smrg if (!cache) 1942c393a42Smrg { 1952c393a42Smrg fprintf (stderr, "%s: error scanning\n", dir); 1962c393a42Smrg ret++; 1972c393a42Smrg continue; 1982c393a42Smrg } 1992c393a42Smrg } 2002c393a42Smrg 2012c393a42Smrg if (was_valid) 2022c393a42Smrg { 2032c393a42Smrg if (verbose) 2042c393a42Smrg printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 2052c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2062c393a42Smrg } 2072c393a42Smrg else 2082c393a42Smrg { 2092c393a42Smrg if (verbose) 2102c393a42Smrg printf ("caching, new cache contents: %d fonts, %d dirs\n", 2112c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2122c393a42Smrg 2132c393a42Smrg if (!FcDirCacheValid (dir)) 2142c393a42Smrg { 2152c393a42Smrg fprintf (stderr, "%s: failed to write cache\n", dir); 2162c393a42Smrg (void) FcDirCacheUnlink (dir, config); 2172c393a42Smrg ret++; 2182c393a42Smrg } 2192c393a42Smrg } 2202c393a42Smrg 2212c393a42Smrg subdirs = FcStrSetCreate (); 2222c393a42Smrg if (!subdirs) 2232c393a42Smrg { 2242c393a42Smrg fprintf (stderr, "%s: Can't create subdir set\n", dir); 2252c393a42Smrg ret++; 2262c393a42Smrg FcDirCacheUnload (cache); 2272c393a42Smrg continue; 2282c393a42Smrg } 2292c393a42Smrg for (i = 0; i < FcCacheNumSubdir (cache); i++) 2302c393a42Smrg FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2312c393a42Smrg 2322c393a42Smrg FcDirCacheUnload (cache); 2332c393a42Smrg 2342c393a42Smrg sublist = FcStrListCreate (subdirs); 2352c393a42Smrg FcStrSetDestroy (subdirs); 2362c393a42Smrg if (!sublist) 2372c393a42Smrg { 2382c393a42Smrg fprintf (stderr, "%s: Can't create subdir list\n", dir); 2392c393a42Smrg ret++; 2402c393a42Smrg continue; 2412c393a42Smrg } 2422c393a42Smrg FcStrSetAdd (processed_dirs, dir); 2432c393a42Smrg ret += scanDirs (sublist, config, force, really_force, verbose); 2442c393a42Smrg } 2452c393a42Smrg FcStrListDone (list); 2462c393a42Smrg return ret; 2472c393a42Smrg} 2482c393a42Smrg 2492c393a42Smrgstatic FcBool 2502c393a42SmrgcleanCacheDirectory (FcConfig *config, FcChar8 *dir, FcBool verbose) 2512c393a42Smrg{ 2522c393a42Smrg DIR *d; 2532c393a42Smrg struct dirent *ent; 2542c393a42Smrg FcChar8 *dir_base; 2552c393a42Smrg FcBool ret = FcTrue; 2562c393a42Smrg FcBool remove; 2572c393a42Smrg FcCache *cache; 2582c393a42Smrg struct stat target_stat; 2592c393a42Smrg 2602c393a42Smrg dir_base = FcStrPlus (dir, (FcChar8 *) "/"); 2612c393a42Smrg if (!dir_base) 2622c393a42Smrg { 2632c393a42Smrg fprintf (stderr, "%s: out of memory\n", dir); 2642c393a42Smrg return FcFalse; 2652c393a42Smrg } 2662c393a42Smrg if (access ((char *) dir, W_OK) != 0) 2672c393a42Smrg { 2682c393a42Smrg if (verbose) 2692c393a42Smrg printf ("%s: not cleaning %s cache directory\n", dir, 2702c393a42Smrg access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent"); 2712c393a42Smrg FcStrFree (dir_base); 2722c393a42Smrg return FcTrue; 2732c393a42Smrg } 2742c393a42Smrg if (verbose) 2752c393a42Smrg printf ("%s: cleaning cache directory\n", dir); 2762c393a42Smrg d = opendir ((char *) dir); 2772c393a42Smrg if (!d) 2782c393a42Smrg { 2792c393a42Smrg perror ((char *) dir); 2802c393a42Smrg FcStrFree (dir_base); 2812c393a42Smrg return FcFalse; 2822c393a42Smrg } 2832c393a42Smrg while ((ent = readdir (d))) 2842c393a42Smrg { 2852c393a42Smrg FcChar8 *file_name; 2862c393a42Smrg const FcChar8 *target_dir; 2872c393a42Smrg 2882c393a42Smrg if (ent->d_name[0] == '.') 2892c393a42Smrg continue; 2902c393a42Smrg /* skip cache files for different architectures and */ 2912c393a42Smrg /* files which are not cache files at all */ 2922c393a42Smrg if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) || 2932c393a42Smrg strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX)) 2942c393a42Smrg continue; 2952c393a42Smrg 2962c393a42Smrg file_name = FcStrPlus (dir_base, (FcChar8 *) ent->d_name); 2972c393a42Smrg if (!file_name) 2982c393a42Smrg { 2992c393a42Smrg fprintf (stderr, "%s: allocation failure\n", dir); 3002c393a42Smrg ret = FcFalse; 3012c393a42Smrg break; 3022c393a42Smrg } 3032c393a42Smrg remove = FcFalse; 3042c393a42Smrg cache = FcDirCacheLoadFile (file_name, NULL); 3052c393a42Smrg if (!cache) 3062c393a42Smrg { 3072c393a42Smrg if (verbose) 3082c393a42Smrg printf ("%s: invalid cache file: %s\n", dir, ent->d_name); 3092c393a42Smrg remove = FcTrue; 3102c393a42Smrg } 3112c393a42Smrg else 3122c393a42Smrg { 3132c393a42Smrg target_dir = FcCacheDir (cache); 3142c393a42Smrg if (stat ((char *) target_dir, &target_stat) < 0) 3152c393a42Smrg { 3162c393a42Smrg if (verbose) 3172c393a42Smrg printf ("%s: %s: missing directory: %s \n", 3182c393a42Smrg dir, ent->d_name, target_dir); 3192c393a42Smrg remove = FcTrue; 3202c393a42Smrg } 3212c393a42Smrg } 3222c393a42Smrg if (remove) 3232c393a42Smrg { 3242c393a42Smrg if (unlink ((char *) file_name) < 0) 3252c393a42Smrg { 3262c393a42Smrg perror ((char *) file_name); 3272c393a42Smrg ret = FcFalse; 3282c393a42Smrg } 3292c393a42Smrg } 3302c393a42Smrg FcDirCacheUnload (cache); 3312c393a42Smrg FcStrFree (file_name); 3322c393a42Smrg } 3332c393a42Smrg 3342c393a42Smrg closedir (d); 3352c393a42Smrg FcStrFree (dir_base); 3362c393a42Smrg return ret; 3372c393a42Smrg} 3382c393a42Smrg 3392c393a42Smrgstatic FcBool 3402c393a42SmrgcleanCacheDirectories (FcConfig *config, FcBool verbose) 3412c393a42Smrg{ 3422c393a42Smrg FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 3432c393a42Smrg FcChar8 *cache_dir; 3442c393a42Smrg FcBool ret = FcTrue; 3452c393a42Smrg 3462c393a42Smrg if (!cache_dirs) 3472c393a42Smrg return FcFalse; 3482c393a42Smrg while ((cache_dir = FcStrListNext (cache_dirs))) 3492c393a42Smrg { 3502c393a42Smrg if (!cleanCacheDirectory (config, cache_dir, verbose)) 3512c393a42Smrg { 3522c393a42Smrg ret = FcFalse; 3532c393a42Smrg break; 3542c393a42Smrg } 3552c393a42Smrg } 3562c393a42Smrg FcStrListDone (cache_dirs); 3572c393a42Smrg return ret; 3582c393a42Smrg} 3592c393a42Smrg 3602c393a42Smrgint 3612c393a42Smrgmain (int argc, char **argv) 3622c393a42Smrg{ 3632c393a42Smrg FcStrSet *dirs; 3642c393a42Smrg FcStrList *list; 3652c393a42Smrg FcBool verbose = FcFalse; 3662c393a42Smrg FcBool force = FcFalse; 3672c393a42Smrg FcBool really_force = FcFalse; 3682c393a42Smrg FcBool systemOnly = FcFalse; 3692c393a42Smrg FcConfig *config; 3702c393a42Smrg int i; 3712c393a42Smrg int ret; 3722c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT 3732c393a42Smrg int c; 3742c393a42Smrg 3752c393a42Smrg#if HAVE_GETOPT_LONG 3762c393a42Smrg while ((c = getopt_long (argc, argv, "frsVv?", longopts, NULL)) != -1) 3772c393a42Smrg#else 3782c393a42Smrg while ((c = getopt (argc, argv, "frsVv?")) != -1) 3792c393a42Smrg#endif 3802c393a42Smrg { 3812c393a42Smrg switch (c) { 3822c393a42Smrg case 'r': 3832c393a42Smrg really_force = FcTrue; 3842c393a42Smrg /* fall through */ 3852c393a42Smrg case 'f': 3862c393a42Smrg force = FcTrue; 3872c393a42Smrg break; 3882c393a42Smrg case 's': 3892c393a42Smrg systemOnly = FcTrue; 3902c393a42Smrg break; 3912c393a42Smrg case 'V': 3922c393a42Smrg fprintf (stderr, "fontconfig version %d.%d.%d\n", 3932c393a42Smrg FC_MAJOR, FC_MINOR, FC_REVISION); 3942c393a42Smrg exit (0); 3952c393a42Smrg case 'v': 3962c393a42Smrg verbose = FcTrue; 3972c393a42Smrg break; 3982c393a42Smrg default: 3992c393a42Smrg usage (argv[0]); 4002c393a42Smrg } 4012c393a42Smrg } 4022c393a42Smrg i = optind; 4032c393a42Smrg#else 4042c393a42Smrg i = 1; 4052c393a42Smrg#endif 4062c393a42Smrg 4072c393a42Smrg if (systemOnly) 4082c393a42Smrg FcConfigEnableHome (FcFalse); 4092c393a42Smrg config = FcInitLoadConfig (); 4102c393a42Smrg if (!config) 4112c393a42Smrg { 4122c393a42Smrg fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 4132c393a42Smrg return 1; 4142c393a42Smrg } 4152c393a42Smrg FcConfigSetCurrent (config); 4162c393a42Smrg 4172c393a42Smrg if (argv[i]) 4182c393a42Smrg { 4192c393a42Smrg dirs = FcStrSetCreate (); 4202c393a42Smrg if (!dirs) 4212c393a42Smrg { 4222c393a42Smrg fprintf (stderr, "%s: Can't create list of directories\n", 4232c393a42Smrg argv[0]); 4242c393a42Smrg return 1; 4252c393a42Smrg } 4262c393a42Smrg while (argv[i]) 4272c393a42Smrg { 4282c393a42Smrg if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 4292c393a42Smrg { 4302c393a42Smrg fprintf (stderr, "%s: Can't add directory\n", argv[0]); 4312c393a42Smrg return 1; 4322c393a42Smrg } 4332c393a42Smrg i++; 4342c393a42Smrg } 4352c393a42Smrg list = FcStrListCreate (dirs); 4362c393a42Smrg FcStrSetDestroy (dirs); 4372c393a42Smrg } 4382c393a42Smrg else 4392c393a42Smrg list = FcConfigGetConfigDirs (config); 4402c393a42Smrg 4412c393a42Smrg if ((processed_dirs = FcStrSetCreate()) == NULL) { 4422c393a42Smrg fprintf(stderr, "Cannot malloc\n"); 4432c393a42Smrg return 1; 4442c393a42Smrg } 4452c393a42Smrg 4462c393a42Smrg ret = scanDirs (list, config, force, really_force, verbose); 4472c393a42Smrg 4482c393a42Smrg FcStrSetDestroy (processed_dirs); 4492c393a42Smrg 4502c393a42Smrg cleanCacheDirectories (config, verbose); 4512c393a42Smrg 4522c393a42Smrg /* 4532c393a42Smrg * Now we need to sleep a second (or two, to be extra sure), to make 4542c393a42Smrg * sure that timestamps for changes after this run of fc-cache are later 4552c393a42Smrg * then any timestamps we wrote. We don't use gettimeofday() because 4562c393a42Smrg * sleep(3) can't be interrupted by a signal here -- this isn't in the 4572c393a42Smrg * library, and there aren't any signals flying around here. 4582c393a42Smrg */ 4592c393a42Smrg FcConfigDestroy (config); 4602c393a42Smrg FcFini (); 4612c393a42Smrg sleep (2); 4622c393a42Smrg if (verbose) 4632c393a42Smrg printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 4642c393a42Smrg return ret; 4652c393a42Smrg} 466