(root)/
fontconfig-2.14.2/
fc-cache/
fc-cache.c
       1  /*
       2   * fontconfig/fc-cache/fc-cache.c
       3   *
       4   * Copyright © 2002 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  #ifdef HAVE_CONFIG_H
      26  #include <config.h>
      27  #else
      28  #ifdef linux
      29  #define HAVE_GETOPT_LONG 1
      30  #endif
      31  #define HAVE_GETOPT 1
      32  #endif
      33  
      34  #include <fontconfig/fontconfig.h>
      35  #include <stdio.h>
      36  #include <stdlib.h>
      37  #ifdef HAVE_UNISTD_H
      38  #include <unistd.h>
      39  #endif
      40  #include <sys/types.h>
      41  #include <sys/stat.h>
      42  #include <errno.h>
      43  #include <fcntl.h>
      44  #ifdef HAVE_DIRENT_H
      45  #include <dirent.h>
      46  #endif
      47  #include <string.h>
      48  #include <locale.h>
      49  
      50  #if defined (_WIN32)
      51  #define STRICT
      52  #include <windows.h>
      53  #define sleep(x) Sleep((x) * 1000)
      54  #undef STRICT
      55  #endif
      56  
      57  #ifdef ENABLE_NLS
      58  #include <libintl.h>
      59  #define _(x)		(dgettext(GETTEXT_PACKAGE, x))
      60  #else
      61  #define dgettext(d, s)	(s)
      62  #define _(x)		(x)
      63  #endif
      64  
      65  #ifndef O_BINARY
      66  #define O_BINARY 0
      67  #endif
      68  
      69  #ifndef S_ISDIR
      70  #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
      71  #endif
      72  
      73  #ifndef HAVE_GETOPT
      74  #define HAVE_GETOPT 0
      75  #endif
      76  #ifndef HAVE_GETOPT_LONG
      77  #define HAVE_GETOPT_LONG 0
      78  #endif
      79  
      80  #if HAVE_GETOPT_LONG
      81  #undef  _GNU_SOURCE
      82  #define _GNU_SOURCE
      83  #include <getopt.h>
      84  const struct option longopts[] = {
      85      {"error-on-no-fonts", 0, 0, 'E'},
      86      {"force", 0, 0, 'f'},
      87      {"really-force", 0, 0, 'r'},
      88      {"sysroot", required_argument, 0, 'y'},
      89      {"system-only", 0, 0, 's'},
      90      {"version", 0, 0, 'V'},
      91      {"verbose", 0, 0, 'v'},
      92      {"help", 0, 0, 'h'},
      93      {NULL,0,0,0},
      94  };
      95  #else
      96  #if HAVE_GETOPT
      97  extern char *optarg;
      98  extern int optind, opterr, optopt;
      99  #endif
     100  #endif
     101  
     102  static void
     103  usage (char *program, int error)
     104  {
     105      FILE *file = error ? stderr : stdout;
     106  #if HAVE_GETOPT_LONG
     107      fprintf (file, _("usage: %s [-EfrsvVh] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n"),
     108  	     program);
     109  #else
     110      fprintf (file, _("usage: %s [-EfrsvVh] [-y SYSROOT] [dirs]\n"),
     111  	     program);
     112  #endif
     113      fprintf (file, _("Build font information caches in [dirs]\n"
     114  		     "(all directories in font configuration by default).\n"));
     115      fprintf (file, "\n");
     116  #if HAVE_GETOPT_LONG
     117      fprintf (file, _("  -E, --error-on-no-fonts  raise an error if no fonts in a directory\n"));
     118      fprintf (file, _("  -f, --force              scan directories with apparently valid caches\n"));
     119      fprintf (file, _("  -r, --really-force       erase all existing caches, then rescan\n"));
     120      fprintf (file, _("  -s, --system-only        scan system-wide directories only\n"));
     121      fprintf (file, _("  -y, --sysroot=SYSROOT    prepend SYSROOT to all paths for scanning\n"));
     122      fprintf (file, _("  -v, --verbose            display status information while busy\n"));
     123      fprintf (file, _("  -V, --version            display font config version and exit\n"));
     124      fprintf (file, _("  -h, --help               display this help and exit\n"));
     125  #else
     126      fprintf (file, _("  -E         (error-on-no-fonts)\n"));
     127      fprintf (file, _("                       raise an error if no fonts in a directory\n"));
     128      fprintf (file, _("  -f         (force)   scan directories with apparently valid caches\n"));
     129      fprintf (file, _("  -r,   (really force) erase all existing caches, then rescan\n"));
     130      fprintf (file, _("  -s         (system)  scan system-wide directories only\n"));
     131      fprintf (file, _("  -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n"));
     132      fprintf (file, _("  -v         (verbose) display status information while busy\n"));
     133      fprintf (file, _("  -V         (version) display font config version and exit\n"));
     134      fprintf (file, _("  -h         (help)    display this help and exit\n"));
     135  #endif
     136      exit (error);
     137  }
     138  
     139  static FcStrSet *processed_dirs;
     140  
     141  static int
     142  scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed)
     143  {
     144      int		    ret = 0;
     145      const FcChar8   *dir;
     146      FcStrSet	    *subdirs;
     147      FcStrList	    *sublist;
     148      FcCache	    *cache;
     149      struct stat	    statb;
     150      FcBool	    was_valid, was_processed = FcFalse;
     151      int		    i;
     152      const FcChar8   *sysroot = FcConfigGetSysRoot (config);
     153  
     154      /*
     155       * Now scan all of the directories into separate databases
     156       * and write out the results
     157       */
     158      while ((dir = FcStrListNext (list)))
     159      {
     160  	if (verbose)
     161  	{
     162  	    if (sysroot)
     163  		printf ("[%s]", sysroot);
     164  	    printf ("%s: ", dir);
     165  	    fflush (stdout);
     166  	}
     167  
     168  	if (FcStrSetMember (processed_dirs, dir))
     169  	{
     170  	    if (verbose)
     171  		printf (_("skipping, looped directory detected\n"));
     172  	    continue;
     173  	}
     174  
     175      FcChar8 *rooted_dir = NULL;
     176      if (sysroot)
     177      {
     178          rooted_dir = FcStrPlus(sysroot, dir);
     179      }
     180      else {
     181          rooted_dir = FcStrCopy(dir);
     182      }
     183  
     184  	if (stat ((char *) rooted_dir, &statb) == -1)
     185  	{
     186  	    switch (errno) {
     187  	    case ENOENT:
     188  	    case ENOTDIR:
     189  		if (verbose)
     190  		    printf (_("skipping, no such directory\n"));
     191  		break;
     192  	    default:
     193  		fprintf (stderr, "\"%s\": ", dir);
     194  		perror ("");
     195  		ret++;
     196  		break;
     197  	    }
     198  	    FcStrFree (rooted_dir);
     199  	    rooted_dir = NULL;
     200  	    continue;
     201  	}
     202  
     203      FcStrFree(rooted_dir);
     204      rooted_dir = NULL;
     205  
     206  	if (!S_ISDIR (statb.st_mode))
     207  	{
     208  	    fprintf (stderr, _("\"%s\": not a directory, skipping\n"), dir);
     209  	    continue;
     210  	}
     211  	was_processed = FcTrue;
     212  
     213  	if (really_force)
     214  	{
     215  	    FcDirCacheUnlink (dir, config);
     216  	}
     217  
     218  	cache = NULL;
     219  	was_valid = FcFalse;
     220  	if (!force) {
     221  	    cache = FcDirCacheLoad (dir, config, NULL);
     222  	    if (cache)
     223  		was_valid = FcTrue;
     224  	}
     225  	
     226  	if (!cache)
     227  	{
     228  	    (*changed)++;
     229  	    cache = FcDirCacheRead (dir, FcTrue, config);
     230  	    if (!cache)
     231  	    {
     232  		fprintf (stderr, _("\"%s\": scanning error\n"), dir);
     233  		ret++;
     234  		continue;
     235  	    }
     236  	}
     237  
     238  	if (was_valid)
     239  	{
     240  	    if (verbose)
     241  		printf (_("skipping, existing cache is valid: %d fonts, %d dirs\n"),
     242  			FcCacheNumFont (cache), FcCacheNumSubdir (cache));
     243  	}
     244  	else
     245  	{
     246  	    if (verbose)
     247  		printf (_("caching, new cache contents: %d fonts, %d dirs\n"),
     248  			FcCacheNumFont (cache), FcCacheNumSubdir (cache));
     249  
     250  	    if (!FcDirCacheValid (dir))
     251  	    {
     252  		fprintf (stderr, _("%s: failed to write cache\n"), dir);
     253  		(void) FcDirCacheUnlink (dir, config);
     254  		ret++;
     255  	    }
     256  	}
     257  
     258  	subdirs = FcStrSetCreate ();
     259  	if (!subdirs)
     260  	{
     261  	    fprintf (stderr, _("%s: Can't create subdir set\n"), dir);
     262  	    ret++;
     263  	    FcDirCacheUnload (cache);
     264  	    continue;
     265  	}
     266  	for (i = 0; i < FcCacheNumSubdir (cache); i++)
     267  	    FcStrSetAdd (subdirs, FcCacheSubdir (cache, i));
     268  	
     269  	FcDirCacheUnload (cache);
     270  
     271  	sublist = FcStrListCreate (subdirs);
     272  	FcStrSetDestroy (subdirs);
     273  	if (!sublist)
     274  	{
     275  	    fprintf (stderr, _("%s: Can't create subdir list\n"), dir);
     276  	    ret++;
     277  	    continue;
     278  	}
     279  	FcStrSetAdd (processed_dirs, dir);
     280  	ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed);
     281  	FcStrListDone (sublist);
     282      }
     283  
     284      if (error_on_no_fonts && !was_processed)
     285  	ret++;
     286      return ret;
     287  }
     288  
     289  static FcBool
     290  cleanCacheDirectories (FcConfig *config, FcBool verbose)
     291  {
     292      FcStrList	*cache_dirs = FcConfigGetCacheDirs (config);
     293      FcChar8	*cache_dir;
     294      FcBool	ret = FcTrue;
     295  
     296      if (!cache_dirs)
     297  	return FcFalse;
     298      while ((cache_dir = FcStrListNext (cache_dirs)))
     299      {
     300  	if (!FcDirCacheClean (cache_dir, verbose))
     301  	{
     302  	    ret = FcFalse;
     303  	    break;
     304  	}
     305      }
     306      FcStrListDone (cache_dirs);
     307      return ret;
     308  }
     309  
     310  int
     311  main (int argc, char **argv)
     312  {
     313      FcStrSet	*dirs;
     314      FcStrList	*list;
     315      FcBool    	verbose = FcFalse;
     316      FcBool	force = FcFalse;
     317      FcBool	really_force = FcFalse;
     318      FcBool	systemOnly = FcFalse;
     319      FcBool	error_on_no_fonts = FcFalse;
     320      FcConfig	*config;
     321      FcChar8     *sysroot = NULL;
     322      int		i;
     323      int		changed;
     324      int		ret;
     325  #if HAVE_GETOPT_LONG || HAVE_GETOPT
     326      int		c;
     327  
     328      setlocale (LC_ALL, "");
     329  #if HAVE_GETOPT_LONG
     330      while ((c = getopt_long (argc, argv, "Efrsy:Vvh", longopts, NULL)) != -1)
     331  #else
     332      while ((c = getopt (argc, argv, "Efrsy:Vvh")) != -1)
     333  #endif
     334      {
     335  	switch (c) {
     336  	case 'E':
     337  	    error_on_no_fonts = FcTrue;
     338  	    break;
     339  	case 'r':
     340  	    really_force = FcTrue;
     341  	    /* fall through */
     342  	case 'f':
     343  	    force = FcTrue;
     344  	    break;
     345  	case 's':
     346  	    systemOnly = FcTrue;
     347  	    break;
     348  	case 'y':
     349  	    sysroot = FcStrCopy ((const FcChar8 *)optarg);
     350  	    break;
     351  	case 'V':
     352  	    fprintf (stderr, "fontconfig version %d.%d.%d\n",
     353  		     FC_MAJOR, FC_MINOR, FC_REVISION);
     354  	    exit (0);
     355  	case 'v':
     356  	    verbose = FcTrue;
     357  	    break;
     358  	case 'h':
     359  	    usage (argv[0], 0);
     360  	default:
     361  	    usage (argv[0], 1);
     362  	}
     363      }
     364      i = optind;
     365  #else
     366      i = 1;
     367  #endif
     368  
     369      if (systemOnly)
     370  	FcConfigEnableHome (FcFalse);
     371      if (sysroot)
     372      {
     373  	FcConfigSetSysRoot (NULL, sysroot);
     374  	FcStrFree (sysroot);
     375  	config = FcConfigGetCurrent();
     376      }
     377      else
     378      {
     379  	config = FcInitLoadConfig ();
     380      }
     381      if (!config)
     382      {
     383  	fprintf (stderr, _("%s: Can't initialize font config library\n"), argv[0]);
     384  	return 1;
     385      }
     386      FcConfigSetCurrent (config);
     387  
     388      if (argv[i])
     389      {
     390  	dirs = FcStrSetCreate ();
     391  	if (!dirs)
     392  	{
     393  	    fprintf (stderr, _("%s: Can't create list of directories\n"),
     394  		     argv[0]);
     395  	    return 1;
     396  	}
     397  	while (argv[i])
     398  	{
     399  	    if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
     400  	    {
     401  		fprintf (stderr, _("%s: Can't add directory\n"), argv[0]);
     402  		return 1;
     403  	    }
     404  	    i++;
     405  	}
     406  	list = FcStrListCreate (dirs);
     407  	FcStrSetDestroy (dirs);
     408      }
     409      else
     410  	list = FcConfigGetFontDirs (config);
     411  
     412      if ((processed_dirs = FcStrSetCreate()) == NULL) {
     413  	fprintf(stderr, _("Out of Memory\n"));
     414  	return 1;
     415      }
     416  
     417      if (verbose)
     418      {
     419  	const FcChar8 *dir;
     420  
     421  	printf ("Font directories:\n");
     422  	while ((dir = FcStrListNext (list)))
     423  	{
     424  	    printf ("\t%s\n", dir);
     425  	}
     426  	FcStrListFirst(list);
     427      }
     428      changed = 0;
     429      ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed);
     430      FcStrListDone (list);
     431  
     432      /*
     433       * Try to create CACHEDIR.TAG anyway.
     434       * This expects the fontconfig cache directory already exists.
     435       * If it doesn't, it won't be simply created.
     436       */
     437      FcCacheCreateTagFile (config);
     438  
     439      FcStrSetDestroy (processed_dirs);
     440  
     441      cleanCacheDirectories (config, verbose);
     442  
     443      FcConfigDestroy (config);
     444      FcFini ();
     445      /* 
     446       * Now we need to sleep a second  (or two, to be extra sure), to make
     447       * sure that timestamps for changes after this run of fc-cache are later
     448       * then any timestamps we wrote.  We don't use gettimeofday() because
     449       * sleep(3) can't be interrupted by a signal here -- this isn't in the
     450       * library, and there aren't any signals flying around here.
     451       */
     452      /* the resolution of mtime on FAT is 2 seconds */
     453      if (changed)
     454  	sleep (2);
     455      if (verbose)
     456  	printf ("%s: %s\n", argv[0], ret ? _("failed") : _("succeeded"));
     457      return ret;
     458  }