fc-cache.c revision 18bd4a06
11.207Smycroft/* 21.147Simp * fontconfig/fc-cache/fc-cache.c 31.1Schristos * 41.14Schristos * Copyright © 2002 Keith Packard 51.207Smycroft * 61.1Schristos * Permission to use, copy, modify, distribute, and sell this software and its 71.1Schristos * documentation for any purpose is hereby granted without fee, provided that 81.14Schristos * the above copyright notice appear in all copies and that both that 91.14Schristos * copyright notice and this permission notice appear in supporting 101.14Schristos * documentation, and that the name of the author(s) not be used in 111.1Schristos * advertising or publicity pertaining to distribution of the software without 121.1Schristos * specific, written prior permission. The authors make no 131.1Schristos * representations about the suitability of this software for any purpose. It 141.1Schristos * is provided "as is" without express or implied warranty. 151.1Schristos * 161.1Schristos * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 171.1Schristos * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 181.1Schristos * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 191.1Schristos * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 201.1Schristos * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 211.14Schristos * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 221.14Schristos * PERFORMANCE OF THIS SOFTWARE. 231.14Schristos */ 241.14Schristos 251.14Schristos#ifdef HAVE_CONFIG_H 261.1Schristos#include <config.h> 271.14Schristos#else 281.14Schristos#ifdef linux 291.14Schristos#define HAVE_GETOPT_LONG 1 301.14Schristos#endif 311.14Schristos#define HAVE_GETOPT 1 321.14Schristos#endif 331.14Schristos 341.14Schristos#include <fontconfig/fontconfig.h> 351.14Schristos#include <stdio.h> 361.14Schristos#include <stdlib.h> 371.14Schristos#include <unistd.h> 381.1Schristos#include <sys/types.h> 391.1Schristos#include <sys/stat.h> 401.1Schristos#include <errno.h> 411.140Simp#include <fcntl.h> 421.1Schristos#include <dirent.h> 431.1Schristos#include <string.h> 441.31Snathanw 451.176Smason#if defined (_WIN32) 461.83Smycroft#define STRICT 471.15Skenh#include <windows.h> 481.40Shwr#define sleep(x) Sleep((x) * 1000) 491.65Sjoda#undef STRICT 501.1Schristos#endif 511.183Sgmcgarry 521.205Smycroft#ifndef O_BINARY 531.1Schristos#define O_BINARY 0 541.1Schristos#endif 551.1Schristos 561.1Schristos#ifndef HAVE_GETOPT 571.65Sjoda#define HAVE_GETOPT 0 581.11Sthorpej#endif 591.147Simp#ifndef HAVE_GETOPT_LONG 601.128Sichiro#define HAVE_GETOPT_LONG 0 611.1Schristos#endif 621.140Simp 631.147Simp#if HAVE_GETOPT_LONG 641.1Schristos#undef _GNU_SOURCE 651.1Schristos#define _GNU_SOURCE 661.147Simp#include <getopt.h> 671.65Sjodaconst struct option longopts[] = { 681.175Srjs {"error-on-no-fonts", 0, 0, 'E'}, 691.140Simp {"force", 0, 0, 'f'}, 701.171Sjonathan {"quick", 0, 0, 'q'}, 711.1Schristos {"really-force", 0, 0, 'r'}, 721.147Simp {"sysroot", required_argument, 0, 'y'}, 731.4Sthorpej {"system-only", 0, 0, 's'}, 741.147Simp {"version", 0, 0, 'V'}, 751.39Ssommerfe {"verbose", 0, 0, 'v'}, 761.66Sjlam {"help", 0, 0, 'h'}, 771.158Sjoda {NULL,0,0,0}, 781.135Spooka}; 791.65Sjoda#else 801.111Simp#if HAVE_GETOPT 811.1Schristosextern char *optarg; 821.67Schoppsextern int optind, opterr, optopt; 831.98Simp#endif 841.55Ssommerfe#endif 851.92Sonoe 861.99Smsaitohstatic void 871.85Smycroftusage (char *program, int error) 881.97Sonoe{ 891.189Sichiro FILE *file = error ? stderr : stdout; 901.196Smycroft#if HAVE_GETOPT_LONG 911.136Sichiro fprintf (file, "usage: %s [-EfqrsvVh] [--quick] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 921.125Sichiro program); 931.159Saymeric#else 941.122Sichiro fprintf (file, "usage: %s [-EfqrsvVh] [-y SYSROOT] [dirs]\n", 951.201Smycroft program); 961.147Simp#endif 971.147Simp fprintf (file, "Build font information caches in [dirs]\n" 981.172Smartin "(all directories in font configuration by default).\n"); 991.38Smjl fprintf (file, "\n"); 1001.72Ssoren#if HAVE_GETOPT_LONG 1011.147Simp fprintf (file, " -E, --error-on-no-fonts raise an error if no fonts in a directory\n"); 1021.147Simp fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 1031.159Saymeric fprintf (file, " -q, --quick don't sleep before exiting\n"); 1041.25Sthorpej fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 1051.78Senami fprintf (file, " -s, --system-only scan system-wide directories only\n"); 1061.43Stron fprintf (file, " -y, --sysroot=SYSROOT prepend SYSROOT to all paths for scanning\n"); 1071.99Smsaitoh fprintf (file, " -v, --verbose display status information while busy\n"); 1081.110Snonaka fprintf (file, " -V, --version display font config version and exit\n"); 1091.22Sthorpej fprintf (file, " -h, --help display this help and exit\n"); 1101.43Stron#else 1111.15Skenh fprintf (file, " -E (error-on-no-fonts)\n"); 1121.89Sis fprintf (file, " raise an error if no fonts in a directory\n"); 1131.195Smycroft fprintf (file, " -f (force) scan directories with apparently valid caches\n"); 1141.108Sichiro fprintf (file, " -q (quick) don't sleep before exiting\n"); 1151.191Sitohy fprintf (file, " -r, (really force) erase all existing caches, then rescan\n"); 1161.1Schristos fprintf (file, " -s (system) scan system-wide directories only\n"); 1171.1Schristos fprintf (file, " -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n"); 1181.140Simp fprintf (file, " -v (verbose) display status information while busy\n"); 1191.140Simp fprintf (file, " -V (version) display font config version and exit\n"); 1201.1Schristos fprintf (file, " -h (help) display this help and exit\n"); 1211.58Ssoren#endif 1221.1Schristos exit (error); 1231.94Sjoda} 1241.140Simp 1251.140Simpstatic FcStrSet *processed_dirs; 1261.140Simp 1271.140Simpstatic int 1281.1SchristosscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed) 1291.1Schristos{ 1301.13Sthorpej int ret = 0; 1311.147Simp const FcChar8 *dir; 1321.132Sitojun FcStrSet *subdirs; 1331.172Smartin FcStrList *sublist; 1341.172Smartin FcCache *cache; 1351.172Smartin struct stat statb; 1361.24Sthorpej FcBool was_valid, was_processed = FcFalse; 1371.140Simp int i; 1381.140Simp const FcChar8 *sysroot = FcConfigGetSysRoot (config); 1391.140Simp 1401.140Simp /* 1411.140Simp * Now scan all of the directories into separate databases 1421.140Simp * and write out the results 1431.140Simp */ 1441.140Simp while ((dir = FcStrListNext (list))) 1451.140Simp { 1461.140Simp if (verbose) 1471.140Simp { 1481.189Sichiro if (sysroot) 1491.189Sichiro printf ("[%s]", sysroot); 1501.189Sichiro printf ("%s: ", dir); 1511.189Sichiro fflush (stdout); 1521.140Simp } 1531.147Simp 1541.147Simp if (FcStrSetMember (processed_dirs, dir)) 1551.147Simp { 1561.196Smycroft if (verbose) 1571.196Smycroft printf ("skipping, looped directory detected\n"); 1581.196Smycroft continue; 1591.201Smycroft } 1601.201Smycroft 1611.201Smycroft if (stat ((char *) dir, &statb) == -1) 1621.140Simp { 1631.140Simp switch (errno) { 1641.140Simp case ENOENT: 1651.140Simp case ENOTDIR: 1661.169Smycroft if (verbose) 1671.140Simp printf ("skipping, no such directory\n"); 1681.147Simp break; 1691.147Simp default: 1701.147Simp fprintf (stderr, "\"%s\": ", dir); 1711.140Simp perror (""); 1721.140Simp ret++; 1731.199Senami break; 1741.198Senami } 1751.198Senami continue; 1761.150Senami } 1771.140Simp 1781.90Sgmcgarry if (!S_ISDIR (statb.st_mode)) 1791.140Simp { 1801.90Sgmcgarry fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 1811.90Sgmcgarry continue; 1821.24Sthorpej } 1831.147Simp was_processed = FcTrue; 1841.24Sthorpej 1851.37Smjl if (really_force) 1861.140Simp FcDirCacheUnlink (dir, config); 1871.140Simp 1881.140Simp cache = NULL; 1891.1Schristos was_valid = FcFalse; 1901.1Schristos if (!force) { 1911.16Sdbj cache = FcDirCacheLoad (dir, config, NULL); 1921.16Sdbj if (cache) 1931.1Schristos was_valid = FcTrue; 1941.15Skenh } 1951.15Skenh 1961.120Sthorpej if (!cache) 1971.120Sthorpej { 1981.120Sthorpej (*changed)++; 1991.101Shubertf cache = FcDirCacheRead (dir, FcTrue, config); 2001.147Simp if (!cache) 2011.147Simp { 2021.157Saymeric fprintf (stderr, "%s: error scanning\n", dir); 2031.157Saymeric ret++; 2041.157Saymeric continue; 2051.147Simp } 2061.101Shubertf } 2071.101Shubertf 2081.101Shubertf if (was_valid) 2091.144Sichiro { 2101.173Smartin if (verbose) 2111.31Snathanw printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 2121.140Simp FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2131.140Simp } 2141.158Sjoda else 2151.158Sjoda { 2161.158Sjoda if (verbose) 2171.140Simp printf ("caching, new cache contents: %d fonts, %d dirs\n", 2181.140Simp FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2191.140Simp 2201.140Simp if (!FcDirCacheValid (dir)) 2211.31Snathanw { 2221.32Snathanw fprintf (stderr, "%s: failed to write cache\n", dir); 2231.140Simp (void) FcDirCacheUnlink (dir, config); 2241.34Sthorpej ret++; 2251.191Sitohy } 2261.1Schristos } 2271.1Schristos 2281.103Sdrochner subdirs = FcStrSetCreate (); 2291.93Ssoren if (!subdirs) 2301.93Ssoren { 2311.93Ssoren fprintf (stderr, "%s: Can't create subdir set\n", dir); 2321.93Ssoren ret++; 2331.149Syamt FcDirCacheUnload (cache); 2341.93Ssoren continue; 2351.1Schristos } 2361.191Sitohy for (i = 0; i < FcCacheNumSubdir (cache); i++) 2371.155Sichiro FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2381.59Ssoren 2391.1Schristos FcDirCacheUnload (cache); 2401.90Sgmcgarry 2411.140Simp sublist = FcStrListCreate (subdirs); 2421.90Sgmcgarry FcStrSetDestroy (subdirs); 2431.90Sgmcgarry if (!sublist) 2441.1Schristos { 2451.56Senami fprintf (stderr, "%s: Can't create subdir list\n", dir); 2461.122Sichiro ret++; 2471.192Ssekiya continue; 2481.92Sonoe } 2491.140Simp FcStrSetAdd (processed_dirs, dir); 2501.140Simp ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed); 2511.140Simp FcStrListDone (sublist); 2521.140Simp } 2531.140Simp if (error_on_no_fonts && !was_processed) 2541.72Ssoren ret++; 2551.72Ssoren return ret; 2561.102Ssoren} 2571.1Schristos 2581.1Schristosstatic FcBool 2591.140SimpcleanCacheDirectories (FcConfig *config, FcBool verbose) 2601.73Sthorpej{ 2611.21Sitohy FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 2621.1Schristos FcChar8 *cache_dir; 2631.147Simp FcBool ret = FcTrue; 2641.164Sichiro 2651.167Sonoe if (!cache_dirs) 2661.140Simp return FcFalse; 2671.204Smycroft while ((cache_dir = FcStrListNext (cache_dirs))) 2681.204Smycroft { 2691.204Smycroft if (!FcDirCacheClean (cache_dir, verbose)) 2701.204Smycroft { 2711.140Simp ret = FcFalse; 2721.140Simp break; 2731.140Simp } 2741.1Schristos } 2751.1Schristos FcStrListDone (cache_dirs); 2761.203Smycroft return ret; 2771.1Schristos} 2781.58Ssoren 2791.18Sthorpejint 2801.203Smycroftmain (int argc, char **argv) 2811.1Schristos{ 2821.1Schristos FcStrSet *dirs; 2831.78Senami FcStrList *list; 2841.191Sitohy FcBool verbose = FcFalse; 2851.128Sichiro FcBool quick = FcFalse; 2861.140Simp FcBool force = FcFalse; 2871.140Simp FcBool really_force = FcFalse; 2881.140Simp FcBool systemOnly = FcFalse; 2891.147Simp FcBool error_on_no_fonts = FcFalse; 2901.140Simp FcConfig *config; 2911.140Simp FcChar8 *sysroot = NULL; 2921.140Simp int i; 2931.147Simp int changed; 2941.147Simp int ret; 2951.147Simp#if HAVE_GETOPT_LONG || HAVE_GETOPT 2961.140Simp int c; 2971.147Simp 2981.147Simp#if HAVE_GETOPT_LONG 2991.147Simp while ((c = getopt_long (argc, argv, "Efqrsy:Vvh", longopts, NULL)) != -1) 3001.147Simp#else 3011.175Srjs while ((c = getopt (argc, argv, "Efqrsy:Vvh")) != -1) 3021.147Simp#endif 3031.166Schris { 3041.176Smason switch (c) { 3051.140Simp case 'E': 3061.128Sichiro error_on_no_fonts = FcTrue; 3071.191Sitohy break; 3081.85Smycroft case 'r': 3091.195Smycroft really_force = FcTrue; 3101.195Smycroft /* fall through */ 3111.195Smycroft case 'f': 3121.85Smycroft force = FcTrue; 3131.85Smycroft break; 3141.91Ssoren case 'q': 3151.91Ssoren quick = FcTrue; 3161.91Ssoren break; 3171.175Srjs case 's': 3181.175Srjs systemOnly = FcTrue; 3191.175Srjs break; 3201.83Smycroft case 'y': 3211.83Smycroft sysroot = FcStrCopy ((const FcChar8 *)optarg); 3221.83Smycroft break; 3231.88Sjoda case 'V': 3241.141Schristos fprintf (stderr, "fontconfig version %d.%d.%d\n", 3251.147Simp FC_MAJOR, FC_MINOR, FC_REVISION); 3261.147Simp exit (0); 3271.147Simp case 'v': 3281.147Simp verbose = FcTrue; 3291.147Simp break; 3301.135Spooka case 'h': 3311.135Spooka usage (argv[0], 0); 3321.135Spooka default: 3331.78Senami usage (argv[0], 1); 3341.147Simp } 3351.185Sichiro } 3361.147Simp i = optind; 3371.147Simp#else 3381.140Simp i = 1; 3391.140Simp#endif 3401.140Simp 3411.140Simp if (systemOnly) 3421.140Simp FcConfigEnableHome (FcFalse); 3431.140Simp if (sysroot) 3441.140Simp { 3451.140Simp FcConfigSetSysRoot (NULL, sysroot); 3461.4Sthorpej FcStrFree (sysroot); 3471.15Skenh config = FcConfigGetCurrent(); 3481.15Skenh } 3491.183Sgmcgarry else 3501.183Sgmcgarry { 3511.183Sgmcgarry config = FcInitLoadConfig (); 3521.15Skenh } 3531.4Sthorpej if (!config) 3541.4Sthorpej { 3551.39Ssommerfe fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 3561.39Ssommerfe return 1; 3571.1Schristos } 3581.140Simp FcConfigSetCurrent (config); 3591.140Simp 3601.140Simp if (argv[i]) 3611.140Simp { 3621.1Schristos dirs = FcStrSetCreate (); 3631.127Sjhawk if (!dirs) 3641.156Spooka { 3651.1Schristos fprintf (stderr, "%s: Can't create list of directories\n", 3661.19Sthorpej argv[0]); 3671.140Simp return 1; 3681.100Stoddpw } 3691.184Smartin while (argv[i]) 3701.140Simp { 3711.140Simp if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 3721.140Simp { 3731.1Schristos fprintf (stderr, "%s: Can't add directory\n", argv[0]); 3741.1Schristos return 1; 3751.140Simp } 3761.1Schristos i++; 3771.140Simp } 3781.163Smartin list = FcStrListCreate (dirs); 3791.1Schristos FcStrSetDestroy (dirs); 3801.140Simp } 3811.140Simp else 3821.140Simp list = FcConfigGetConfigDirs (config); 3831.140Simp 3841.140Simp if ((processed_dirs = FcStrSetCreate()) == NULL) { 3851.140Simp fprintf(stderr, "Cannot malloc\n"); 3861.20Sthorpej return 1; 3871.90Sgmcgarry } 3881.90Sgmcgarry 3891.147Simp changed = 0; 3901.90Sgmcgarry ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed); 3911.145Schristos FcStrListDone (list); 3921.140Simp 3931.140Simp /* 3941.114Sthorpej * Try to create CACHEDIR.TAG anyway. 3951.90Sgmcgarry * This expects the fontconfig cache directory already exists. 3961.140Simp * If it doesn't, it won't be simply created. 3971.90Sgmcgarry */ 3981.96Sgmcgarry FcCacheCreateTagFile (config); 3991.96Sgmcgarry 4001.134Schristos FcStrSetDestroy (processed_dirs); 4011.140Simp 4021.140Simp cleanCacheDirectories (config, verbose); 4031.140Simp 4041.195Smycroft FcConfigDestroy (config); 4051.140Simp FcFini (); 4061.140Simp /* 4071.140Simp * Now we need to sleep a second (or two, to be extra sure), to make 4081.140Simp * sure that timestamps for changes after this run of fc-cache are later 4091.191Sitohy * then any timestamps we wrote. We don't use gettimeofday() because 4101.174Smjl * sleep(3) can't be interrupted by a signal here -- this isn't in the 4111.140Simp * library, and there aren't any signals flying around here. 4121.62Sdanw */ 4131.64Saugustss /* the resolution of mtime on FAT is 2 seconds */ 4141.147Simp if (!quick && changed) 4151.196Smycroft sleep (2); 4161.76Senami if (verbose) 4171.95Sonoe printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 4181.160Sis return ret; 4191.140Simp} 4201.147Simp