fc-cache.c revision 2c393a42
1/* 2 * $RCSId: xc/lib/fontconfig/fc-cache/fc-cache.c,v 1.8tsi Exp $ 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 Keith Packard not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. Keith Packard makes 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 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL KEITH PACKARD 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#include "../fc-arch/fcarch.h" 26 27#ifdef HAVE_CONFIG_H 28#include <config.h> 29#else 30#ifdef linux 31#define HAVE_GETOPT_LONG 1 32#endif 33#define HAVE_GETOPT 1 34#endif 35 36#include <fontconfig/fontconfig.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <unistd.h> 40#include <sys/types.h> 41#include <sys/stat.h> 42#include <errno.h> 43#include <fcntl.h> 44#include <dirent.h> 45#include <string.h> 46 47#if defined (_WIN32) 48#define STRICT 49#include <windows.h> 50#define sleep(x) Sleep((x) * 1000) 51#undef STRICT 52#endif 53 54#ifndef O_BINARY 55#define O_BINARY 0 56#endif 57 58#ifndef HAVE_GETOPT 59#define HAVE_GETOPT 0 60#endif 61#ifndef HAVE_GETOPT_LONG 62#define HAVE_GETOPT_LONG 0 63#endif 64 65#if HAVE_GETOPT_LONG 66#undef _GNU_SOURCE 67#define _GNU_SOURCE 68#include <getopt.h> 69const struct option longopts[] = { 70 {"force", 0, 0, 'f'}, 71 {"really-force", 0, 0, 'r'}, 72 {"system-only", 0, 0, 's'}, 73 {"version", 0, 0, 'V'}, 74 {"verbose", 0, 0, 'v'}, 75 {"help", 0, 0, '?'}, 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) 87{ 88#if HAVE_GETOPT_LONG 89 fprintf (stderr, "usage: %s [-frsvV?] [--force|--really-force] [--system-only] [--verbose] [--version] [--help] [dirs]\n", 90 program); 91#else 92 fprintf (stderr, "usage: %s [-frsvV?] [dirs]\n", 93 program); 94#endif 95 fprintf (stderr, "Build font information caches in [dirs]\n" 96 "(all directories in font configuration by default).\n"); 97 fprintf (stderr, "\n"); 98#if HAVE_GETOPT_LONG 99 fprintf (stderr, " -f, --force scan directories with apparently valid caches\n"); 100 fprintf (stderr, " -r, --really-force erase all existing caches, then rescan\n"); 101 fprintf (stderr, " -s, --system-only scan system-wide directories only\n"); 102 fprintf (stderr, " -v, --verbose display status information while busy\n"); 103 fprintf (stderr, " -V, --version display font config version and exit\n"); 104 fprintf (stderr, " -?, --help display this help and exit\n"); 105#else 106 fprintf (stderr, " -f (force) scan directories with apparently valid caches\n"); 107 fprintf (stderr, " -r, (really force) erase all existing caches, then rescan\n"); 108 fprintf (stderr, " -s (system) scan system-wide directories only\n"); 109 fprintf (stderr, " -v (verbose) display status information while busy\n"); 110 fprintf (stderr, " -V (version) display font config version and exit\n"); 111 fprintf (stderr, " -? (help) display this help and exit\n"); 112#endif 113 exit (1); 114} 115 116static FcStrSet *processed_dirs; 117 118static int 119scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose) 120{ 121 int ret = 0; 122 const FcChar8 *dir; 123 FcStrSet *subdirs; 124 FcStrList *sublist; 125 FcCache *cache; 126 struct stat statb; 127 FcBool was_valid; 128 int i; 129 130 /* 131 * Now scan all of the directories into separate databases 132 * and write out the results 133 */ 134 while ((dir = FcStrListNext (list))) 135 { 136 if (verbose) 137 { 138 printf ("%s: ", dir); 139 fflush (stdout); 140 } 141 142 if (!dir) 143 { 144 if (verbose) 145 printf ("skipping, no such directory\n"); 146 continue; 147 } 148 149 if (FcStrSetMember (processed_dirs, dir)) 150 { 151 if (verbose) 152 printf ("skipping, looped directory detected\n"); 153 continue; 154 } 155 156 if (stat ((char *) dir, &statb) == -1) 157 { 158 switch (errno) { 159 case ENOENT: 160 case ENOTDIR: 161 if (verbose) 162 printf ("skipping, no such directory\n"); 163 break; 164 default: 165 fprintf (stderr, "\"%s\": ", dir); 166 perror (""); 167 ret++; 168 break; 169 } 170 continue; 171 } 172 173 if (!S_ISDIR (statb.st_mode)) 174 { 175 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); 176 continue; 177 } 178 179 if (really_force) 180 FcDirCacheUnlink (dir, config); 181 182 cache = NULL; 183 was_valid = FcFalse; 184 if (!force) { 185 cache = FcDirCacheLoad (dir, config, NULL); 186 if (cache) 187 was_valid = FcTrue; 188 } 189 190 if (!cache) 191 { 192 cache = FcDirCacheRead (dir, FcTrue, config); 193 if (!cache) 194 { 195 fprintf (stderr, "%s: error scanning\n", dir); 196 ret++; 197 continue; 198 } 199 } 200 201 if (was_valid) 202 { 203 if (verbose) 204 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", 205 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 206 } 207 else 208 { 209 if (verbose) 210 printf ("caching, new cache contents: %d fonts, %d dirs\n", 211 FcCacheNumFont (cache), FcCacheNumSubdir (cache)); 212 213 if (!FcDirCacheValid (dir)) 214 { 215 fprintf (stderr, "%s: failed to write cache\n", dir); 216 (void) FcDirCacheUnlink (dir, config); 217 ret++; 218 } 219 } 220 221 subdirs = FcStrSetCreate (); 222 if (!subdirs) 223 { 224 fprintf (stderr, "%s: Can't create subdir set\n", dir); 225 ret++; 226 FcDirCacheUnload (cache); 227 continue; 228 } 229 for (i = 0; i < FcCacheNumSubdir (cache); i++) 230 FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); 231 232 FcDirCacheUnload (cache); 233 234 sublist = FcStrListCreate (subdirs); 235 FcStrSetDestroy (subdirs); 236 if (!sublist) 237 { 238 fprintf (stderr, "%s: Can't create subdir list\n", dir); 239 ret++; 240 continue; 241 } 242 FcStrSetAdd (processed_dirs, dir); 243 ret += scanDirs (sublist, config, force, really_force, verbose); 244 } 245 FcStrListDone (list); 246 return ret; 247} 248 249static FcBool 250cleanCacheDirectory (FcConfig *config, FcChar8 *dir, FcBool verbose) 251{ 252 DIR *d; 253 struct dirent *ent; 254 FcChar8 *dir_base; 255 FcBool ret = FcTrue; 256 FcBool remove; 257 FcCache *cache; 258 struct stat target_stat; 259 260 dir_base = FcStrPlus (dir, (FcChar8 *) "/"); 261 if (!dir_base) 262 { 263 fprintf (stderr, "%s: out of memory\n", dir); 264 return FcFalse; 265 } 266 if (access ((char *) dir, W_OK) != 0) 267 { 268 if (verbose) 269 printf ("%s: not cleaning %s cache directory\n", dir, 270 access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent"); 271 FcStrFree (dir_base); 272 return FcTrue; 273 } 274 if (verbose) 275 printf ("%s: cleaning cache directory\n", dir); 276 d = opendir ((char *) dir); 277 if (!d) 278 { 279 perror ((char *) dir); 280 FcStrFree (dir_base); 281 return FcFalse; 282 } 283 while ((ent = readdir (d))) 284 { 285 FcChar8 *file_name; 286 const FcChar8 *target_dir; 287 288 if (ent->d_name[0] == '.') 289 continue; 290 /* skip cache files for different architectures and */ 291 /* files which are not cache files at all */ 292 if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) || 293 strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX)) 294 continue; 295 296 file_name = FcStrPlus (dir_base, (FcChar8 *) ent->d_name); 297 if (!file_name) 298 { 299 fprintf (stderr, "%s: allocation failure\n", dir); 300 ret = FcFalse; 301 break; 302 } 303 remove = FcFalse; 304 cache = FcDirCacheLoadFile (file_name, NULL); 305 if (!cache) 306 { 307 if (verbose) 308 printf ("%s: invalid cache file: %s\n", dir, ent->d_name); 309 remove = FcTrue; 310 } 311 else 312 { 313 target_dir = FcCacheDir (cache); 314 if (stat ((char *) target_dir, &target_stat) < 0) 315 { 316 if (verbose) 317 printf ("%s: %s: missing directory: %s \n", 318 dir, ent->d_name, target_dir); 319 remove = FcTrue; 320 } 321 } 322 if (remove) 323 { 324 if (unlink ((char *) file_name) < 0) 325 { 326 perror ((char *) file_name); 327 ret = FcFalse; 328 } 329 } 330 FcDirCacheUnload (cache); 331 FcStrFree (file_name); 332 } 333 334 closedir (d); 335 FcStrFree (dir_base); 336 return ret; 337} 338 339static FcBool 340cleanCacheDirectories (FcConfig *config, FcBool verbose) 341{ 342 FcStrList *cache_dirs = FcConfigGetCacheDirs (config); 343 FcChar8 *cache_dir; 344 FcBool ret = FcTrue; 345 346 if (!cache_dirs) 347 return FcFalse; 348 while ((cache_dir = FcStrListNext (cache_dirs))) 349 { 350 if (!cleanCacheDirectory (config, cache_dir, verbose)) 351 { 352 ret = FcFalse; 353 break; 354 } 355 } 356 FcStrListDone (cache_dirs); 357 return ret; 358} 359 360int 361main (int argc, char **argv) 362{ 363 FcStrSet *dirs; 364 FcStrList *list; 365 FcBool verbose = FcFalse; 366 FcBool force = FcFalse; 367 FcBool really_force = FcFalse; 368 FcBool systemOnly = FcFalse; 369 FcConfig *config; 370 int i; 371 int ret; 372#if HAVE_GETOPT_LONG || HAVE_GETOPT 373 int c; 374 375#if HAVE_GETOPT_LONG 376 while ((c = getopt_long (argc, argv, "frsVv?", longopts, NULL)) != -1) 377#else 378 while ((c = getopt (argc, argv, "frsVv?")) != -1) 379#endif 380 { 381 switch (c) { 382 case 'r': 383 really_force = FcTrue; 384 /* fall through */ 385 case 'f': 386 force = FcTrue; 387 break; 388 case 's': 389 systemOnly = FcTrue; 390 break; 391 case 'V': 392 fprintf (stderr, "fontconfig version %d.%d.%d\n", 393 FC_MAJOR, FC_MINOR, FC_REVISION); 394 exit (0); 395 case 'v': 396 verbose = FcTrue; 397 break; 398 default: 399 usage (argv[0]); 400 } 401 } 402 i = optind; 403#else 404 i = 1; 405#endif 406 407 if (systemOnly) 408 FcConfigEnableHome (FcFalse); 409 config = FcInitLoadConfig (); 410 if (!config) 411 { 412 fprintf (stderr, "%s: Can't init font config library\n", argv[0]); 413 return 1; 414 } 415 FcConfigSetCurrent (config); 416 417 if (argv[i]) 418 { 419 dirs = FcStrSetCreate (); 420 if (!dirs) 421 { 422 fprintf (stderr, "%s: Can't create list of directories\n", 423 argv[0]); 424 return 1; 425 } 426 while (argv[i]) 427 { 428 if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i])) 429 { 430 fprintf (stderr, "%s: Can't add directory\n", argv[0]); 431 return 1; 432 } 433 i++; 434 } 435 list = FcStrListCreate (dirs); 436 FcStrSetDestroy (dirs); 437 } 438 else 439 list = FcConfigGetConfigDirs (config); 440 441 if ((processed_dirs = FcStrSetCreate()) == NULL) { 442 fprintf(stderr, "Cannot malloc\n"); 443 return 1; 444 } 445 446 ret = scanDirs (list, config, force, really_force, verbose); 447 448 FcStrSetDestroy (processed_dirs); 449 450 cleanCacheDirectories (config, verbose); 451 452 /* 453 * Now we need to sleep a second (or two, to be extra sure), to make 454 * sure that timestamps for changes after this run of fc-cache are later 455 * then any timestamps we wrote. We don't use gettimeofday() because 456 * sleep(3) can't be interrupted by a signal here -- this isn't in the 457 * library, and there aren't any signals flying around here. 458 */ 459 FcConfigDestroy (config); 460 FcFini (); 461 sleep (2); 462 if (verbose) 463 printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded"); 464 return ret; 465} 466