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> 377872e0a1Smrg#ifdef HAVE_UNISTD_H 382c393a42Smrg#include <unistd.h> 397872e0a1Smrg#endif 402c393a42Smrg#include <sys/types.h> 412c393a42Smrg#include <sys/stat.h> 422c393a42Smrg#include <errno.h> 432c393a42Smrg#include <fcntl.h> 447872e0a1Smrg#ifdef HAVE_DIRENT_H 452c393a42Smrg#include <dirent.h> 467872e0a1Smrg#endif 472c393a42Smrg#include <string.h> 481887081fSmrg#include <locale.h> 492c393a42Smrg 502c393a42Smrg#if defined (_WIN32) 512c393a42Smrg#define STRICT 522c393a42Smrg#include <windows.h> 532c393a42Smrg#define sleep(x) Sleep((x) * 1000) 542c393a42Smrg#undef STRICT 552c393a42Smrg#endif 562c393a42Smrg 571887081fSmrg#ifdef ENABLE_NLS 581887081fSmrg#include <libintl.h> 591887081fSmrg#define _(x) (dgettext(GETTEXT_PACKAGE, x)) 601887081fSmrg#else 611887081fSmrg#define dgettext(d, s) (s) 621887081fSmrg#define _(x) (x) 631887081fSmrg#endif 641887081fSmrg 652c393a42Smrg#ifndef O_BINARY 662c393a42Smrg#define O_BINARY 0 672c393a42Smrg#endif 682c393a42Smrg 697872e0a1Smrg#ifndef S_ISDIR 707872e0a1Smrg#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) 717872e0a1Smrg#endif 727872e0a1Smrg 732c393a42Smrg#ifndef HAVE_GETOPT 742c393a42Smrg#define HAVE_GETOPT 0 752c393a42Smrg#endif 762c393a42Smrg#ifndef HAVE_GETOPT_LONG 772c393a42Smrg#define HAVE_GETOPT_LONG 0 782c393a42Smrg#endif 792c393a42Smrg 802c393a42Smrg#if HAVE_GETOPT_LONG 812c393a42Smrg#undef _GNU_SOURCE 822c393a42Smrg#define _GNU_SOURCE 832c393a42Smrg#include <getopt.h> 842c393a42Smrgconst struct option longopts[] = { 8518bd4a06Smrg {"error-on-no-fonts", 0, 0, 'E'}, 862c393a42Smrg {"force", 0, 0, 'f'}, 877f785a56Sjmcneill {"quick", 0, 0, 'q'}, 882c393a42Smrg {"really-force", 0, 0, 'r'}, 89b2a52337Smrg {"sysroot", required_argument, 0, 'y'}, 902c393a42Smrg {"system-only", 0, 0, 's'}, 912c393a42Smrg {"version", 0, 0, 'V'}, 922c393a42Smrg {"verbose", 0, 0, 'v'}, 93a6844aabSmrg {"help", 0, 0, 'h'}, 942c393a42Smrg {NULL,0,0,0}, 952c393a42Smrg}; 962c393a42Smrg#else 972c393a42Smrg#if HAVE_GETOPT 982c393a42Smrgextern char *optarg; 992c393a42Smrgextern int optind, opterr, optopt; 1002c393a42Smrg#endif 1012c393a42Smrg#endif 1022c393a42Smrg 1032c393a42Smrgstatic void 104a6844aabSmrgusage (char *program, int error) 1052c393a42Smrg{ 106a6844aabSmrg FILE *file = error ? stderr : stdout; 107639750fcSmrg#if HAVE_GETOPT_LONG 1081887081fSmrg fprintf (file, _("usage: %s [-EfqrsvVh] [--quick] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n"), 1092c393a42Smrg program); 1102c393a42Smrg#else 1111887081fSmrg fprintf (file, _("usage: %s [-EfqrsvVh] [-y SYSROOT] [dirs]\n"), 1122c393a42Smrg program); 1132c393a42Smrg#endif 1141887081fSmrg fprintf (file, _("Build font information caches in [dirs]\n" 1151887081fSmrg "(all directories in font configuration by default).\n")); 116a6844aabSmrg fprintf (file, "\n"); 1172c393a42Smrg#if HAVE_GETOPT_LONG 1181887081fSmrg fprintf (file, _(" -E, --error-on-no-fonts raise an error if no fonts in a directory\n")); 1191887081fSmrg fprintf (file, _(" -f, --force scan directories with apparently valid caches\n")); 1201887081fSmrg fprintf (file, _(" -q, --quick don't sleep before exiting\n")); 1211887081fSmrg fprintf (file, _(" -r, --really-force erase all existing caches, then rescan\n")); 1221887081fSmrg fprintf (file, _(" -s, --system-only scan system-wide directories only\n")); 1231887081fSmrg fprintf (file, _(" -y, --sysroot=SYSROOT prepend SYSROOT to all paths for scanning\n")); 1241887081fSmrg fprintf (file, _(" -v, --verbose display status information while busy\n")); 1251887081fSmrg fprintf (file, _(" -V, --version display font config version and exit\n")); 1261887081fSmrg fprintf (file, _(" -h, --help display this help and exit\n")); 1272c393a42Smrg#else 1281887081fSmrg fprintf (file, _(" -E (error-on-no-fonts)\n")); 1291887081fSmrg fprintf (file, _(" raise an error if no fonts in a directory\n")); 1301887081fSmrg fprintf (file, _(" -f (force) scan directories with apparently valid caches\n")); 1311887081fSmrg fprintf (file, _(" -q (quick) don't sleep before exiting\n")); 1321887081fSmrg fprintf (file, _(" -r, (really force) erase all existing caches, then rescan\n")); 1331887081fSmrg fprintf (file, _(" -s (system) scan system-wide directories only\n")); 1341887081fSmrg fprintf (file, _(" -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n")); 1351887081fSmrg fprintf (file, _(" -v (verbose) display status information while busy\n")); 1361887081fSmrg fprintf (file, _(" -V (version) display font config version and exit\n")); 1371887081fSmrg fprintf (file, _(" -h (help) display this help and exit\n")); 1382c393a42Smrg#endif 139a6844aabSmrg exit (error); 1402c393a42Smrg} 1412c393a42Smrg 1422c393a42Smrgstatic FcStrSet *processed_dirs; 1432c393a42Smrg 1442c393a42Smrgstatic int 14518bd4a06SmrgscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed) 1462c393a42Smrg{ 1472c393a42Smrg int ret = 0; 1482c393a42Smrg const FcChar8 *dir; 1492c393a42Smrg FcStrSet *subdirs; 1502c393a42Smrg FcStrList *sublist; 1512c393a42Smrg FcCache *cache; 1522c393a42Smrg struct stat statb; 15318bd4a06Smrg FcBool was_valid, was_processed = FcFalse; 1542c393a42Smrg int i; 15518bd4a06Smrg const FcChar8 *sysroot = FcConfigGetSysRoot (config); 15618bd4a06Smrg 1572c393a42Smrg /* 1582c393a42Smrg * Now scan all of the directories into separate databases 1592c393a42Smrg * and write out the results 1602c393a42Smrg */ 1612c393a42Smrg while ((dir = FcStrListNext (list))) 1622c393a42Smrg { 1632c393a42Smrg if (verbose) 1642c393a42Smrg { 16518bd4a06Smrg if (sysroot) 16618bd4a06Smrg printf ("[%s]", sysroot); 16718bd4a06Smrg printf ("%s: ", dir); 1682c393a42Smrg fflush (stdout); 1692c393a42Smrg } 1707872e0a1Smrg 17118bd4a06Smrg if (FcStrSetMember (processed_dirs, dir)) 1722c393a42Smrg { 1732c393a42Smrg if (verbose) 1741887081fSmrg printf (_("skipping, looped directory detected\n")); 1752c393a42Smrg continue; 1762c393a42Smrg } 1772c393a42Smrg 1787872e0a1Smrg FcChar8 *rooted_dir = NULL; 1797872e0a1Smrg if (sysroot) 1807872e0a1Smrg { 1817872e0a1Smrg rooted_dir = FcStrPlus(sysroot, dir); 1827872e0a1Smrg } 1837872e0a1Smrg else { 1847872e0a1Smrg rooted_dir = FcStrCopy(dir); 1857872e0a1Smrg } 1867872e0a1Smrg 1877872e0a1Smrg if (stat ((char *) rooted_dir, &statb) == -1) 1882c393a42Smrg { 1892c393a42Smrg switch (errno) { 1902c393a42Smrg case ENOENT: 1912c393a42Smrg case ENOTDIR: 1922c393a42Smrg if (verbose) 1931887081fSmrg printf (_("skipping, no such directory\n")); 1942c393a42Smrg break; 1952c393a42Smrg default: 1962c393a42Smrg fprintf (stderr, "\"%s\": ", dir); 1972c393a42Smrg perror (""); 1982c393a42Smrg ret++; 1992c393a42Smrg break; 2002c393a42Smrg } 2017872e0a1Smrg FcStrFree (rooted_dir); 2027872e0a1Smrg rooted_dir = NULL; 2032c393a42Smrg continue; 2042c393a42Smrg } 2052c393a42Smrg 2067872e0a1Smrg FcStrFree(rooted_dir); 2077872e0a1Smrg rooted_dir = NULL; 2087872e0a1Smrg 2092c393a42Smrg if (!S_ISDIR (statb.st_mode)) 2102c393a42Smrg { 2111887081fSmrg fprintf (stderr, _("\"%s\": not a directory, skipping\n"), dir); 2122c393a42Smrg continue; 2132c393a42Smrg } 21418bd4a06Smrg was_processed = FcTrue; 2152c393a42Smrg 2162c393a42Smrg if (really_force) 2171887081fSmrg { 2182c393a42Smrg FcDirCacheUnlink (dir, config); 2191887081fSmrg } 2202c393a42Smrg 2212c393a42Smrg cache = NULL; 2222c393a42Smrg was_valid = FcFalse; 2232c393a42Smrg if (!force) { 2242c393a42Smrg cache = FcDirCacheLoad (dir, config, NULL); 2252c393a42Smrg if (cache) 2262c393a42Smrg was_valid = FcTrue; 2272c393a42Smrg } 2282c393a42Smrg 2292c393a42Smrg if (!cache) 2302c393a42Smrg { 23118bd4a06Smrg (*changed)++; 23218bd4a06Smrg cache = FcDirCacheRead (dir, FcTrue, config); 2332c393a42Smrg if (!cache) 2342c393a42Smrg { 2351887081fSmrg fprintf (stderr, _("\"%s\": scanning error\n"), dir); 2362c393a42Smrg ret++; 2372c393a42Smrg continue; 2382c393a42Smrg } 2392c393a42Smrg } 2402c393a42Smrg 2412c393a42Smrg if (was_valid) 2422c393a42Smrg { 2432c393a42Smrg if (verbose) 2441887081fSmrg printf (_("skipping, existing cache is valid: %d fonts, %d dirs\n"), 2452c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2462c393a42Smrg } 2472c393a42Smrg else 2482c393a42Smrg { 2492c393a42Smrg if (verbose) 2501887081fSmrg printf (_("caching, new cache contents: %d fonts, %d dirs\n"), 2512c393a42Smrg FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 2522c393a42Smrg 2532c393a42Smrg if (!FcDirCacheValid (dir)) 2542c393a42Smrg { 2551887081fSmrg fprintf (stderr, _("%s: failed to write cache\n"), dir); 2562c393a42Smrg (void) FcDirCacheUnlink (dir, config); 2572c393a42Smrg ret++; 2582c393a42Smrg } 2592c393a42Smrg } 260d91dd368Smrg 26118bd4a06Smrg subdirs = FcStrSetCreate (); 26218bd4a06Smrg if (!subdirs) 2632c393a42Smrg { 2641887081fSmrg fprintf (stderr, _("%s: Can't create subdir set\n"), dir); 26518bd4a06Smrg ret++; 266d91dd368Smrg FcDirCacheUnload (cache); 26718bd4a06Smrg continue; 26818bd4a06Smrg } 26918bd4a06Smrg for (i = 0; i < FcCacheNumSubdir (cache); i++) 27018bd4a06Smrg FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 2712c393a42Smrg 27218bd4a06Smrg FcDirCacheUnload (cache); 27318bd4a06Smrg 27418bd4a06Smrg sublist = FcStrListCreate (subdirs); 27518bd4a06Smrg FcStrSetDestroy (subdirs); 27618bd4a06Smrg if (!sublist) 27718bd4a06Smrg { 2781887081fSmrg fprintf (stderr, _("%s: Can't create subdir list\n"), dir); 27918bd4a06Smrg ret++; 28018bd4a06Smrg continue; 2812c393a42Smrg } 28218bd4a06Smrg FcStrSetAdd (processed_dirs, dir); 28318bd4a06Smrg ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed); 28418bd4a06Smrg FcStrListDone (sublist); 2852c393a42Smrg } 2867872e0a1Smrg 28718bd4a06Smrg if (error_on_no_fonts && !was_processed) 28818bd4a06Smrg ret++; 2892c393a42Smrg return ret; 2902c393a42Smrg} 2912c393a42Smrg 2922c393a42Smrgstatic FcBool 2932c393a42SmrgcleanCacheDirectories (FcConfig *config, FcBool verbose) 2942c393a42Smrg{ 2952c393a42Smrg FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 2962c393a42Smrg FcChar8 *cache_dir; 2972c393a42Smrg FcBool ret = FcTrue; 2982c393a42Smrg 2992c393a42Smrg if (!cache_dirs) 3002c393a42Smrg return FcFalse; 3012c393a42Smrg while ((cache_dir = FcStrListNext (cache_dirs))) 3022c393a42Smrg { 303898dab68Smrg if (!FcDirCacheClean (cache_dir, verbose)) 3042c393a42Smrg { 3052c393a42Smrg ret = FcFalse; 3062c393a42Smrg break; 3072c393a42Smrg } 3082c393a42Smrg } 3092c393a42Smrg FcStrListDone (cache_dirs); 3102c393a42Smrg return ret; 3112c393a42Smrg} 3122c393a42Smrg 3132c393a42Smrgint 3142c393a42Smrgmain (int argc, char **argv) 3152c393a42Smrg{ 31618bd4a06Smrg FcStrSet *dirs; 3172c393a42Smrg FcStrList *list; 3182c393a42Smrg FcBool verbose = FcFalse; 3197f785a56Sjmcneill FcBool quick = FcFalse; 3202c393a42Smrg FcBool force = FcFalse; 3212c393a42Smrg FcBool really_force = FcFalse; 3222c393a42Smrg FcBool systemOnly = FcFalse; 32318bd4a06Smrg FcBool error_on_no_fonts = FcFalse; 3242c393a42Smrg FcConfig *config; 3255e61939bSmrg FcChar8 *sysroot = NULL; 3262c393a42Smrg int i; 327898dab68Smrg int changed; 3282c393a42Smrg int ret; 3292c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT 3302c393a42Smrg int c; 3312c393a42Smrg 3321887081fSmrg setlocale (LC_ALL, ""); 3332c393a42Smrg#if HAVE_GETOPT_LONG 33418bd4a06Smrg while ((c = getopt_long (argc, argv, "Efqrsy:Vvh", longopts, NULL)) != -1) 3352c393a42Smrg#else 33618bd4a06Smrg while ((c = getopt (argc, argv, "Efqrsy:Vvh")) != -1) 3372c393a42Smrg#endif 3382c393a42Smrg { 3392c393a42Smrg switch (c) { 34018bd4a06Smrg case 'E': 34118bd4a06Smrg error_on_no_fonts = FcTrue; 34218bd4a06Smrg break; 3432c393a42Smrg case 'r': 3442c393a42Smrg really_force = FcTrue; 3452c393a42Smrg /* fall through */ 3462c393a42Smrg case 'f': 3472c393a42Smrg force = FcTrue; 3482c393a42Smrg break; 3497f785a56Sjmcneill case 'q': 3507f785a56Sjmcneill quick = FcTrue; 3517f785a56Sjmcneill break; 3522c393a42Smrg case 's': 3532c393a42Smrg systemOnly = FcTrue; 3542c393a42Smrg break; 3555e61939bSmrg case 'y': 3565e61939bSmrg sysroot = FcStrCopy ((const FcChar8 *)optarg); 3575e61939bSmrg break; 3582c393a42Smrg case 'V': 3591887081fSmrg fprintf (stderr, "fontconfig version %d.%d.%d\n", 3602c393a42Smrg FC_MAJOR, FC_MINOR, FC_REVISION); 3612c393a42Smrg exit (0); 3622c393a42Smrg case 'v': 3632c393a42Smrg verbose = FcTrue; 3642c393a42Smrg break; 365a6844aabSmrg case 'h': 366a6844aabSmrg usage (argv[0], 0); 3672c393a42Smrg default: 368a6844aabSmrg usage (argv[0], 1); 3692c393a42Smrg } 3702c393a42Smrg } 3712c393a42Smrg i = optind; 3722c393a42Smrg#else 3732c393a42Smrg i = 1; 3742c393a42Smrg#endif 3752c393a42Smrg 3762c393a42Smrg if (systemOnly) 3772c393a42Smrg FcConfigEnableHome (FcFalse); 3785e61939bSmrg if (sysroot) 3795e61939bSmrg { 3805e61939bSmrg FcConfigSetSysRoot (NULL, sysroot); 3815e61939bSmrg FcStrFree (sysroot); 3825e61939bSmrg config = FcConfigGetCurrent(); 3835e61939bSmrg } 3845e61939bSmrg else 3855e61939bSmrg { 3865e61939bSmrg config = FcInitLoadConfig (); 3875e61939bSmrg } 3882c393a42Smrg if (!config) 3892c393a42Smrg { 3901887081fSmrg fprintf (stderr, _("%s: Can't initialize font config library\n"), argv[0]); 3912c393a42Smrg return 1; 3922c393a42Smrg } 3932c393a42Smrg FcConfigSetCurrent (config); 3942c393a42Smrg 3952c393a42Smrg if (argv[i]) 3962c393a42Smrg { 3972c393a42Smrg dirs = FcStrSetCreate (); 3982c393a42Smrg if (!dirs) 3992c393a42Smrg { 4001887081fSmrg fprintf (stderr, _("%s: Can't create list of directories\n"), 4012c393a42Smrg argv[0]); 4022c393a42Smrg return 1; 4032c393a42Smrg } 4042c393a42Smrg while (argv[i]) 4052c393a42Smrg { 4062c393a42Smrg if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 4072c393a42Smrg { 4081887081fSmrg fprintf (stderr, _("%s: Can't add directory\n"), argv[0]); 4092c393a42Smrg return 1; 4102c393a42Smrg } 4112c393a42Smrg i++; 4122c393a42Smrg } 4132c393a42Smrg list = FcStrListCreate (dirs); 4142c393a42Smrg FcStrSetDestroy (dirs); 4152c393a42Smrg } 4162c393a42Smrg else 4171887081fSmrg list = FcConfigGetFontDirs (config); 4182c393a42Smrg 4192c393a42Smrg if ((processed_dirs = FcStrSetCreate()) == NULL) { 4201887081fSmrg fprintf(stderr, _("Out of Memory\n")); 4212c393a42Smrg return 1; 4222c393a42Smrg } 423b2a52337Smrg 4247872e0a1Smrg if (verbose) 4257872e0a1Smrg { 4267872e0a1Smrg const FcChar8 *dir; 4277872e0a1Smrg 4287872e0a1Smrg printf ("Font directories:\n"); 4297872e0a1Smrg while ((dir = FcStrListNext (list))) 4307872e0a1Smrg { 4317872e0a1Smrg printf ("\t%s\n", dir); 4327872e0a1Smrg } 4337872e0a1Smrg FcStrListFirst(list); 4347872e0a1Smrg } 435898dab68Smrg changed = 0; 43618bd4a06Smrg ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed); 437d91dd368Smrg FcStrListDone (list); 438898dab68Smrg 439898dab68Smrg /* 440898dab68Smrg * Try to create CACHEDIR.TAG anyway. 441898dab68Smrg * This expects the fontconfig cache directory already exists. 442898dab68Smrg * If it doesn't, it won't be simply created. 443898dab68Smrg */ 444898dab68Smrg FcCacheCreateTagFile (config); 4452c393a42Smrg 4462c393a42Smrg FcStrSetDestroy (processed_dirs); 4472c393a42Smrg 4482c393a42Smrg cleanCacheDirectories (config, verbose); 4492c393a42Smrg 450d91dd368Smrg FcConfigDestroy (config); 451d91dd368Smrg FcFini (); 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 */ 459d91dd368Smrg /* the resolution of mtime on FAT is 2 seconds */ 460898dab68Smrg if (!quick && changed) 461898dab68Smrg sleep (2); 4622c393a42Smrg if (verbose) 4631887081fSmrg printf ("%s: %s\n", argv[0], ret ? _("failed") : _("succeeded")); 4642c393a42Smrg return ret; 4652c393a42Smrg} 466