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