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