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 31FcBool 32FcFileIsDir (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 41FcBool 42FcFileIsLink (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 55FcBool 56FcFileIsFile (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 65static FcBool 66FcFileScanFontConfig (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 130FcBool 131FcFileScanConfig (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 165FcBool 166FcFileScan (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 */ 188static int 189cmpstringp(const void *p1, const void *p2) 190{ 191 return strcmp(* (char **) p1, * (char **) p2); 192} 193 194FcBool 195FcDirScanConfig (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 = NULL, *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 if (sysroot) 217 s_dir = FcStrBuildFilename (sysroot, dir, NULL); 218 else 219 s_dir = FcStrdup (dir); 220 if (!s_dir) { 221 ret = FcFalse; 222 goto bail; 223 } 224 225 /* freed below */ 226 file_prefix = (FcChar8 *) malloc (strlen ((char *) s_dir) + 1 + FC_MAX_FILE_LEN + 1); 227 if (!file_prefix) { 228 ret = FcFalse; 229 goto bail; 230 } 231 strcpy ((char *) file_prefix, (char *) s_dir); 232 strcat ((char *) file_prefix, FC_DIR_SEPARATOR_S); 233 base = file_prefix + strlen ((char *) file_prefix); 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 276bail2: 277 FcStrSetDestroy (files); 278bail1: 279 closedir (d); 280bail: 281 if (s_dir) 282 free (s_dir); 283 if (file_prefix) 284 free (file_prefix); 285 286 return ret; 287} 288 289FcBool 290FcDirScan (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 */ 315FcCache * 316FcDirCacheScan (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 382FcCache * 383FcDirCacheRescan (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 434bail1: 435#ifndef _WIN32 436 FcDirCacheUnlock (fd); 437#endif 438 FcStrSetDestroy (dirs); 439bail: 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 */ 450FcCache * 451FcDirCacheRead (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 468FcBool 469FcDirSave (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