Bump version to 2.10.93
[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, 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 (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         subdirs = FcStrSetCreate ();
218         if (!subdirs)
219         {
220             fprintf (stderr, "%s: Can't create subdir set\n", dir);
221             ret++;
222             FcDirCacheUnload (cache);
223             continue;
224         }
225         for (i = 0; i < FcCacheNumSubdir (cache); i++)
226             FcStrSetAdd (subdirs, FcCacheSubdir (cache, i));
227         
228         FcDirCacheUnload (cache);
229         
230         sublist = FcStrListCreate (subdirs);
231         FcStrSetDestroy (subdirs);
232         if (!sublist)
233         {
234             fprintf (stderr, "%s: Can't create subdir list\n", dir);
235             ret++;
236             continue;
237         }
238         FcStrSetAdd (processed_dirs, dir);
239         ret += scanDirs (sublist, config, force, really_force, verbose, changed);
240     }
241     FcStrListDone (list);
242     return ret;
243 }
244
245 static FcBool
246 cleanCacheDirectories (FcConfig *config, FcBool verbose)
247 {
248     FcStrList   *cache_dirs = FcConfigGetCacheDirs (config);
249     FcChar8     *cache_dir;
250     FcBool      ret = FcTrue;
251
252     if (!cache_dirs)
253         return FcFalse;
254     while ((cache_dir = FcStrListNext (cache_dirs)))
255     {
256         if (!FcDirCacheClean (cache_dir, verbose))
257         {
258             ret = FcFalse;
259             break;
260         }
261     }
262     FcStrListDone (cache_dirs);
263     return ret;
264 }
265
266 int
267 main (int argc, char **argv)
268 {
269     FcStrSet    *dirs;
270     FcStrList   *list;
271     FcBool      verbose = FcFalse;
272     FcBool      force = FcFalse;
273     FcBool      really_force = FcFalse;
274     FcBool      systemOnly = FcFalse;
275     FcConfig    *config;
276     FcChar8     *sysroot = NULL;
277     int         i;
278     int         changed;
279     int         ret;
280 #if HAVE_GETOPT_LONG || HAVE_GETOPT
281     int         c;
282
283 #if HAVE_GETOPT_LONG
284     while ((c = getopt_long (argc, argv, "frsy:Vvh", longopts, NULL)) != -1)
285 #else
286     while ((c = getopt (argc, argv, "frsy:Vvh")) != -1)
287 #endif
288     {
289         switch (c) {
290         case 'r':
291             really_force = FcTrue;
292             /* fall through */
293         case 'f':
294             force = FcTrue;
295             break;
296         case 's':
297             systemOnly = FcTrue;
298             break;
299         case 'y':
300             sysroot = FcStrCopy ((const FcChar8 *)optarg);
301             break;
302         case 'V':
303             fprintf (stderr, "fontconfig version %d.%d.%d\n", 
304                      FC_MAJOR, FC_MINOR, FC_REVISION);
305             exit (0);
306         case 'v':
307             verbose = FcTrue;
308             break;
309         case 'h':
310             usage (argv[0], 0);
311         default:
312             usage (argv[0], 1);
313         }
314     }
315     i = optind;
316 #else
317     i = 1;
318 #endif
319
320     if (systemOnly)
321         FcConfigEnableHome (FcFalse);
322     if (sysroot)
323     {
324         FcConfigSetSysRoot (NULL, sysroot);
325         FcStrFree (sysroot);
326         config = FcConfigGetCurrent();
327     }
328     else
329     {
330         config = FcInitLoadConfig ();
331     }
332     if (!config)
333     {
334         fprintf (stderr, "%s: Can't init font config library\n", argv[0]);
335         return 1;
336     }
337     FcConfigSetCurrent (config);
338
339     if (argv[i])
340     {
341         dirs = FcStrSetCreate ();
342         if (!dirs)
343         {
344             fprintf (stderr, "%s: Can't create list of directories\n",
345                      argv[0]);
346             return 1;
347         }
348         while (argv[i])
349         {
350             if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
351             {
352                 fprintf (stderr, "%s: Can't add directory\n", argv[0]);
353                 return 1;
354             }
355             i++;
356         }
357         list = FcStrListCreate (dirs);
358         FcStrSetDestroy (dirs);
359     }
360     else
361         list = FcConfigGetConfigDirs (config);
362
363     if ((processed_dirs = FcStrSetCreate()) == NULL) {
364         fprintf(stderr, "Cannot malloc\n");
365         return 1;
366     }
367         
368     changed = 0;
369     ret = scanDirs (list, config, force, really_force, verbose, &changed);
370
371     /*
372      * Try to create CACHEDIR.TAG anyway.
373      * This expects the fontconfig cache directory already exists.
374      * If it doesn't, it won't be simply created.
375      */
376     FcCacheCreateTagFile (config);
377
378     FcStrSetDestroy (processed_dirs);
379
380     cleanCacheDirectories (config, verbose);
381
382     /* 
383      * Now we need to sleep a second  (or two, to be extra sure), to make
384      * sure that timestamps for changes after this run of fc-cache are later
385      * then any timestamps we wrote.  We don't use gettimeofday() because
386      * sleep(3) can't be interrupted by a signal here -- this isn't in the
387      * library, and there aren't any signals flying around here.
388      */
389     FcConfigDestroy (config);
390     FcFini ();
391     if (changed)
392         sleep (2);
393     if (verbose)
394         printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded");
395     return ret;
396 }