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