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