fc-cache.c revision 18bd4a06
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 {"error-on-no-fonts", 0, 0, 'E'}, 69 {"force", 0, 0, 'f'}, 70 {"quick", 0, 0, 'q'}, 71 {"really-force", 0, 0, 'r'}, 72 {"sysroot", required_argument, 0, 'y'}, 73 {"system-only", 0, 0, 's'}, 74 {"version", 0, 0, 'V'}, 75 {"verbose", 0, 0, 'v'}, 76 {"help", 0, 0, 'h'}, 77 {NULL,0,0,0}, 78}; 79#else 80#if HAVE_GETOPT 81extern char *optarg; 82extern int optind, opterr, optopt; 83#endif 84#endif 85 86static void 87usage (char *program, int error) 88{ 89 FILE *file = error ? stderr : stdout; 90#if HAVE_GETOPT_LONG 91 fprintf (file, "usage: %s [-EfqrsvVh] [--quick] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 92 program); 93#else 94 fprintf (file, "usage: %s [-EfqrsvVh] [-y SYSROOT] [dirs]\n", 95 program); 96#endif 97 fprintf (file, "Build font information caches in [dirs]\n" 98 "(all directories in font configuration by default).\n"); 99 fprintf (file, "\n"); 100#if HAVE_GETOPT_LONG 101 fprintf (file, " -E, --error-on-no-fonts raise an error if no fonts in a directory\n"); 102 fprintf (file, " -f, --force scan directories with apparently valid caches\n"); 103 fprintf (file, " -q, --quick don't sleep before exiting\n"); 104 fprintf (file, " -r, --really-force erase all existing caches, then rescan\n"); 105 fprintf (file, " -s, --system-only scan system-wide directories only\n"); 106 fprintf (file, " -y, --sysroot=SYSROOT prepend SYSROOT to all paths for scanning\n"); 107 fprintf (file, " -v, --verbose display status information while busy\n"); 108 fprintf (file, " -V, --version display font config version and exit\n"); 109 fprintf (file, " -h, --help display this help and exit\n"); 110#else 111 fprintf (file, " -E (error-on-no-fonts)\n"); 112 fprintf (file, " raise an error if no fonts in a directory\n"); 113 fprintf (file, " -f (force) scan directories with apparently valid caches\n"); 114 fprintf (file, " -q (quick) don't sleep before exiting\n"); 115 fprintf (file, " -r, (really force) erase all existing caches, then rescan\n"); 116 fprintf (file, " -s (system) scan system-wide directories only\n"); 117 fprintf (file, " -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n"); 118 fprintf (file, " -v (verbose) display status information while busy\n"); 119 fprintf (file, " -V (version) display font config version and exit\n"); 120 fprintf (file, " -h (help) display this help and exit\n"); 121#endif 122 exit (error); 123} 124 125static FcStrSet *processed_dirs; 126 127static int 128scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed) 129{ 130 int ret = 0; 131 const FcChar8 *dir; 132 FcStrSet *subdirs; 133 FcStrList *sublist; 134 FcCache *cache; 135 struct stat statb; 136 FcBool was_valid, was_processed = FcFalse; 137 int i; 138 const FcChar8 *sysroot = FcConfigGetSysRoot (config); 139 140 /* 141 * Now scan all of the directories into separate databases 142 * and write out the results 143 */ 144 while ((dir = FcStrListNext (list))) 145 { 146 if (verbose) 147 { 148 if (sysroot) 149 printf ("[%s]", sysroot); 150 printf ("%s: ", dir); 151 fflush (stdout); 152 } 153 154 if (FcStrSetMember (processed_dirs, dir)) 155 { 156 if (verbose) 157 printf ("skipping, looped directory detected\n"); 158 continue; 159 } 160 161 if (stat ((char *) dir, &statb) == -1) 162 { 163 switch (errno) { 164 case ENOENT: 165 case ENOTDIR: 166 if (verbose) 167 printf ("skipping, no such directory\n"); 168 break; 169 default: 170 fprintf (stderr, "\"%s\": ", dir); 171 perror (""); 172 ret++; 173 break; 174 } 175 continue; 176 } 177 178 if (!S_ISDIR (statb.st_mode)) 179 { 180 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 181 continue; 182 } 183 was_processed = FcTrue; 184 185 if (really_force) 186 FcDirCacheUnlink (dir, config); 187 188 cache = NULL; 189 was_valid = FcFalse; 190 if (!force) { 191 cache = FcDirCacheLoad (dir, config, NULL); 192 if (cache) 193 was_valid = FcTrue; 194 } 195 196 if (!cache) 197 { 198 (*changed)++; 199 cache = FcDirCacheRead (dir, FcTrue, config); 200 if (!cache) 201 { 202 fprintf (stderr, "%s: error scanning\n", dir); 203 ret++; 204 continue; 205 } 206 } 207 208 if (was_valid) 209 { 210 if (verbose) 211 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 212 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 213 } 214 else 215 { 216 if (verbose) 217 printf ("caching, new cache contents: %d fonts, %d dirs\n", 218 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 219 220 if (!FcDirCacheValid (dir)) 221 { 222 fprintf (stderr, "%s: failed to write cache\n", dir); 223 (void) FcDirCacheUnlink (dir, config); 224 ret++; 225 } 226 } 227 228 subdirs = FcStrSetCreate (); 229 if (!subdirs) 230 { 231 fprintf (stderr, "%s: Can't create subdir set\n", dir); 232 ret++; 233 FcDirCacheUnload (cache); 234 continue; 235 } 236 for (i = 0; i < FcCacheNumSubdir (cache); i++) 237 FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 238 239 FcDirCacheUnload (cache); 240 241 sublist = FcStrListCreate (subdirs); 242 FcStrSetDestroy (subdirs); 243 if (!sublist) 244 { 245 fprintf (stderr, "%s: Can't create subdir list\n", dir); 246 ret++; 247 continue; 248 } 249 FcStrSetAdd (processed_dirs, dir); 250 ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed); 251 FcStrListDone (sublist); 252 } 253 if (error_on_no_fonts && !was_processed) 254 ret++; 255 return ret; 256} 257 258static FcBool 259cleanCacheDirectories (FcConfig *config, FcBool verbose) 260{ 261 FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 262 FcChar8 *cache_dir; 263 FcBool ret = FcTrue; 264 265 if (!cache_dirs) 266 return FcFalse; 267 while ((cache_dir = FcStrListNext (cache_dirs))) 268 { 269 if (!FcDirCacheClean (cache_dir, verbose)) 270 { 271 ret = FcFalse; 272 break; 273 } 274 } 275 FcStrListDone (cache_dirs); 276 return ret; 277} 278 279int 280main (int argc, char **argv) 281{ 282 FcStrSet *dirs; 283 FcStrList *list; 284 FcBool verbose = FcFalse; 285 FcBool quick = FcFalse; 286 FcBool force = FcFalse; 287 FcBool really_force = FcFalse; 288 FcBool systemOnly = FcFalse; 289 FcBool error_on_no_fonts = FcFalse; 290 FcConfig *config; 291 FcChar8 *sysroot = NULL; 292 int i; 293 int changed; 294 int ret; 295#if HAVE_GETOPT_LONG || HAVE_GETOPT 296 int c; 297 298#if HAVE_GETOPT_LONG 299 while ((c = getopt_long (argc, argv, "Efqrsy:Vvh", longopts, NULL)) != -1) 300#else 301 while ((c = getopt (argc, argv, "Efqrsy:Vvh")) != -1) 302#endif 303 { 304 switch (c) { 305 case 'E': 306 error_on_no_fonts = FcTrue; 307 break; 308 case 'r': 309 really_force = FcTrue; 310 /* fall through */ 311 case 'f': 312 force = FcTrue; 313 break; 314 case 'q': 315 quick = FcTrue; 316 break; 317 case 's': 318 systemOnly = FcTrue; 319 break; 320 case 'y': 321 sysroot = FcStrCopy ((const FcChar8 *)optarg); 322 break; 323 case 'V': 324 fprintf (stderr, "fontconfig version %d.%d.%d\n", 325 FC_MAJOR, FC_MINOR, FC_REVISION); 326 exit (0); 327 case 'v': 328 verbose = FcTrue; 329 break; 330 case 'h': 331 usage (argv[0], 0); 332 default: 333 usage (argv[0], 1); 334 } 335 } 336 i = optind; 337#else 338 i = 1; 339#endif 340 341 if (systemOnly) 342 FcConfigEnableHome (FcFalse); 343 if (sysroot) 344 { 345 FcConfigSetSysRoot (NULL, sysroot); 346 FcStrFree (sysroot); 347 config = FcConfigGetCurrent(); 348 } 349 else 350 { 351 config = FcInitLoadConfig (); 352 } 353 if (!config) 354 { 355 fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 356 return 1; 357 } 358 FcConfigSetCurrent (config); 359 360 if (argv[i]) 361 { 362 dirs = FcStrSetCreate (); 363 if (!dirs) 364 { 365 fprintf (stderr, "%s: Can't create list of directories\n", 366 argv[0]); 367 return 1; 368 } 369 while (argv[i]) 370 { 371 if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 372 { 373 fprintf (stderr, "%s: Can't add directory\n", argv[0]); 374 return 1; 375 } 376 i++; 377 } 378 list = FcStrListCreate (dirs); 379 FcStrSetDestroy (dirs); 380 } 381 else 382 list = FcConfigGetConfigDirs (config); 383 384 if ((processed_dirs = FcStrSetCreate()) == NULL) { 385 fprintf(stderr, "Cannot malloc\n"); 386 return 1; 387 } 388 389 changed = 0; 390 ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed); 391 FcStrListDone (list); 392 393 /* 394 * Try to create CACHEDIR.TAG anyway. 395 * This expects the fontconfig cache directory already exists. 396 * If it doesn't, it won't be simply created. 397 */ 398 FcCacheCreateTagFile (config); 399 400 FcStrSetDestroy (processed_dirs); 401 402 cleanCacheDirectories (config, verbose); 403 404 FcConfigDestroy (config); 405 FcFini (); 406 /* 407 * Now we need to sleep a second (or two, to be extra sure), to make 408 * sure that timestamps for changes after this run of fc-cache are later 409 * then any timestamps we wrote. We don't use gettimeofday() because 410 * sleep(3) can't be interrupted by a signal here -- this isn't in the 411 * library, and there aren't any signals flying around here. 412 */ 413 /* the resolution of mtime on FAT is 2 seconds */ 414 if (!quick && changed) 415 sleep (2); 416 if (verbose) 417 printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 418 return ret; 419} 420