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