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