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
FcFileIsDir(const FcChar8 * file)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
FcFileIsLink(const FcChar8 * file)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
FcFileIsFile(const FcChar8 * file)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
FcFileScanFontConfig(FcFontSet * set,const FcChar8 * file,FcConfig * config)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
FcFileScanConfig(FcFontSet * set,FcStrSet * dirs,const FcChar8 * file,FcConfig * config)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
FcFileScan(FcFontSet * set,FcStrSet * dirs,FcFileCache * cache FC_UNUSED,FcBlanks * blanks FC_UNUSED,const FcChar8 * file,FcBool force FC_UNUSED)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     FcConfig *config;
171     FcBool ret;
172 
173     config = FcConfigReference (NULL);
174     if (!config)
175 	return FcFalse;
176     ret = FcFileScanConfig (set, dirs, file, config);
177     FcConfigDestroy (config);
178 
179     return ret;
180 }
181 
182 /*
183  * Strcmp helper that takes pointers to pointers, copied from qsort(3) manpage
184  */
185 static int
cmpstringp(const void * p1,const void * p2)186 cmpstringp(const void *p1, const void *p2)
187 {
188     return strcmp(* (char **) p1, * (char **) p2);
189 }
190 
191 FcBool
FcDirScanConfig(FcFontSet * set,FcStrSet * dirs,const FcChar8 * dir,FcBool force,FcConfig * config)192 FcDirScanConfig (FcFontSet	*set,
193 		 FcStrSet	*dirs,
194 		 const FcChar8	*dir,
195 		 FcBool		force, /* XXX unused */
196 		 FcConfig	*config)
197 {
198     DIR			*d;
199     struct dirent	*e;
200     FcStrSet		*files;
201     FcChar8		*file;
202     FcChar8		*base;
203     FcBool		ret = FcTrue;
204     int			i;
205 
206     if (!force)
207 	return FcFalse;
208 
209     if (!set && !dirs)
210 	return FcTrue;
211 
212     /* freed below */
213     file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
214     if (!file) {
215 	ret = FcFalse;
216 	goto bail;
217     }
218 
219     strcpy ((char *) file, (char *) dir);
220     strcat ((char *) file, "/");
221     base = file + strlen ((char *) file);
222 
223     if (FcDebug () & FC_DBG_SCAN)
224 	printf ("\tScanning dir %s\n", dir);
225 
226     d = opendir ((char *) dir);
227     if (!d)
228     {
229 	/* Don't complain about missing directories */
230 	if (errno != ENOENT)
231 	    ret = FcFalse;
232 	goto bail;
233     }
234 
235     files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
236     if (!files)
237     {
238 	ret = FcFalse;
239 	goto bail1;
240     }
241     while ((e = readdir (d)))
242     {
243 	if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN)
244 	{
245 	    strcpy ((char *) base, (char *) e->d_name);
246 	    if (!FcStrSetAdd (files, file)) {
247 		ret = FcFalse;
248 		goto bail2;
249 	    }
250 	}
251     }
252 
253     /*
254      * Sort files to make things prettier
255      */
256     qsort(files->strs, files->num, sizeof(FcChar8 *), cmpstringp);
257 
258     /*
259      * Scan file files to build font patterns
260      */
261     for (i = 0; i < files->num; i++)
262 	FcFileScanConfig (set, dirs, files->strs[i], config);
263 
264 bail2:
265     FcStrSetDestroy (files);
266 bail1:
267     closedir (d);
268 bail:
269     if (file)
270 	free (file);
271 
272     return ret;
273 }
274 
275 FcBool
FcDirScan(FcFontSet * set,FcStrSet * dirs,FcFileCache * cache FC_UNUSED,FcBlanks * blanks FC_UNUSED,const FcChar8 * dir,FcBool force FC_UNUSED)276 FcDirScan (FcFontSet	    *set,
277 	   FcStrSet	    *dirs,
278 	   FcFileCache	    *cache FC_UNUSED,
279 	   FcBlanks	    *blanks FC_UNUSED,
280 	   const FcChar8    *dir,
281 	   FcBool	    force FC_UNUSED)
282 {
283     FcConfig *config;
284     FcBool ret;
285 
286     if (cache || !force)
287 	return FcFalse;
288 
289     config = FcConfigReference (NULL);
290     if (!config)
291 	return FcFalse;
292     ret = FcDirScanConfig (set, dirs, dir, force, config);
293     FcConfigDestroy (config);
294 
295     return ret;
296 }
297 
298 /*
299  * Scan the specified directory and construct a cache of its contents
300  */
301 FcCache *
FcDirCacheScan(const FcChar8 * dir,FcConfig * config)302 FcDirCacheScan (const FcChar8 *dir, FcConfig *config)
303 {
304     FcStrSet		*dirs;
305     FcFontSet		*set;
306     FcCache		*cache = NULL;
307     struct stat		dir_stat;
308     const FcChar8	*sysroot = FcConfigGetSysRoot (config);
309     FcChar8		*d;
310 #ifndef _WIN32
311     int			fd = -1;
312 #endif
313 
314     if (sysroot)
315 	d = FcStrBuildFilename (sysroot, dir, NULL);
316     else
317 	d = FcStrdup (dir);
318 
319     if (FcDebug () & FC_DBG_FONTSET)
320 	printf ("cache scan dir %s\n", d);
321 
322     if (FcStatChecksum (d, &dir_stat) < 0)
323 	goto bail;
324 
325     set = FcFontSetCreate();
326     if (!set)
327 	goto bail;
328 
329     dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
330     if (!dirs)
331 	goto bail1;
332 
333 #ifndef _WIN32
334     fd = FcDirCacheLock (dir, config);
335 #endif
336     /*
337      * Scan the dir
338      */
339     if (!FcDirScanConfig (set, dirs, d, FcTrue, config))
340 	goto bail2;
341 
342     /*
343      * Build the cache object
344      */
345     cache = FcDirCacheBuild (set, dir, &dir_stat, dirs);
346     if (!cache)
347 	goto bail2;
348 
349     /*
350      * Write out the cache file, ignoring any troubles
351      */
352     FcDirCacheWrite (cache, config);
353 
354  bail2:
355 #ifndef _WIN32
356     FcDirCacheUnlock (fd);
357 #endif
358     FcStrSetDestroy (dirs);
359  bail1:
360     FcFontSetDestroy (set);
361  bail:
362     FcStrFree (d);
363 
364     return cache;
365 }
366 
367 FcCache *
FcDirCacheRescan(const FcChar8 * dir,FcConfig * config)368 FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
369 {
370     FcCache *cache;
371     FcCache *new = NULL;
372     struct stat dir_stat;
373     FcStrSet *dirs;
374     const FcChar8 *sysroot;
375     FcChar8 *d = NULL;
376 #ifndef _WIN32
377     int fd = -1;
378 #endif
379 
380     config = FcConfigReference (config);
381     if (!config)
382 	return NULL;
383     sysroot = FcConfigGetSysRoot (config);
384     cache = FcDirCacheLoad (dir, config, NULL);
385     if (!cache)
386 	goto bail;
387 
388     if (sysroot)
389 	d = FcStrBuildFilename (sysroot, dir, NULL);
390     else
391 	d = FcStrdup (dir);
392     if (FcStatChecksum (d, &dir_stat) < 0)
393 	goto bail;
394     dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
395     if (!dirs)
396 	goto bail;
397 
398 #ifndef _WIN32
399     fd = FcDirCacheLock (dir, config);
400 #endif
401     /*
402      * Scan the dir
403      */
404     if (!FcDirScanConfig (NULL, dirs, d, FcTrue, config))
405 	goto bail1;
406     /*
407      * Rebuild the cache object
408      */
409     new = FcDirCacheRebuild (cache, &dir_stat, dirs);
410     if (!new)
411 	goto bail1;
412     FcDirCacheUnload (cache);
413     /*
414      * Write out the cache file, ignoring any troubles
415      */
416     FcDirCacheWrite (new, config);
417 
418 bail1:
419 #ifndef _WIN32
420     FcDirCacheUnlock (fd);
421 #endif
422     FcStrSetDestroy (dirs);
423 bail:
424     if (d)
425 	FcStrFree (d);
426     FcConfigDestroy (config);
427 
428     return new;
429 }
430 
431 /*
432  * Read (or construct) the cache for a directory
433  */
434 FcCache *
FcDirCacheRead(const FcChar8 * dir,FcBool force,FcConfig * config)435 FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
436 {
437     FcCache		*cache = NULL;
438 
439     config = FcConfigReference (config);
440     /* Try to use existing cache file */
441     if (!force)
442 	cache = FcDirCacheLoad (dir, config, NULL);
443 
444     /* Not using existing cache file, construct new cache */
445     if (!cache)
446 	cache = FcDirCacheScan (dir, config);
447     FcConfigDestroy (config);
448 
449     return cache;
450 }
451 
452 FcBool
FcDirSave(FcFontSet * set FC_UNUSED,FcStrSet * dirs FC_UNUSED,const FcChar8 * dir FC_UNUSED)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__
460