]> git.wh0rd.org - fontconfig.git/blame - fc-cache/fc-cache.c
fc-cache: convert
[fontconfig.git] / fc-cache / fc-cache.c
CommitLineData
24330d27 1/*
317b8492 2 * fontconfig/fc-cache/fc-cache.c
24330d27 3 *
46b51147 4 * Copyright © 2002 Keith Packard
24330d27
KP
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
5aaf466d 10 * documentation, and that the name of the author(s) not be used in
24330d27 11 * advertising or publicity pertaining to distribution of the software without
5aaf466d 12 * specific, written prior permission. The authors make no
24330d27
KP
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
15 *
3074a73b 16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
24330d27 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
3074a73b 18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
24330d27
KP
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
d1a0fca3 25#include "../src/fcarch.h"
0334e5a2 26
24330d27
KP
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#else
c4bd0638
MALF
30#ifdef linux
31#define HAVE_GETOPT_LONG 1
32#endif
24330d27
KP
33#define HAVE_GETOPT 1
34#endif
35
4984242e 36#include <fontconfig/fontconfig.h>
f045376c
PL
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>
d8ab9e6c
KP
43#include <fcntl.h>
44#include <dirent.h>
0334e5a2 45#include <string.h>
f045376c 46
d6217cc6
PL
47#if defined (_WIN32)
48#define STRICT
49#include <windows.h>
50#define sleep(x) Sleep((x) * 1000)
51#undef STRICT
52#endif
53
d8ab9e6c
KP
54#ifndef O_BINARY
55#define O_BINARY 0
56#endif
57
c4bd0638
MALF
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
24330d27 65#if HAVE_GETOPT_LONG
c4bd0638 66#undef _GNU_SOURCE
24330d27
KP
67#define _GNU_SOURCE
68#include <getopt.h>
69const struct option longopts[] = {
8ba2b3c5 70 {"convert", 0, 0, 'c'},
179c3995 71 {"force", 0, 0, 'f'},
d2c01029 72 {"really-force", 0, 0, 'r'},
d0471dd2 73 {"root", 1, 0, 'R'},
ff3f1f98 74 {"system-only", 0, 0, 's'},
24330d27
KP
75 {"version", 0, 0, 'V'},
76 {"verbose", 0, 0, 'v'},
1439c8f2 77 {"help", 0, 0, 'h'},
24330d27
KP
78 {NULL,0,0,0},
79};
80#else
81#if HAVE_GETOPT
82extern char *optarg;
83extern int optind, opterr, optopt;
84#endif
85#endif
86
65018b4a 87static void
1439c8f2 88usage (char *program, int error)
24330d27 89{
1439c8f2 90 FILE *file = error ? stderr : stdout;
86b12431 91#if HAVE_GETOPT_LONG
d0471dd2 92 fprintf (file, "usage: %s [-frRsvVh] [--force|--really-force] [--root <root>] [--system-only] [--verbose] [--version] [--help] [dirs]\n",
86b12431
KP
93 program);
94#else
d0471dd2 95 fprintf (file, "usage: %s [-frRsvVh] [dirs]\n",
24330d27 96 program);
86b12431 97#endif
1439c8f2 98 fprintf (file, "Build font information caches in [dirs]\n"
24330d27 99 "(all directories in font configuration by default).\n");
1439c8f2 100 fprintf (file, "\n");
86b12431 101#if HAVE_GETOPT_LONG
1439c8f2
BE
102 fprintf (file, " -f, --force scan directories with apparently valid caches\n");
103 fprintf (file, " -r, --really-force erase all existing caches, then rescan\n");
d0471dd2 104 fprintf (file, " -R, --root <root> change to <root> before loading files\n");
1439c8f2
BE
105 fprintf (file, " -s, --system-only scan system-wide directories only\n");
106 fprintf (file, " -v, --verbose display status information while busy\n");
107 fprintf (file, " -V, --version display font config version and exit\n");
108 fprintf (file, " -h, --help display this help and exit\n");
86b12431 109#else
1439c8f2
BE
110 fprintf (file, " -f (force) scan directories with apparently valid caches\n");
111 fprintf (file, " -r, (really force) erase all existing caches, then rescan\n");
d0471dd2 112 fprintf (file, " -R <root> (root) change to <root> before loading files\n");
1439c8f2
BE
113 fprintf (file, " -s (system) scan system-wide directories only\n");
114 fprintf (file, " -v (verbose) display status information while busy\n");
115 fprintf (file, " -V (version) display font config version and exit\n");
116 fprintf (file, " -h (help) display this help and exit\n");
86b12431 117#endif
1439c8f2 118 exit (error);
24330d27
KP
119}
120
660acf8f
PL
121static FcStrSet *processed_dirs;
122
179c3995 123static int
111e5b6d 124scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, int *changed)
24330d27 125{
4984242e
KP
126 int ret = 0;
127 const FcChar8 *dir;
128 FcStrSet *subdirs;
129 FcStrList *sublist;
130 FcCache *cache;
131 struct stat statb;
132 FcBool was_valid;
133 int i;
179c3995
KP
134
135 /*
136 * Now scan all of the directories into separate databases
137 * and write out the results
138 */
db50cbda 139 while ((dir = FcStrListNext (list)))
179c3995
KP
140 {
141 if (verbose)
142 {
bc5e487f 143 printf ("%s: ", dir);
179c3995
KP
144 fflush (stdout);
145 }
275cf6cd
PL
146
147 if (!dir)
148 {
149 if (verbose)
150 printf ("skipping, no such directory\n");
151 continue;
152 }
153
660acf8f
PL
154 if (FcStrSetMember (processed_dirs, dir))
155 {
156 if (verbose)
157 printf ("skipping, looped directory detected\n");
158 continue;
159 }
275cf6cd 160
d0471dd2 161 if (FcStat (config, dir, &statb) == -1)
0c35c0fa 162 {
565a919e
KP
163 switch (errno) {
164 case ENOENT:
165 case ENOTDIR:
5d43e799 166 if (verbose)
565a919e 167 printf ("skipping, no such directory\n");
565a919e
KP
168 break;
169 default:
0c35c0fa
KP
170 fprintf (stderr, "\"%s\": ", dir);
171 perror ("");
172 ret++;
e12f718f 173 break;
0c35c0fa 174 }
565a919e
KP
175 continue;
176 }
e12f718f 177
0c35c0fa
KP
178 if (!S_ISDIR (statb.st_mode))
179 {
180 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
181 continue;
182 }
d2c01029
PL
183
184 if (really_force)
185 FcDirCacheUnlink (dir, config);
186
bc5e487f
KP
187 cache = NULL;
188 was_valid = FcFalse;
189 if (!force) {
190 cache = FcDirCacheLoad (dir, config, NULL);
191 if (cache)
192 was_valid = FcTrue;
193 }
2d3387fd 194
bc5e487f 195 if (!cache)
179c3995 196 {
111e5b6d 197 (*changed)++;
bc5e487f
KP
198 cache = FcDirCacheRead (dir, FcTrue, config);
199 if (!cache)
200 {
201 fprintf (stderr, "%s: error scanning\n", dir);
202 ret++;
203 continue;
204 }
179c3995 205 }
bc5e487f 206
bc5e487f 207 if (was_valid)
179c3995
KP
208 {
209 if (verbose)
50124d1e 210 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n",
4984242e 211 FcCacheNumFont (cache), FcCacheNumSubdir (cache));
179c3995
KP
212 }
213 else
214 {
215 if (verbose)
50124d1e 216 printf ("caching, new cache contents: %d fonts, %d dirs\n",
4984242e 217 FcCacheNumFont (cache), FcCacheNumSubdir (cache));
4262e0b3 218
f57783d2 219 if (!FcDirCacheValid (dir))
179c3995 220 {
2d3387fd
KP
221 fprintf (stderr, "%s: failed to write cache\n", dir);
222 (void) FcDirCacheUnlink (dir, config);
179c3995
KP
223 ret++;
224 }
225 }
bc5e487f
KP
226
227 subdirs = FcStrSetCreate ();
228 if (!subdirs)
229 {
230 fprintf (stderr, "%s: Can't create subdir set\n", dir);
231 ret++;
232 FcDirCacheUnload (cache);
233 continue;
234 }
4984242e 235 for (i = 0; i < FcCacheNumSubdir (cache); i++)
bc5e487f
KP
236 FcStrSetAdd (subdirs, FcCacheSubdir (cache, i));
237
238 FcDirCacheUnload (cache);
239
179c3995 240 sublist = FcStrListCreate (subdirs);
5c1853cd 241 FcStrSetDestroy (subdirs);
179c3995
KP
242 if (!sublist)
243 {
2d3387fd 244 fprintf (stderr, "%s: Can't create subdir list\n", dir);
179c3995
KP
245 ret++;
246 continue;
247 }
660acf8f 248 FcStrSetAdd (processed_dirs, dir);
111e5b6d 249 ret += scanDirs (sublist, config, force, really_force, verbose, changed);
179c3995
KP
250 }
251 FcStrListDone (list);
252 return ret;
253}
254
d8ab9e6c
KP
255static FcBool
256cleanCacheDirectory (FcConfig *config, FcChar8 *dir, FcBool verbose)
257{
258 DIR *d;
259 struct dirent *ent;
d0471dd2
MF
260 FcChar8 *fullDir;
261 FcChar8 *checkDir;
d8ab9e6c
KP
262 FcBool ret = FcTrue;
263 FcBool remove;
264 FcCache *cache;
d8ab9e6c
KP
265 struct stat target_stat;
266
d0471dd2
MF
267 fullDir = FcConfigGetRootPlus (config, dir);
268 if (fullDir)
269 checkDir = fullDir;
270 else
271 checkDir = dir;
272
273 if (access ((char *) checkDir, W_OK) != 0)
d8ab9e6c
KP
274 {
275 if (verbose)
2b0d3d8a
KP
276 printf ("%s: not cleaning %s cache directory\n", dir,
277 access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent");
d0471dd2 278 goto done;
d8ab9e6c 279 }
bc5e487f
KP
280 if (verbose)
281 printf ("%s: cleaning cache directory\n", dir);
d0471dd2 282 d = opendir ((char *) checkDir);
d8ab9e6c
KP
283 if (!d)
284 {
b190ad9d 285 perror ((char *) dir);
d0471dd2
MF
286 ret = FcFalse;
287 goto done;
d8ab9e6c
KP
288 }
289 while ((ent = readdir (d)))
290 {
291 FcChar8 *file_name;
4984242e 292 const FcChar8 *target_dir;
d8ab9e6c
KP
293
294 if (ent->d_name[0] == '.')
295 continue;
0334e5a2
MF
296 /* skip cache files for different architectures and */
297 /* files which are not cache files at all */
298 if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) ||
299 strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX))
300 continue;
301
bb8057ea 302 file_name = FcStrPathPlus (dir, (const FcChar8 *) ent->d_name, NULL);
d8ab9e6c
KP
303 if (!file_name)
304 {
305 fprintf (stderr, "%s: allocation failure\n", dir);
306 ret = FcFalse;
307 break;
308 }
2a3e3c44 309 remove = FcFalse;
d0471dd2 310 cache = FcDirCacheLoadFile2 (file_name, config, NULL);
d8ab9e6c 311 if (!cache)
d8ab9e6c
KP
312 {
313 if (verbose)
2a3e3c44 314 printf ("%s: invalid cache file: %s\n", dir, ent->d_name);
d8ab9e6c
KP
315 remove = FcTrue;
316 }
2a3e3c44 317 else
d8ab9e6c 318 {
2a3e3c44 319 target_dir = FcCacheDir (cache);
d0471dd2 320 if (FcStat (config, target_dir, &target_stat) < 0)
2a3e3c44
KP
321 {
322 if (verbose)
323 printf ("%s: %s: missing directory: %s \n",
324 dir, ent->d_name, target_dir);
2a3e3c44
KP
325 remove = FcTrue;
326 }
d8ab9e6c
KP
327 }
328 if (remove)
329 {
d0471dd2
MF
330 FcChar8 *unlink_file = FcConfigGetRootPlus (config, file_name);
331 if (!unlink_file)
332 unlink_file = file_name;
333 if (unlink ((char *) unlink_file) < 0)
d8ab9e6c 334 {
d0471dd2 335 perror ((char *) unlink_file);
d8ab9e6c
KP
336 ret = FcFalse;
337 }
d0471dd2
MF
338 if (unlink_file != file_name)
339 FcStrFree (unlink_file);
d8ab9e6c 340 }
1741499e 341 FcDirCacheUnload (cache);
d8ab9e6c
KP
342 FcStrFree (file_name);
343 }
344
345 closedir (d);
d0471dd2
MF
346 done:
347 if (fullDir)
348 FcStrFree (fullDir);
d8ab9e6c
KP
349 return ret;
350}
351
352static FcBool
353cleanCacheDirectories (FcConfig *config, FcBool verbose)
354{
355 FcStrList *cache_dirs = FcConfigGetCacheDirs (config);
356 FcChar8 *cache_dir;
357 FcBool ret = FcTrue;
358
359 if (!cache_dirs)
360 return FcFalse;
361 while ((cache_dir = FcStrListNext (cache_dirs)))
362 {
363 if (!cleanCacheDirectory (config, cache_dir, verbose))
364 {
365 ret = FcFalse;
366 break;
367 }
368 }
369 FcStrListDone (cache_dirs);
370 return ret;
371}
372
179c3995
KP
373int
374main (int argc, char **argv)
375{
376 FcStrSet *dirs;
377 FcStrList *list;
378 FcBool verbose = FcFalse;
379 FcBool force = FcFalse;
d2c01029 380 FcBool really_force = FcFalse;
ff3f1f98 381 FcBool systemOnly = FcFalse;
179c3995 382 FcConfig *config;
d0471dd2 383 const char *rootDir;
24330d27 384 int i;
111e5b6d 385 int changed;
179c3995 386 int ret;
24330d27
KP
387#if HAVE_GETOPT_LONG || HAVE_GETOPT
388 int c;
389
8ba2b3c5
MF
390FcBool convert = FcFalse;
391
24330d27 392#if HAVE_GETOPT_LONG
8ba2b3c5 393 while ((c = getopt_long (argc, argv, "cfrR:sVvh", longopts, NULL)) != -1)
24330d27 394#else
8ba2b3c5 395 while ((c = getopt (argc, argv, "cfrR:sVvh")) != -1)
24330d27
KP
396#endif
397 {
398 switch (c) {
8ba2b3c5
MF
399 case 'c':
400 convert = FcTrue;
401 break;
d2c01029
PL
402 case 'r':
403 really_force = FcTrue;
404 /* fall through */
179c3995
KP
405 case 'f':
406 force = FcTrue;
407 break;
d0471dd2
MF
408 case 'R':
409 rootDir = optarg;
410 break;
ff3f1f98
KP
411 case 's':
412 systemOnly = FcTrue;
413 break;
24330d27
KP
414 case 'V':
415 fprintf (stderr, "fontconfig version %d.%d.%d\n",
416 FC_MAJOR, FC_MINOR, FC_REVISION);
417 exit (0);
418 case 'v':
179c3995 419 verbose = FcTrue;
24330d27 420 break;
1439c8f2
BE
421 case 'h':
422 usage (argv[0], 0);
24330d27 423 default:
1439c8f2 424 usage (argv[0], 1);
24330d27
KP
425 }
426 }
427 i = optind;
428#else
429 i = 1;
430#endif
431
8ba2b3c5
MF
432 if (convert)
433 {
434 const FcChar8 *src_cache, *dst_cache, *src_type, *dst_type;
435
436 src_cache = (const FcChar8 *) argv[i++];
437 src_type = (const FcChar8 *) argv[i++];
438 dst_cache = (const FcChar8 *) argv[i++];
439 dst_type = (const FcChar8 *) argv[i++];
440
441 FcDirCacheConvert (src_cache, src_type, dst_cache, dst_type);
442
443 return 0;
444 }
445
ff3f1f98
KP
446 if (systemOnly)
447 FcConfigEnableHome (FcFalse);
5e678e94 448 config = FcInitLoadConfig ();
179c3995 449 if (!config)
24330d27 450 {
179c3995 451 fprintf (stderr, "%s: Can't init font config library\n", argv[0]);
24330d27
KP
452 return 1;
453 }
df3efc11 454 FcConfigSetCurrent (config);
d0471dd2 455 FcConfigSetRoot (config, (const FcChar8 *) rootDir);
212c9f43 456
24330d27 457 if (argv[i])
24330d27 458 {
179c3995
KP
459 dirs = FcStrSetCreate ();
460 if (!dirs)
24330d27 461 {
179c3995
KP
462 fprintf (stderr, "%s: Can't create list of directories\n",
463 argv[0]);
464 return 1;
24330d27 465 }
179c3995 466 while (argv[i])
24330d27 467 {
9b511b29 468 if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
24330d27 469 {
179c3995
KP
470 fprintf (stderr, "%s: Can't add directory\n", argv[0]);
471 return 1;
24330d27 472 }
179c3995 473 i++;
24330d27 474 }
179c3995
KP
475 list = FcStrListCreate (dirs);
476 FcStrSetDestroy (dirs);
24330d27 477 }
179c3995
KP
478 else
479 list = FcConfigGetConfigDirs (config);
660acf8f
PL
480
481 if ((processed_dirs = FcStrSetCreate()) == NULL) {
482 fprintf(stderr, "Cannot malloc\n");
483 return 1;
484 }
485
111e5b6d
BE
486 changed = 0;
487 ret = scanDirs (list, config, force, really_force, verbose, &changed);
660acf8f
PL
488
489 FcStrSetDestroy (processed_dirs);
490
d8ab9e6c
KP
491 cleanCacheDirectories (config, verbose);
492
54560b01
KP
493 /*
494 * Now we need to sleep a second (or two, to be extra sure), to make
495 * sure that timestamps for changes after this run of fc-cache are later
496 * then any timestamps we wrote. We don't use gettimeofday() because
497 * sleep(3) can't be interrupted by a signal here -- this isn't in the
498 * library, and there aren't any signals flying around here.
499 */
716ac8b8 500 FcConfigDestroy (config);
c6c9400d 501 FcFini ();
111e5b6d
BE
502 if (changed)
503 sleep (2);
24330d27
KP
504 if (verbose)
505 printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded");
506 return ret;
507}