Imported Upstream version 2.12.1
[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     {"error-on-no-fonts", 0, 0, 'E'},
69     {"force", 0, 0, 'f'},
70     {"really-force", 0, 0, 'r'},
71     {"sysroot", required_argument, 0, 'y'},
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 [-EfrsvVh] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n",
91              program);
92 #else
93     fprintf (file, "usage: %s [-EfrsvVh] [-y SYSROOT] [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, "  -E, --error-on-no-fonts  raise an error if no fonts in a directory\n");
101     fprintf (file, "  -f, --force              scan directories with apparently valid caches\n");
102     fprintf (file, "  -r, --really-force       erase all existing caches, then rescan\n");
103     fprintf (file, "  -s, --system-only        scan system-wide directories only\n");
104     fprintf (file, "  -y, --sysroot=SYSROOT    prepend SYSROOT to all paths for scanning\n");
105     fprintf (file, "  -v, --verbose            display status information while busy\n");
106     fprintf (file, "  -V, --version            display font config version and exit\n");
107     fprintf (file, "  -h, --help               display this help and exit\n");
108 #else
109     fprintf (file, "  -E         (error-on-no-fonts)\n");
110     fprintf (file, "                       raise an error if no fonts in a directory\n");
111     fprintf (file, "  -f         (force)   scan directories with apparently valid caches\n");
112     fprintf (file, "  -r,   (really force) erase all existing caches, then rescan\n");
113     fprintf (file, "  -s         (system)  scan system-wide directories only\n");
114     fprintf (file, "  -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n");
115     fprintf (file, "  -v         (verbose) display status information while busy\n");
116     fprintf (file, "  -V         (version) display font config version and exit\n");
117     fprintf (file, "  -h         (help)    display this help and exit\n");
118 #endif
119     exit (error);
120 }
121
122 static FcStrSet *processed_dirs;
123
124 static int
125 scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed)
126 {
127     int             ret = 0;
128     const FcChar8   *dir;
129     FcStrSet        *subdirs;
130     FcStrList       *sublist;
131     FcCache         *cache;
132     struct stat     statb;
133     FcBool          was_valid, was_processed = FcFalse;
134     int             i;
135     const FcChar8   *sysroot = FcConfigGetSysRoot (config);
136
137     /*
138      * Now scan all of the directories into separate databases
139      * and write out the results
140      */
141     while ((dir = FcStrListNext (list)))
142     {
143         if (verbose)
144         {
145             if (sysroot)
146                 printf ("[%s]", sysroot);
147             printf ("%s: ", dir);
148             fflush (stdout);
149         }
150         
151         if (FcStrSetMember (processed_dirs, dir))
152         {
153             if (verbose)
154                 printf ("skipping, looped directory detected\n");
155             continue;
156         }
157
158         if (stat ((char *) dir, &statb) == -1)
159         {
160             switch (errno) {
161             case ENOENT:
162             case ENOTDIR:
163                 if (verbose)
164                     printf ("skipping, no such directory\n");
165                 break;
166             default:
167                 fprintf (stderr, "\"%s\": ", dir);
168                 perror ("");
169                 ret++;
170                 break;
171             }
172             continue;
173         }
174
175         if (!S_ISDIR (statb.st_mode))
176         {
177             fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
178             continue;
179         }
180         was_processed = FcTrue;
181
182         if (really_force)
183             FcDirCacheUnlink (dir, config);
184
185         cache = NULL;
186         was_valid = FcFalse;
187         if (!force) {
188             cache = FcDirCacheLoad (dir, config, NULL);
189             if (cache)
190                 was_valid = FcTrue;
191         }
192         
193         if (!cache)
194         {
195             (*changed)++;
196             cache = FcDirCacheRead (dir, FcTrue, config);
197             if (!cache)
198             {
199                 fprintf (stderr, "%s: error scanning\n", dir);
200                 ret++;
201                 continue;
202             }
203         }
204
205         if (was_valid)
206         {
207             if (verbose)
208                 printf ("skipping, existing cache is valid: %d fonts, %d dirs\n",
209                         FcCacheNumFont (cache), FcCacheNumSubdir (cache));
210         }
211         else
212         {
213             if (verbose)
214                 printf ("caching, new cache contents: %d fonts, %d dirs\n", 
215                         FcCacheNumFont (cache), FcCacheNumSubdir (cache));
216
217             if (!FcDirCacheValid (dir))
218             {
219                 fprintf (stderr, "%s: failed to write cache\n", dir);
220                 (void) FcDirCacheUnlink (dir, config);
221                 ret++;
222             }
223         }
224
225         subdirs = FcStrSetCreate ();
226         if (!subdirs)
227         {
228             fprintf (stderr, "%s: Can't create subdir set\n", dir);
229             ret++;
230             FcDirCacheUnload (cache);
231             continue;
232         }
233         for (i = 0; i < FcCacheNumSubdir (cache); i++)
234             FcStrSetAdd (subdirs, FcCacheSubdir (cache, i));
235         
236         FcDirCacheUnload (cache);
237
238         sublist = FcStrListCreate (subdirs);
239         FcStrSetDestroy (subdirs);
240         if (!sublist)
241         {
242             fprintf (stderr, "%s: Can't create subdir list\n", dir);
243             ret++;
244             continue;
245         }
246         FcStrSetAdd (processed_dirs, dir);
247         ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed);
248         FcStrListDone (sublist);
249     }
250     if (error_on_no_fonts && !was_processed)
251         ret++;
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;
280     FcStrList   *list;
281     FcBool      verbose = FcFalse;
282     FcBool      force = FcFalse;
283     FcBool      really_force = FcFalse;
284     FcBool      systemOnly = FcFalse;
285     FcBool      error_on_no_fonts = FcFalse;
286     FcConfig    *config;
287     FcChar8     *sysroot = NULL;
288     int         i;
289     int         changed;
290     int         ret;
291 #if HAVE_GETOPT_LONG || HAVE_GETOPT
292     int         c;
293
294 #if HAVE_GETOPT_LONG
295     while ((c = getopt_long (argc, argv, "Efrsy:Vvh", longopts, NULL)) != -1)
296 #else
297     while ((c = getopt (argc, argv, "Efrsy:Vvh")) != -1)
298 #endif
299     {
300         switch (c) {
301         case 'E':
302             error_on_no_fonts = FcTrue;
303             break;
304         case 'r':
305             really_force = FcTrue;
306             /* fall through */
307         case 'f':
308             force = FcTrue;
309             break;
310         case 's':
311             systemOnly = FcTrue;
312             break;
313         case 'y':
314             sysroot = FcStrCopy ((const FcChar8 *)optarg);
315             break;
316         case 'V':
317             fprintf (stderr, "fontconfig version %d.%d.%d\n", 
318                      FC_MAJOR, FC_MINOR, FC_REVISION);
319             exit (0);
320         case 'v':
321             verbose = FcTrue;
322             break;
323         case 'h':
324             usage (argv[0], 0);
325         default:
326             usage (argv[0], 1);
327         }
328     }
329     i = optind;
330 #else
331     i = 1;
332 #endif
333
334     if (systemOnly)
335         FcConfigEnableHome (FcFalse);
336     if (sysroot)
337     {
338         FcConfigSetSysRoot (NULL, sysroot);
339         FcStrFree (sysroot);
340         config = FcConfigGetCurrent();
341     }
342     else
343     {
344         config = FcInitLoadConfig ();
345     }
346     if (!config)
347     {
348         fprintf (stderr, "%s: Can't init font config library\n", argv[0]);
349         return 1;
350     }
351     FcConfigSetCurrent (config);
352
353     if (argv[i])
354     {
355         dirs = FcStrSetCreate ();
356         if (!dirs)
357         {
358             fprintf (stderr, "%s: Can't create list of directories\n",
359                      argv[0]);
360             return 1;
361         }
362         while (argv[i])
363         {
364             if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
365             {
366                 fprintf (stderr, "%s: Can't add directory\n", argv[0]);
367                 return 1;
368             }
369             i++;
370         }
371         list = FcStrListCreate (dirs);
372         FcStrSetDestroy (dirs);
373     }
374     else
375         list = FcConfigGetConfigDirs (config);
376
377     if ((processed_dirs = FcStrSetCreate()) == NULL) {
378         fprintf(stderr, "Cannot malloc\n");
379         return 1;
380     }
381
382     changed = 0;
383     ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed);
384     FcStrListDone (list);
385
386     /*
387      * Try to create CACHEDIR.TAG anyway.
388      * This expects the fontconfig cache directory already exists.
389      * If it doesn't, it won't be simply created.
390      */
391     FcCacheCreateTagFile (config);
392
393     FcStrSetDestroy (processed_dirs);
394
395     cleanCacheDirectories (config, verbose);
396
397     FcConfigDestroy (config);
398     FcFini ();
399     /* 
400      * Now we need to sleep a second  (or two, to be extra sure), to make
401      * sure that timestamps for changes after this run of fc-cache are later
402      * then any timestamps we wrote.  We don't use gettimeofday() because
403      * sleep(3) can't be interrupted by a signal here -- this isn't in the
404      * library, and there aren't any signals flying around here.
405      */
406     /* the resolution of mtime on FAT is 2 seconds */
407     if (changed)
408         sleep (2);
409     if (verbose)
410         printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded");
411     return ret;
412 }