27c75130433ecb824d10a8ecf3a457df34f72f6d
[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, FcStrSet *updateDirs)
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             if (!recursive)
141                 printf ("Re-scanning %s: ", dir);
142             else
143                 printf ("%s: ", dir);
144             fflush (stdout);
145         }
146         
147         if (recursive && FcStrSetMember (processed_dirs, dir))
148         {
149             if (verbose)
150                 printf ("skipping, looped directory detected\n");
151             continue;
152         }
153
154         if (stat ((char *) dir, &statb) == -1)
155         {
156             switch (errno) {
157             case ENOENT:
158             case ENOTDIR:
159                 if (verbose)
160                     printf ("skipping, no such directory\n");
161                 break;
162             default:
163                 fprintf (stderr, "\"%s\": ", dir);
164                 perror ("");
165                 ret++;
166                 break;
167             }
168             continue;
169         }
170
171         if (!S_ISDIR (statb.st_mode))
172         {
173             fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
174             continue;
175         }
176
177         if (really_force)
178             FcDirCacheUnlink (dir, config);
179
180         cache = NULL;
181         was_valid = FcFalse;
182         if (!force) {
183             cache = FcDirCacheLoad (dir, config, NULL);
184             if (cache)
185                 was_valid = FcTrue;
186         }
187         
188         if (!cache)
189         {
190             (*changed)++;
191             cache = FcDirCacheRead (dir, FcTrue, config);
192             if (!cache)
193             {
194                 fprintf (stderr, "%s: error scanning\n", dir);
195                 ret++;
196                 continue;
197             }
198         }
199
200         if (was_valid)
201         {
202             if (verbose)
203                 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n",
204                         FcCacheNumFont (cache), FcCacheNumSubdir (cache));
205         }
206         else
207         {
208             if (verbose)
209                 printf ("caching, new cache contents: %d fonts, %d dirs\n", 
210                         FcCacheNumFont (cache), FcCacheNumSubdir (cache));
211
212             if (!FcDirCacheValid (dir))
213             {
214                 fprintf (stderr, "%s: failed to write cache\n", dir);
215                 (void) FcDirCacheUnlink (dir, config);
216                 ret++;
217             }
218         }
219
220         if (recursive)
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             if (updateDirs && FcCacheNumSubdir (cache) > 0)
233                 FcStrSetAdd (updateDirs, dir);
234         
235             FcDirCacheUnload (cache);
236         
237             sublist = FcStrListCreate (subdirs);
238             FcStrSetDestroy (subdirs);
239             if (!sublist)
240             {
241                 fprintf (stderr, "%s: Can't create subdir list\n", dir);
242                 ret++;
243                 continue;
244             }
245             FcStrSetAdd (processed_dirs, dir);
246             ret += scanDirs (sublist, config, force, really_force, verbose, recursive, changed, updateDirs);
247             FcStrListDone (sublist);
248         }
249         else
250             FcDirCacheUnload (cache);
251     }
252     return ret;
253 }
254
255 static FcBool
256 cleanCacheDirectories (FcConfig *config, FcBool verbose)
257 {
258     FcStrList   *cache_dirs = FcConfigGetCacheDirs (config);
259     FcChar8     *cache_dir;
260     FcBool      ret = FcTrue;
261
262     if (!cache_dirs)
263         return FcFalse;
264     while ((cache_dir = FcStrListNext (cache_dirs)))
265     {
266         if (!FcDirCacheClean (cache_dir, verbose))
267         {
268             ret = FcFalse;
269             break;
270         }
271     }
272     FcStrListDone (cache_dirs);
273     return ret;
274 }
275
276 int
277 main (int argc, char **argv)
278 {
279     FcStrSet    *dirs, *updateDirs;
280     FcStrList   *list;
281     FcBool      verbose = FcFalse;
282     FcBool      force = FcFalse;
283     FcBool      really_force = FcFalse;
284     FcBool      systemOnly = FcFalse;
285     FcConfig    *config;
286     FcChar8     *sysroot = NULL;
287     int         i;
288     int         changed;
289     int         ret;
290 #if HAVE_GETOPT_LONG || HAVE_GETOPT
291     int         c;
292
293 #if HAVE_GETOPT_LONG
294     while ((c = getopt_long (argc, argv, "frsy:Vvh", longopts, NULL)) != -1)
295 #else
296     while ((c = getopt (argc, argv, "frsy:Vvh")) != -1)
297 #endif
298     {
299         switch (c) {
300         case 'r':
301             really_force = FcTrue;
302             /* fall through */
303         case 'f':
304             force = FcTrue;
305             break;
306         case 's':
307             systemOnly = FcTrue;
308             break;
309         case 'y':
310             sysroot = FcStrCopy ((const FcChar8 *)optarg);
311             break;
312         case 'V':
313             fprintf (stderr, "fontconfig version %d.%d.%d\n", 
314                      FC_MAJOR, FC_MINOR, FC_REVISION);
315             exit (0);
316         case 'v':
317             verbose = FcTrue;
318             break;
319         case 'h':
320             usage (argv[0], 0);
321         default:
322             usage (argv[0], 1);
323         }
324     }
325     i = optind;
326 #else
327     i = 1;
328 #endif
329
330     if (systemOnly)
331         FcConfigEnableHome (FcFalse);
332     if (sysroot)
333     {
334         FcConfigSetSysRoot (NULL, sysroot);
335         FcStrFree (sysroot);
336         config = FcConfigGetCurrent();
337     }
338     else
339     {
340         config = FcInitLoadConfig ();
341     }
342     if (!config)
343     {
344         fprintf (stderr, "%s: Can't init font config library\n", argv[0]);
345         return 1;
346     }
347     FcConfigSetCurrent (config);
348
349     if (argv[i])
350     {
351         dirs = FcStrSetCreate ();
352         if (!dirs)
353         {
354             fprintf (stderr, "%s: Can't create list of directories\n",
355                      argv[0]);
356             return 1;
357         }
358         while (argv[i])
359         {
360             if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
361             {
362                 fprintf (stderr, "%s: Can't add directory\n", argv[0]);
363                 return 1;
364             }
365             i++;
366         }
367         list = FcStrListCreate (dirs);
368         FcStrSetDestroy (dirs);
369     }
370     else
371         list = FcConfigGetConfigDirs (config);
372
373     if ((processed_dirs = FcStrSetCreate()) == NULL) {
374         fprintf(stderr, "Cannot malloc\n");
375         return 1;
376     }
377
378     updateDirs = FcStrSetCreate ();
379     changed = 0;
380     ret = scanDirs (list, config, force, really_force, verbose, FcTrue, &changed, updateDirs);
381     /* Update the directory cache again to avoid the race condition as much as possible */
382     FcStrListDone (list);
383     list = FcStrListCreate (updateDirs);
384     if (list)
385     {
386         ret += scanDirs (list, config, FcTrue, really_force, verbose, FcFalse, &changed, NULL);
387         FcStrListDone (list);
388     }
389
390     /*
391      * Try to create CACHEDIR.TAG anyway.
392      * This expects the fontconfig cache directory already exists.
393      * If it doesn't, it won't be simply created.
394      */
395     FcCacheCreateTagFile (config);
396
397     FcStrSetDestroy (processed_dirs);
398
399     cleanCacheDirectories (config, verbose);
400
401     FcConfigDestroy (config);
402     FcFini ();
403     /* 
404      * Now we need to sleep a second  (or two, to be extra sure), to make
405      * sure that timestamps for changes after this run of fc-cache are later
406      * then any timestamps we wrote.  We don't use gettimeofday() because
407      * sleep(3) can't be interrupted by a signal here -- this isn't in the
408      * library, and there aren't any signals flying around here.
409      */
410     /* the resolution of mtime on FAT is 2 seconds */
411     if (changed)
412         sleep (2);
413     if (verbose)
414         printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded");
415     return ret;
416 }