(root)/
fontconfig-2.14.2/
src/
fcdir.c
       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, *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
     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__