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