fcdir.c revision 37d6d98f
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
28FcBool
29FcFileIsDir (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
38FcBool
39FcFileIsLink (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
52FcBool
53FcFileIsFile (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
62static FcBool
63FcFileScanFontConfig (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
127FcBool
128FcFileScanConfig (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
162FcBool
163FcFileScan (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 */
176static int
177cmpstringp(const void *p1, const void *p2)
178{
179    return strcmp(* (char **) p1, * (char **) p2);
180}
181
182FcBool
183FcDirScanConfig (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
255bail2:
256    FcStrSetDestroy (files);
257bail1:
258    closedir (d);
259bail:
260    if (file)
261	free (file);
262
263    return ret;
264}
265
266FcBool
267FcDirScan (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 */
283FcCache *
284FcDirCacheScan (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
349FcCache *
350FcDirCacheRescan (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
396bail1:
397#ifndef _WIN32
398    FcDirCacheUnlock (fd);
399#endif
400    FcStrSetDestroy (dirs);
401bail:
402    if (d)
403	FcStrFree (d);
404
405    return new;
406}
407
408/*
409 * Read (or construct) the cache for a directory
410 */
411FcCache *
412FcDirCacheRead (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
425    return cache;
426}
427
428FcBool
429FcDirSave (FcFontSet *set FC_UNUSED, FcStrSet * dirs FC_UNUSED, const FcChar8 *dir FC_UNUSED)
430{
431    return FcFalse; /* XXX deprecated */
432}
433#define __fcdir__
434#include "fcaliastail.h"
435#undef __fcdir__
436