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