Imported Upstream version 2.13.1
[platform/upstream/fontconfig.git] / src / fcdir.c
1 /*
2  * fontconfig/src/fcdir.c
3  *
4  * Copyright © 2000 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 #include "fcint.h"
26 #include <dirent.h>
27
28 FcBool
29 FcFileIsDir (const FcChar8 *file)
30 {
31     struct stat     statb;
32
33     if (FcStat (file, &statb) != 0)
34         return FcFalse;
35     return S_ISDIR(statb.st_mode);
36 }
37
38 FcBool
39 FcFileIsLink (const FcChar8 *file)
40 {
41 #if HAVE_LSTAT
42     struct stat statb;
43
44     if (lstat ((const char *)file, &statb) != 0)
45         return FcFalse;
46     return S_ISLNK (statb.st_mode);
47 #else
48     return FcFalse;
49 #endif
50 }
51
52 FcBool
53 FcFileIsFile (const FcChar8 *file)
54 {
55     struct stat statb;
56
57     if (FcStat (file, &statb) != 0)
58         return FcFalse;
59     return S_ISREG (statb.st_mode);
60 }
61
62 static FcBool
63 FcFileScanFontConfig (FcFontSet         *set,
64                       const FcChar8     *file,
65                       FcConfig          *config)
66 {
67     int         i;
68     FcBool      ret = FcTrue;
69     int         old_nfont = set->nfont;
70     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
71
72     if (FcDebug () & FC_DBG_SCAN)
73     {
74         printf ("\tScanning file %s...", file);
75         fflush (stdout);
76     }
77
78     if (!FcFreeTypeQueryAll (file, -1, NULL, NULL, set))
79         return FcFalse;
80
81     if (FcDebug () & FC_DBG_SCAN)
82         printf ("done\n");
83
84     for (i = old_nfont; i < set->nfont; i++)
85     {
86         FcPattern *font = set->fonts[i];
87
88         /*
89          * Get rid of sysroot here so that targeting scan rule may contains FC_FILE pattern
90          * and they should usually expect without sysroot.
91          */
92         if (sysroot)
93         {
94             size_t len = strlen ((const char *)sysroot);
95             FcChar8 *f = NULL;
96
97             if (FcPatternObjectGetString (font, FC_FILE_OBJECT, 0, &f) == FcResultMatch &&
98                 strncmp ((const char *)f, (const char *)sysroot, len) == 0)
99             {
100                 FcChar8 *s = FcStrdup (f);
101                 FcPatternObjectDel (font, FC_FILE_OBJECT);
102                 if (s[len] != '/')
103                     len--;
104                 else if (s[len+1] == '/')
105                     len++;
106                 FcPatternObjectAddString (font, FC_FILE_OBJECT, &s[len]);
107                 FcStrFree (s);
108             }
109         }
110
111         /*
112          * Edit pattern with user-defined rules
113          */
114         if (config && !FcConfigSubstitute (config, font, FcMatchScan))
115             ret = FcFalse;
116
117         if (FcDebug() & FC_DBG_SCANV)
118         {
119             printf ("Final font pattern:\n");
120             FcPatternPrint (font);
121         }
122     }
123
124     return ret;
125 }
126
127 FcBool
128 FcFileScanConfig (FcFontSet     *set,
129                   FcStrSet      *dirs,
130                   const FcChar8 *file,
131                   FcConfig      *config)
132 {
133     if (FcFileIsDir (file))
134     {
135         const FcChar8 *sysroot = FcConfigGetSysRoot (config);
136         const FcChar8 *d = file;
137         size_t len;
138
139         if (sysroot)
140         {
141                 len = strlen ((const char *)sysroot);
142                 if (strncmp ((const char *)file, (const char *)sysroot, len) == 0)
143                 {
144                         if (file[len] != '/')
145                                 len--;
146                         else if (file[len+1] == '/')
147                                 len++;
148                         d = &file[len];
149                 }
150         }
151         return FcStrSetAdd (dirs, d);
152     }
153     else
154     {
155         if (set)
156             return FcFileScanFontConfig (set, file, config);
157         else
158             return FcTrue;
159     }
160 }
161
162 FcBool
163 FcFileScan (FcFontSet       *set,
164             FcStrSet        *dirs,
165             FcFileCache     *cache FC_UNUSED,
166             FcBlanks        *blanks FC_UNUSED,
167             const FcChar8   *file,
168             FcBool          force FC_UNUSED)
169 {
170     return FcFileScanConfig (set, dirs, file, FcConfigGetCurrent ());
171 }
172
173 /*
174  * Strcmp helper that takes pointers to pointers, copied from qsort(3) manpage
175  */
176 static int
177 cmpstringp(const void *p1, const void *p2)
178 {
179     return strcmp(* (char **) p1, * (char **) p2);
180 }
181
182 FcBool
183 FcDirScanConfig (FcFontSet      *set,
184                  FcStrSet       *dirs,
185                  const FcChar8  *dir,
186                  FcBool         force, /* XXX unused */
187                  FcConfig       *config)
188 {
189     DIR                 *d;
190     struct dirent       *e;
191     FcStrSet            *files;
192     FcChar8             *file;
193     FcChar8             *base;
194     FcBool              ret = FcTrue;
195     int                 i;
196
197     if (!force)
198         return FcFalse;
199
200     if (!set && !dirs)
201         return FcTrue;
202
203     /* freed below */
204     file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
205     if (!file) {
206         ret = FcFalse;
207         goto bail;
208     }
209
210     strcpy ((char *) file, (char *) dir);
211     strcat ((char *) file, "/");
212     base = file + strlen ((char *) file);
213
214     if (FcDebug () & FC_DBG_SCAN)
215         printf ("\tScanning dir %s\n", dir);
216         
217     d = opendir ((char *) dir);
218     if (!d)
219     {
220         /* Don't complain about missing directories */
221         if (errno != ENOENT)
222             ret = FcFalse;
223         goto bail;
224     }
225
226     files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
227     if (!files)
228     {
229         ret = FcFalse;
230         goto bail1;
231     }
232     while ((e = readdir (d)))
233     {
234         if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN)
235         {
236             strcpy ((char *) base, (char *) e->d_name);
237             if (!FcStrSetAdd (files, file)) {
238                 ret = FcFalse;
239                 goto bail2;
240             }
241         }
242     }
243
244     /*
245      * Sort files to make things prettier
246      */
247     qsort(files->strs, files->num, sizeof(FcChar8 *), cmpstringp);
248
249     /*
250      * Scan file files to build font patterns
251      */
252     for (i = 0; i < files->num; i++)
253         FcFileScanConfig (set, dirs, files->strs[i], config);
254
255 bail2:
256     FcStrSetDestroy (files);
257 bail1:
258     closedir (d);
259 bail:
260     if (file)
261         free (file);
262
263     return ret;
264 }
265
266 FcBool
267 FcDirScan (FcFontSet        *set,
268            FcStrSet         *dirs,
269            FcFileCache      *cache FC_UNUSED,
270            FcBlanks         *blanks FC_UNUSED,
271            const FcChar8    *dir,
272            FcBool           force FC_UNUSED)
273 {
274     if (cache || !force)
275         return FcFalse;
276
277     return FcDirScanConfig (set, dirs, dir, force, FcConfigGetCurrent ());
278 }
279
280 /*
281  * Scan the specified directory and construct a cache of its contents
282  */
283 FcCache *
284 FcDirCacheScan (const FcChar8 *dir, FcConfig *config)
285 {
286     FcStrSet            *dirs;
287     FcFontSet           *set;
288     FcCache             *cache = NULL;
289     struct stat         dir_stat;
290     const FcChar8       *sysroot = FcConfigGetSysRoot (config);
291     FcChar8             *d;
292 #ifndef _WIN32
293     int                 fd = -1;
294 #endif
295
296     if (sysroot)
297         d = FcStrBuildFilename (sysroot, dir, NULL);
298     else
299         d = FcStrdup (dir);
300
301     if (FcDebug () & FC_DBG_FONTSET)
302         printf ("cache scan dir %s\n", d);
303
304     if (FcStatChecksum (d, &dir_stat) < 0)
305         goto bail;
306
307     set = FcFontSetCreate();
308     if (!set)
309         goto bail;
310
311     dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
312     if (!dirs)
313         goto bail1;
314
315 #ifndef _WIN32
316     fd = FcDirCacheLock (dir, config);
317 #endif
318     /*
319      * Scan the dir
320      */
321     if (!FcDirScanConfig (set, dirs, d, FcTrue, config))
322         goto bail2;
323
324     /*
325      * Build the cache object
326      */
327     cache = FcDirCacheBuild (set, dir, &dir_stat, dirs);
328     if (!cache)
329         goto bail2;
330
331     /*
332      * Write out the cache file, ignoring any troubles
333      */
334     FcDirCacheWrite (cache, config);
335
336  bail2:
337 #ifndef _WIN32
338     FcDirCacheUnlock (fd);
339 #endif
340     FcStrSetDestroy (dirs);
341  bail1:
342     FcFontSetDestroy (set);
343  bail:
344     FcStrFree (d);
345
346     return cache;
347 }
348
349 FcCache *
350 FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
351 {
352     FcCache *cache;
353     FcCache *new = NULL;
354     struct stat dir_stat;
355     FcStrSet *dirs;
356     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
357     FcChar8 *d = NULL;
358 #ifndef _WIN32
359     int fd = -1;
360 #endif
361
362     cache = FcDirCacheLoad (dir, config, NULL);
363     if (!cache)
364         goto bail;
365
366     if (sysroot)
367         d = FcStrBuildFilename (sysroot, dir, NULL);
368     else
369         d = FcStrdup (dir);
370     if (FcStatChecksum (d, &dir_stat) < 0)
371         goto bail;
372     dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
373     if (!dirs)
374         goto bail;
375
376 #ifndef _WIN32
377     fd = FcDirCacheLock (dir, config);
378 #endif
379     /*
380      * Scan the dir
381      */
382     if (!FcDirScanConfig (NULL, dirs, d, FcTrue, config))
383         goto bail1;
384     /*
385      * Rebuild the cache object
386      */
387     new = FcDirCacheRebuild (cache, &dir_stat, dirs);
388     if (!new)
389         goto bail1;
390     FcDirCacheUnload (cache);
391     /*
392      * Write out the cache file, ignoring any troubles
393      */
394     FcDirCacheWrite (new, config);
395
396 bail1:
397 #ifndef _WIN32
398     FcDirCacheUnlock (fd);
399 #endif
400     FcStrSetDestroy (dirs);
401 bail:
402     if (d)
403         FcStrFree (d);
404
405     return new;
406 }
407
408 /*
409  * Read (or construct) the cache for a directory
410  */
411 FcCache *
412 FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
413 {
414     FcCache             *cache = NULL;
415
416     FcDirCacheCreateUUID ((FcChar8 *) dir, FcFalse, config);
417     /* Try to use existing cache file */
418     if (!force)
419         cache = FcDirCacheLoad (dir, config, NULL);
420
421     /* Not using existing cache file, construct new cache */
422     if (!cache)
423         cache = FcDirCacheScan (dir, config);
424     if (cache)
425     {
426         FcFontSet *fs = FcCacheSet (cache);
427
428         if (cache->dirs_count == 0 && (!fs || fs->nfont == 0))
429             FcDirCacheDeleteUUID (dir, config);
430     }
431
432     return cache;
433 }
434
435 FcBool
436 FcDirSave (FcFontSet *set FC_UNUSED, FcStrSet * dirs FC_UNUSED, const FcChar8 *dir FC_UNUSED)
437 {
438     return FcFalse; /* XXX deprecated */
439 }
440 #define __fcdir__
441 #include "fcaliastail.h"
442 #undef __fcdir__