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