| /* |
| * fontconfig/src/fcdir.c |
| * |
| * Copyright © 2000 Keith Packard |
| * |
| * Permission to use, copy, modify, distribute, and sell this software and its |
| * documentation for any purpose is hereby granted without fee, provided that |
| * the above copyright notice appear in all copies and that both that |
| * copyright notice and this permission notice appear in supporting |
| * documentation, and that the name of the author(s) not be used in |
| * advertising or publicity pertaining to distribution of the software without |
| * specific, written prior permission. The authors make no |
| * representations about the suitability of this software for any purpose. It |
| * is provided "as is" without express or implied warranty. |
| * |
| * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "fcint.h" |
| |
| #ifdef HAVE_DIRENT_H |
| #include <dirent.h> |
| #endif |
| |
| FcBool |
| FcFileIsDir (const FcChar8 *file) |
| { |
| struct stat statb; |
| |
| if (FcStat (file, &statb) != 0) |
| return FcFalse; |
| return S_ISDIR(statb.st_mode); |
| } |
| |
| FcBool |
| FcFileIsLink (const FcChar8 *file) |
| { |
| #if HAVE_LSTAT |
| struct stat statb; |
| |
| if (lstat ((const char *)file, &statb) != 0) |
| return FcFalse; |
| return S_ISLNK (statb.st_mode); |
| #else |
| return FcFalse; |
| #endif |
| } |
| |
| FcBool |
| FcFileIsFile (const FcChar8 *file) |
| { |
| struct stat statb; |
| |
| if (FcStat (file, &statb) != 0) |
| return FcFalse; |
| return S_ISREG (statb.st_mode); |
| } |
| |
| static FcBool |
| FcFileScanFontConfig (FcFontSet *set, |
| const FcChar8 *file, |
| FcConfig *config) |
| { |
| int i; |
| FcBool ret = FcTrue; |
| int old_nfont = set->nfont; |
| const FcChar8 *sysroot = FcConfigGetSysRoot (config); |
| |
| if (FcDebug () & FC_DBG_SCAN) |
| { |
| printf ("\tScanning file %s...", file); |
| fflush (stdout); |
| } |
| |
| if (!FcFreeTypeQueryAll (file, -1, NULL, NULL, set)) |
| return FcFalse; |
| |
| if (FcDebug () & FC_DBG_SCAN) |
| printf ("done\n"); |
| |
| for (i = old_nfont; i < set->nfont; i++) |
| { |
| FcPattern *font = set->fonts[i]; |
| |
| /* |
| * Get rid of sysroot here so that targeting scan rule may contains FC_FILE pattern |
| * and they should usually expect without sysroot. |
| */ |
| if (sysroot) |
| { |
| size_t len = strlen ((const char *)sysroot); |
| FcChar8 *f = NULL; |
| |
| if (FcPatternObjectGetString (font, FC_FILE_OBJECT, 0, &f) == FcResultMatch && |
| strncmp ((const char *)f, (const char *)sysroot, len) == 0) |
| { |
| FcChar8 *s = FcStrdup (f); |
| FcPatternObjectDel (font, FC_FILE_OBJECT); |
| if (s[len] != '/') |
| len--; |
| else if (s[len+1] == '/') |
| len++; |
| FcPatternObjectAddString (font, FC_FILE_OBJECT, &s[len]); |
| FcStrFree (s); |
| } |
| } |
| |
| /* |
| * Edit pattern with user-defined rules |
| */ |
| if (config && !FcConfigSubstitute (config, font, FcMatchScan)) |
| ret = FcFalse; |
| |
| if (!FcPatternAddFullname (font)) |
| ret = FcFalse; |
| |
| if (FcDebug() & FC_DBG_SCANV) |
| { |
| printf ("Final font pattern:\n"); |
| FcPatternPrint (font); |
| } |
| } |
| |
| return ret; |
| } |
| |
| FcBool |
| FcFileScanConfig (FcFontSet *set, |
| FcStrSet *dirs, |
| const FcChar8 *file, |
| FcConfig *config) |
| { |
| if (FcFileIsDir (file)) |
| { |
| const FcChar8 *sysroot = FcConfigGetSysRoot (config); |
| const FcChar8 *d = file; |
| size_t len; |
| |
| if (sysroot) |
| { |
| len = strlen ((const char *)sysroot); |
| if (strncmp ((const char *)file, (const char *)sysroot, len) == 0) |
| { |
| if (file[len] != '/') |
| len--; |
| else if (file[len+1] == '/') |
| len++; |
| d = &file[len]; |
| } |
| } |
| return FcStrSetAdd (dirs, d); |
| } |
| else |
| { |
| if (set) |
| return FcFileScanFontConfig (set, file, config); |
| else |
| return FcTrue; |
| } |
| } |
| |
| FcBool |
| FcFileScan (FcFontSet *set, |
| FcStrSet *dirs, |
| FcFileCache *cache FC_UNUSED, |
| FcBlanks *blanks FC_UNUSED, |
| const FcChar8 *file, |
| FcBool force FC_UNUSED) |
| { |
| FcConfig *config; |
| FcBool ret; |
| |
| config = FcConfigReference (NULL); |
| if (!config) |
| return FcFalse; |
| ret = FcFileScanConfig (set, dirs, file, config); |
| FcConfigDestroy (config); |
| |
| return ret; |
| } |
| |
| /* |
| * Strcmp helper that takes pointers to pointers, copied from qsort(3) manpage |
| */ |
| static int |
| cmpstringp(const void *p1, const void *p2) |
| { |
| return strcmp(* (char **) p1, * (char **) p2); |
| } |
| |
| FcBool |
| FcDirScanConfig (FcFontSet *set, |
| FcStrSet *dirs, |
| const FcChar8 *dir, |
| FcBool force, /* XXX unused */ |
| FcConfig *config) |
| { |
| DIR *d; |
| struct dirent *e; |
| FcStrSet *files; |
| FcChar8 *file_prefix, *s_dir = NULL; |
| FcChar8 *base; |
| const FcChar8 *sysroot = FcConfigGetSysRoot (config); |
| FcBool ret = FcTrue; |
| int i; |
| |
| if (!force) |
| return FcFalse; |
| |
| if (!set && !dirs) |
| return FcTrue; |
| |
| /* freed below */ |
| file_prefix = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1); |
| if (!file_prefix) { |
| ret = FcFalse; |
| goto bail; |
| } |
| strcpy ((char *) file_prefix, (char *) dir); |
| strcat ((char *) file_prefix, FC_DIR_SEPARATOR_S); |
| base = file_prefix + strlen ((char *) file_prefix); |
| |
| if (sysroot) |
| s_dir = FcStrBuildFilename (sysroot, dir, NULL); |
| else |
| s_dir = FcStrdup (dir); |
| if (!s_dir) { |
| ret = FcFalse; |
| goto bail; |
| } |
| |
| if (FcDebug () & FC_DBG_SCAN) |
| printf ("\tScanning dir %s\n", s_dir); |
| |
| d = opendir ((char *) s_dir); |
| if (!d) |
| { |
| /* Don't complain about missing directories */ |
| if (errno != ENOENT) |
| ret = FcFalse; |
| goto bail; |
| } |
| |
| files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64); |
| if (!files) |
| { |
| ret = FcFalse; |
| goto bail1; |
| } |
| while ((e = readdir (d))) |
| { |
| if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN) |
| { |
| strcpy ((char *) base, (char *) e->d_name); |
| if (!FcStrSetAdd (files, file_prefix)) { |
| ret = FcFalse; |
| goto bail2; |
| } |
| } |
| } |
| |
| /* |
| * Sort files to make things prettier |
| */ |
| qsort(files->strs, files->num, sizeof(FcChar8 *), cmpstringp); |
| |
| /* |
| * Scan file files to build font patterns |
| */ |
| for (i = 0; i < files->num; i++) |
| FcFileScanConfig (set, dirs, files->strs[i], config); |
| |
| bail2: |
| FcStrSetDestroy (files); |
| bail1: |
| closedir (d); |
| bail: |
| if (s_dir) |
| free (s_dir); |
| if (file_prefix) |
| free (file_prefix); |
| |
| return ret; |
| } |
| |
| FcBool |
| FcDirScan (FcFontSet *set, |
| FcStrSet *dirs, |
| FcFileCache *cache FC_UNUSED, |
| FcBlanks *blanks FC_UNUSED, |
| const FcChar8 *dir, |
| FcBool force FC_UNUSED) |
| { |
| FcConfig *config; |
| FcBool ret; |
| |
| if (cache || !force) |
| return FcFalse; |
| |
| config = FcConfigReference (NULL); |
| if (!config) |
| return FcFalse; |
| ret = FcDirScanConfig (set, dirs, dir, force, config); |
| FcConfigDestroy (config); |
| |
| return ret; |
| } |
| |
| /* |
| * Scan the specified directory and construct a cache of its contents |
| */ |
| FcCache * |
| FcDirCacheScan (const FcChar8 *dir, FcConfig *config) |
| { |
| FcStrSet *dirs; |
| FcFontSet *set; |
| FcCache *cache = NULL; |
| struct stat dir_stat; |
| const FcChar8 *sysroot = FcConfigGetSysRoot (config); |
| FcChar8 *d; |
| #ifndef _WIN32 |
| int fd = -1; |
| #endif |
| |
| if (sysroot) |
| d = FcStrBuildFilename (sysroot, dir, NULL); |
| else |
| d = FcStrdup (dir); |
| |
| if (FcDebug () & FC_DBG_FONTSET) |
| printf ("cache scan dir %s\n", d); |
| |
| if (FcStatChecksum (d, &dir_stat) < 0) |
| goto bail; |
| |
| set = FcFontSetCreate(); |
| if (!set) |
| goto bail; |
| |
| dirs = FcStrSetCreateEx (FCSS_GROW_BY_64); |
| if (!dirs) |
| goto bail1; |
| |
| #ifndef _WIN32 |
| fd = FcDirCacheLock (dir, config); |
| #endif |
| /* |
| * Scan the dir |
| */ |
| /* Do not pass sysroot here. FcDirScanConfig() do take care of it */ |
| if (!FcDirScanConfig (set, dirs, dir, FcTrue, config)) |
| goto bail2; |
| |
| /* |
| * Build the cache object |
| */ |
| cache = FcDirCacheBuild (set, dir, &dir_stat, dirs); |
| if (!cache) |
| goto bail2; |
| |
| /* |
| * Write out the cache file, ignoring any troubles |
| */ |
| FcDirCacheWrite (cache, config); |
| |
| bail2: |
| #ifndef _WIN32 |
| FcDirCacheUnlock (fd); |
| #endif |
| FcStrSetDestroy (dirs); |
| bail1: |
| FcFontSetDestroy (set); |
| bail: |
| FcStrFree (d); |
| |
| return cache; |
| } |
| |
| FcCache * |
| FcDirCacheRescan (const FcChar8 *dir, FcConfig *config) |
| { |
| FcCache *cache; |
| FcCache *new = NULL; |
| struct stat dir_stat; |
| FcStrSet *dirs; |
| const FcChar8 *sysroot; |
| FcChar8 *d = NULL; |
| #ifndef _WIN32 |
| int fd = -1; |
| #endif |
| |
| config = FcConfigReference (config); |
| if (!config) |
| return NULL; |
| sysroot = FcConfigGetSysRoot (config); |
| cache = FcDirCacheLoad (dir, config, NULL); |
| if (!cache) |
| goto bail; |
| |
| if (sysroot) |
| d = FcStrBuildFilename (sysroot, dir, NULL); |
| else |
| d = FcStrdup (dir); |
| if (FcStatChecksum (d, &dir_stat) < 0) |
| goto bail; |
| dirs = FcStrSetCreateEx (FCSS_GROW_BY_64); |
| if (!dirs) |
| goto bail; |
| |
| #ifndef _WIN32 |
| fd = FcDirCacheLock (dir, config); |
| #endif |
| /* |
| * Scan the dir |
| */ |
| /* Do not pass sysroot here. FcDirScanConfig() do take care of it */ |
| if (!FcDirScanConfig (NULL, dirs, dir, FcTrue, config)) |
| goto bail1; |
| /* |
| * Rebuild the cache object |
| */ |
| new = FcDirCacheRebuild (cache, &dir_stat, dirs); |
| if (!new) |
| goto bail1; |
| FcDirCacheUnload (cache); |
| /* |
| * Write out the cache file, ignoring any troubles |
| */ |
| FcDirCacheWrite (new, config); |
| |
| bail1: |
| #ifndef _WIN32 |
| FcDirCacheUnlock (fd); |
| #endif |
| FcStrSetDestroy (dirs); |
| bail: |
| if (d) |
| FcStrFree (d); |
| FcConfigDestroy (config); |
| |
| return new; |
| } |
| |
| /* |
| * Read (or construct) the cache for a directory |
| */ |
| FcCache * |
| FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config) |
| { |
| FcCache *cache = NULL; |
| |
| config = FcConfigReference (config); |
| /* Try to use existing cache file */ |
| if (!force) |
| cache = FcDirCacheLoad (dir, config, NULL); |
| |
| /* Not using existing cache file, construct new cache */ |
| if (!cache) |
| cache = FcDirCacheScan (dir, config); |
| FcConfigDestroy (config); |
| |
| return cache; |
| } |
| |
| FcBool |
| FcDirSave (FcFontSet *set FC_UNUSED, FcStrSet * dirs FC_UNUSED, const FcChar8 *dir FC_UNUSED) |
| { |
| return FcFalse; /* XXX deprecated */ |
| } |
| #define __fcdir__ |
| #include "fcaliastail.h" |
| #undef __fcdir__ |