Home | History | Annotate | Line # | Download | only in src
      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
     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
     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
     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
     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
    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
    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
    189 cmpstringp(const void *p1, const void *p2)
    190 {
    191     return strcmp(* (char **) p1, * (char **) p2);
    192 }
    193 
    194 FcBool
    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 = 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 
    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
    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 *
    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 *
    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 *
    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
    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