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