fc-cache.c revision 5e61939b
1/* 2 * fontconfig/fc-cache/fc-cache.c 3 * 4 * Copyright © 2002 Keith Packard 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of the author(s) not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. The authors make no 13 * representations about the suitability of this software for any purpose. It 14 * is provided "as is" without express or implied warranty. 15 * 16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 * PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25#ifdef HAVE_CONFIG_H 26#include <config.h> 27#else 28#ifdef linux 29#define HAVE_GETOPT_LONG 1 30#endif 31#define HAVE_GETOPT 1 32#endif 33 34#include <fontconfig/fontconfig.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <unistd.h> 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <dirent.h> 43#include <string.h> 44 45#if defined (_WIN32) 46#define STRICT 47#include <windows.h> 48#define sleep(x) Sleep((x) * 1000) 49#undef STRICT 50#endif 51 52#ifndef O_BINARY 53#define O_BINARY 0 54#endif 55 56#ifndef HAVE_GETOPT 57#define HAVE_GETOPT 0 58#endif 59#ifndef HAVE_GETOPT_LONG 60#define HAVE_GETOPT_LONG 0 61#endif 62 63#if HAVE_GETOPT_LONG 64#undef _GNU_SOURCE 65#define _GNU_SOURCE 66#include <getopt.h> 67const struct option longopts[] = { 68 {"force", 0, 0, 'f'}, 69 {"quick", 0, 0, 'q'}, 70 {"really-force", 0, 0, 'r'}, 71 {"sysroot", 0, 0, 'y'}, 72 {"system-only", 0, 0, 's'}, 73 {"version", 0, 0, 'V'}, 74 {"verbose", 0, 0, 'v'}, 75 {"help", 0, 0, 'h'}, 76 {NULL,0,0,0}, 77}; 78#else 79#if HAVE_GETOPT 80extern char *optarg; 81extern int optind, opterr, optopt; 82#endif 83#endif 84 85static void 86usage (char *program, int error) 87{ 88 FILE *file = error ? stderr : stdout; 89#if HAVE_GETOPT_LONG 90 fprintf (file, "usage: %s [-fqrsvVh] [--quick] [-y SYSROOT] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 91 program); 92#else 93 fprintf (file, "usage: %s [-fqrsvVh] [-y SYSROOT] [dirs]\n", 94 program); 95#endif 96 fprintf (file, "Build font information caches in [dirs]\n" 97 "(all directories in font configuration by default).\n"); 98 fprintf (file, "\n"); 99#if HAVE_GETOPT_LONG 100 fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 101 fprintf (file, " -q, --quick don't sleep before exiting\n"); 102 fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 103 fprintf (file, " -s, --system-only scan system-wide directories only\n"); 104 fprintf (file, " -y, --sysroot=SYSROOT prepend SYSROOT to all paths for scanning\n"); 105 fprintf (file, " -v, --verbose display status information while busy\n"); 106 fprintf (file, " -V, --version display font config version and exit\n"); 107 fprintf (file, " -h, --help display this help and exit\n"); 108#else 109 fprintf (file, " -f (force) scan directories with apparently valid caches\n"); 110 fprintf (file, " -q (quick) don't sleep before exiting\n"); 111 fprintf (file, " -r, (really force) erase all existing caches, then rescan\n"); 112 fprintf (file, " -s (system) scan system-wide directories only\n"); 113 fprintf (file, " -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n"); 114 fprintf (file, " -v (verbose) display status information while busy\n"); 115 fprintf (file, " -V (version) display font config version and exit\n"); 116 fprintf (file, " -h (help) display this help and exit\n"); 117#endif 118 exit (error); 119} 120 121static FcStrSet *processed_dirs; 122 123static int 124scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, int *changed) 125{ 126 int ret = 0; 127 const FcChar8 *dir; 128 FcStrSet *subdirs; 129 FcStrList *sublist; 130 FcCache *cache; 131 struct stat statb; 132 FcBool was_valid; 133 int i; 134 135 /* 136 * Now scan all of the directories into separate databases 137 * and write out the results 138 */ 139 while ((dir = FcStrListNext (list))) 140 { 141 if (verbose) 142 { 143 printf ("%s: ", dir); 144 fflush (stdout); 145 } 146 147 if (FcStrSetMember (processed_dirs, dir)) 148 { 149 if (verbose) 150 printf ("skipping, looped directory detected\n"); 151 continue; 152 } 153 154 if (stat ((char *) dir, &statb) == -1) 155 { 156 switch (errno) { 157 case ENOENT: 158 case ENOTDIR: 159 if (verbose) 160 printf ("skipping, no such directory\n"); 161 break; 162 default: 163 fprintf (stderr, "\"%s\": ", dir); 164 perror (""); 165 ret++; 166 break; 167 } 168 continue; 169 } 170 171 if (!S_ISDIR (statb.st_mode)) 172 { 173 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 174 continue; 175 } 176 177 if (really_force) 178 FcDirCacheUnlink (dir, config); 179 180 cache = NULL; 181 was_valid = FcFalse; 182 if (!force) { 183 cache = FcDirCacheLoad (dir, config, NULL); 184 if (cache) 185 was_valid = FcTrue; 186 } 187 188 if (!cache) 189 { 190 (*changed)++; 191 cache = FcDirCacheRead (dir, FcTrue, config); 192 if (!cache) 193 { 194 fprintf (stderr, "%s: error scanning\n", dir); 195 ret++; 196 continue; 197 } 198 } 199 200 if (was_valid) 201 { 202 if (verbose) 203 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 204 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 205 } 206 else 207 { 208 if (verbose) 209 printf ("caching, new cache contents: %d fonts, %d dirs\n", 210 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 211 212 if (!FcDirCacheValid (dir)) 213 { 214 fprintf (stderr, "%s: failed to write cache\n", dir); 215 (void) FcDirCacheUnlink (dir, config); 216 ret++; 217 } 218 } 219 220 subdirs = FcStrSetCreate (); 221 if (!subdirs) 222 { 223 fprintf (stderr, "%s: Can't create subdir set\n", dir); 224 ret++; 225 FcDirCacheUnload (cache); 226 continue; 227 } 228 for (i = 0; i < FcCacheNumSubdir (cache); i++) 229 FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 230 231 FcDirCacheUnload (cache); 232 233 sublist = FcStrListCreate (subdirs); 234 FcStrSetDestroy (subdirs); 235 if (!sublist) 236 { 237 fprintf (stderr, "%s: Can't create subdir list\n", dir); 238 ret++; 239 continue; 240 } 241 FcStrSetAdd (processed_dirs, dir); 242 ret += scanDirs (sublist, config, force, really_force, verbose, changed); 243 } 244 FcStrListDone (list); 245 return ret; 246} 247 248static FcBool 249cleanCacheDirectories (FcConfig *config, FcBool verbose) 250{ 251 FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 252 FcChar8 *cache_dir; 253 FcBool ret = FcTrue; 254 255 if (!cache_dirs) 256 return FcFalse; 257 while ((cache_dir = FcStrListNext (cache_dirs))) 258 { 259 if (!FcDirCacheClean (cache_dir, verbose)) 260 { 261 ret = FcFalse; 262 break; 263 } 264 } 265 FcStrListDone (cache_dirs); 266 return ret; 267} 268 269int 270main (int argc, char **argv) 271{ 272 FcStrSet *dirs; 273 FcStrList *list; 274 FcBool verbose = FcFalse; 275 FcBool quick = FcFalse; 276 FcBool force = FcFalse; 277 FcBool really_force = FcFalse; 278 FcBool systemOnly = FcFalse; 279 FcConfig *config; 280 FcChar8 *sysroot = NULL; 281 int i; 282 int changed; 283 int ret; 284#if HAVE_GETOPT_LONG || HAVE_GETOPT 285 int c; 286 287#if HAVE_GETOPT_LONG 288 while ((c = getopt_long (argc, argv, "fqrsy:Vvh", longopts, NULL)) != -1) 289#else 290 while ((c = getopt (argc, argv, "fqrsy:Vvh")) != -1) 291#endif 292 { 293 switch (c) { 294 case 'r': 295 really_force = FcTrue; 296 /* fall through */ 297 case 'f': 298 force = FcTrue; 299 break; 300 case 'q': 301 quick = FcTrue; 302 break; 303 case 's': 304 systemOnly = FcTrue; 305 break; 306 case 'y': 307 sysroot = FcStrCopy ((const FcChar8 *)optarg); 308 break; 309 case 'V': 310 fprintf (stderr, "fontconfig version %d.%d.%d\n", 311 FC_MAJOR, FC_MINOR, FC_REVISION); 312 exit (0); 313 case 'v': 314 verbose = FcTrue; 315 break; 316 case 'h': 317 usage (argv[0], 0); 318 default: 319 usage (argv[0], 1); 320 } 321 } 322 i = optind; 323#else 324 i = 1; 325#endif 326 327 if (systemOnly) 328 FcConfigEnableHome (FcFalse); 329 if (sysroot) 330 { 331 FcConfigSetSysRoot (NULL, sysroot); 332 FcStrFree (sysroot); 333 config = FcConfigGetCurrent(); 334 } 335 else 336 { 337 config = FcInitLoadConfig (); 338 } 339 if (!config) 340 { 341 fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 342 return 1; 343 } 344 FcConfigSetCurrent (config); 345 346 if (argv[i]) 347 { 348 dirs = FcStrSetCreate (); 349 if (!dirs) 350 { 351 fprintf (stderr, "%s: Can't create list of directories\n", 352 argv[0]); 353 return 1; 354 } 355 while (argv[i]) 356 { 357 if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 358 { 359 fprintf (stderr, "%s: Can't add directory\n", argv[0]); 360 return 1; 361 } 362 i++; 363 } 364 list = FcStrListCreate (dirs); 365 FcStrSetDestroy (dirs); 366 } 367 else 368 list = FcConfigGetConfigDirs (config); 369 370 if ((processed_dirs = FcStrSetCreate()) == NULL) { 371 fprintf(stderr, "Cannot malloc\n"); 372 return 1; 373 } 374 375 changed = 0; 376 ret = scanDirs (list, config, force, really_force, verbose, &changed); 377 378 /* 379 * Try to create CACHEDIR.TAG anyway. 380 * This expects the fontconfig cache directory already exists. 381 * If it doesn't, it won't be simply created. 382 */ 383 FcCacheCreateTagFile (config); 384 385 FcStrSetDestroy (processed_dirs); 386 387 cleanCacheDirectories (config, verbose); 388 389 /* 390 * Now we need to sleep a second (or two, to be extra sure), to make 391 * sure that timestamps for changes after this run of fc-cache are later 392 * then any timestamps we wrote. We don't use gettimeofday() because 393 * sleep(3) can't be interrupted by a signal here -- this isn't in the 394 * library, and there aren't any signals flying around here. 395 */ 396 FcConfigDestroy (config); 397 FcFini (); 398 if (!quick && changed) 399 sleep (2); 400 if (verbose) 401 printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 402 return ret; 403} 404