fc-cache.c revision 18bd4a06
11.207Smycroft/*
21.147Simp * fontconfig/fc-cache/fc-cache.c
31.1Schristos *
41.14Schristos * Copyright © 2002 Keith Packard
51.207Smycroft *
61.1Schristos * Permission to use, copy, modify, distribute, and sell this software and its
71.1Schristos * documentation for any purpose is hereby granted without fee, provided that
81.14Schristos * the above copyright notice appear in all copies and that both that
91.14Schristos * copyright notice and this permission notice appear in supporting
101.14Schristos * documentation, and that the name of the author(s) not be used in
111.1Schristos * advertising or publicity pertaining to distribution of the software without
121.1Schristos * specific, written prior permission.  The authors make no
131.1Schristos * representations about the suitability of this software for any purpose.  It
141.1Schristos * is provided "as is" without express or implied warranty.
151.1Schristos *
161.1Schristos * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
171.1Schristos * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
181.1Schristos * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
191.1Schristos * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
201.1Schristos * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
211.14Schristos * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
221.14Schristos * PERFORMANCE OF THIS SOFTWARE.
231.14Schristos */
241.14Schristos
251.14Schristos#ifdef HAVE_CONFIG_H
261.1Schristos#include <config.h>
271.14Schristos#else
281.14Schristos#ifdef linux
291.14Schristos#define HAVE_GETOPT_LONG 1
301.14Schristos#endif
311.14Schristos#define HAVE_GETOPT 1
321.14Schristos#endif
331.14Schristos
341.14Schristos#include <fontconfig/fontconfig.h>
351.14Schristos#include <stdio.h>
361.14Schristos#include <stdlib.h>
371.14Schristos#include <unistd.h>
381.1Schristos#include <sys/types.h>
391.1Schristos#include <sys/stat.h>
401.1Schristos#include <errno.h>
411.140Simp#include <fcntl.h>
421.1Schristos#include <dirent.h>
431.1Schristos#include <string.h>
441.31Snathanw
451.176Smason#if defined (_WIN32)
461.83Smycroft#define STRICT
471.15Skenh#include <windows.h>
481.40Shwr#define sleep(x) Sleep((x) * 1000)
491.65Sjoda#undef STRICT
501.1Schristos#endif
511.183Sgmcgarry
521.205Smycroft#ifndef O_BINARY
531.1Schristos#define O_BINARY 0
541.1Schristos#endif
551.1Schristos
561.1Schristos#ifndef HAVE_GETOPT
571.65Sjoda#define HAVE_GETOPT 0
581.11Sthorpej#endif
591.147Simp#ifndef HAVE_GETOPT_LONG
601.128Sichiro#define HAVE_GETOPT_LONG 0
611.1Schristos#endif
621.140Simp
631.147Simp#if HAVE_GETOPT_LONG
641.1Schristos#undef  _GNU_SOURCE
651.1Schristos#define _GNU_SOURCE
661.147Simp#include <getopt.h>
671.65Sjodaconst struct option longopts[] = {
681.175Srjs    {"error-on-no-fonts", 0, 0, 'E'},
691.140Simp    {"force", 0, 0, 'f'},
701.171Sjonathan    {"quick", 0, 0, 'q'},
711.1Schristos    {"really-force", 0, 0, 'r'},
721.147Simp    {"sysroot", required_argument, 0, 'y'},
731.4Sthorpej    {"system-only", 0, 0, 's'},
741.147Simp    {"version", 0, 0, 'V'},
751.39Ssommerfe    {"verbose", 0, 0, 'v'},
761.66Sjlam    {"help", 0, 0, 'h'},
771.158Sjoda    {NULL,0,0,0},
781.135Spooka};
791.65Sjoda#else
801.111Simp#if HAVE_GETOPT
811.1Schristosextern char *optarg;
821.67Schoppsextern int optind, opterr, optopt;
831.98Simp#endif
841.55Ssommerfe#endif
851.92Sonoe
861.99Smsaitohstatic void
871.85Smycroftusage (char *program, int error)
881.97Sonoe{
891.189Sichiro    FILE *file = error ? stderr : stdout;
901.196Smycroft#if HAVE_GETOPT_LONG
911.136Sichiro    fprintf (file, "usage: %s [-EfqrsvVh] [--quick] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n",
921.125Sichiro	     program);
931.159Saymeric#else
941.122Sichiro    fprintf (file, "usage: %s [-EfqrsvVh] [-y SYSROOT] [dirs]\n",
951.201Smycroft	     program);
961.147Simp#endif
971.147Simp    fprintf (file, "Build font information caches in [dirs]\n"
981.172Smartin	     "(all directories in font configuration by default).\n");
991.38Smjl    fprintf (file, "\n");
1001.72Ssoren#if HAVE_GETOPT_LONG
1011.147Simp    fprintf (file, "  -E, --error-on-no-fonts  raise an error if no fonts in a directory\n");
1021.147Simp    fprintf (file, "  -f, --force              scan directories with apparently valid caches\n");
1031.159Saymeric    fprintf (file, "  -q, --quick              don't sleep before exiting\n");
1041.25Sthorpej    fprintf (file, "  -r, --really-force       erase all existing caches, then rescan\n");
1051.78Senami    fprintf (file, "  -s, --system-only        scan system-wide directories only\n");
1061.43Stron    fprintf (file, "  -y, --sysroot=SYSROOT    prepend SYSROOT to all paths for scanning\n");
1071.99Smsaitoh    fprintf (file, "  -v, --verbose            display status information while busy\n");
1081.110Snonaka    fprintf (file, "  -V, --version            display font config version and exit\n");
1091.22Sthorpej    fprintf (file, "  -h, --help               display this help and exit\n");
1101.43Stron#else
1111.15Skenh    fprintf (file, "  -E         (error-on-no-fonts)\n");
1121.89Sis    fprintf (file, "                       raise an error if no fonts in a directory\n");
1131.195Smycroft    fprintf (file, "  -f         (force)   scan directories with apparently valid caches\n");
1141.108Sichiro    fprintf (file, "  -q         (quick)   don't sleep before exiting\n");
1151.191Sitohy    fprintf (file, "  -r,   (really force) erase all existing caches, then rescan\n");
1161.1Schristos    fprintf (file, "  -s         (system)  scan system-wide directories only\n");
1171.1Schristos    fprintf (file, "  -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n");
1181.140Simp    fprintf (file, "  -v         (verbose) display status information while busy\n");
1191.140Simp    fprintf (file, "  -V         (version) display font config version and exit\n");
1201.1Schristos    fprintf (file, "  -h         (help)    display this help and exit\n");
1211.58Ssoren#endif
1221.1Schristos    exit (error);
1231.94Sjoda}
1241.140Simp
1251.140Simpstatic FcStrSet *processed_dirs;
1261.140Simp
1271.140Simpstatic int
1281.1SchristosscanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, FcBool error_on_no_fonts, int *changed)
1291.1Schristos{
1301.13Sthorpej    int		    ret = 0;
1311.147Simp    const FcChar8   *dir;
1321.132Sitojun    FcStrSet	    *subdirs;
1331.172Smartin    FcStrList	    *sublist;
1341.172Smartin    FcCache	    *cache;
1351.172Smartin    struct stat	    statb;
1361.24Sthorpej    FcBool	    was_valid, was_processed = FcFalse;
1371.140Simp    int		    i;
1381.140Simp    const FcChar8   *sysroot = FcConfigGetSysRoot (config);
1391.140Simp
1401.140Simp    /*
1411.140Simp     * Now scan all of the directories into separate databases
1421.140Simp     * and write out the results
1431.140Simp     */
1441.140Simp    while ((dir = FcStrListNext (list)))
1451.140Simp    {
1461.140Simp	if (verbose)
1471.140Simp	{
1481.189Sichiro	    if (sysroot)
1491.189Sichiro		printf ("[%s]", sysroot);
1501.189Sichiro	    printf ("%s: ", dir);
1511.189Sichiro	    fflush (stdout);
1521.140Simp	}
1531.147Simp
1541.147Simp	if (FcStrSetMember (processed_dirs, dir))
1551.147Simp	{
1561.196Smycroft	    if (verbose)
1571.196Smycroft		printf ("skipping, looped directory detected\n");
1581.196Smycroft	    continue;
1591.201Smycroft	}
1601.201Smycroft
1611.201Smycroft	if (stat ((char *) dir, &statb) == -1)
1621.140Simp	{
1631.140Simp	    switch (errno) {
1641.140Simp	    case ENOENT:
1651.140Simp	    case ENOTDIR:
1661.169Smycroft		if (verbose)
1671.140Simp		    printf ("skipping, no such directory\n");
1681.147Simp		break;
1691.147Simp	    default:
1701.147Simp		fprintf (stderr, "\"%s\": ", dir);
1711.140Simp		perror ("");
1721.140Simp		ret++;
1731.199Senami		break;
1741.198Senami	    }
1751.198Senami	    continue;
1761.150Senami	}
1771.140Simp
1781.90Sgmcgarry	if (!S_ISDIR (statb.st_mode))
1791.140Simp	{
1801.90Sgmcgarry	    fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
1811.90Sgmcgarry	    continue;
1821.24Sthorpej	}
1831.147Simp	was_processed = FcTrue;
1841.24Sthorpej
1851.37Smjl	if (really_force)
1861.140Simp	    FcDirCacheUnlink (dir, config);
1871.140Simp
1881.140Simp	cache = NULL;
1891.1Schristos	was_valid = FcFalse;
1901.1Schristos	if (!force) {
1911.16Sdbj	    cache = FcDirCacheLoad (dir, config, NULL);
1921.16Sdbj	    if (cache)
1931.1Schristos		was_valid = FcTrue;
1941.15Skenh	}
1951.15Skenh
1961.120Sthorpej	if (!cache)
1971.120Sthorpej	{
1981.120Sthorpej	    (*changed)++;
1991.101Shubertf	    cache = FcDirCacheRead (dir, FcTrue, config);
2001.147Simp	    if (!cache)
2011.147Simp	    {
2021.157Saymeric		fprintf (stderr, "%s: error scanning\n", dir);
2031.157Saymeric		ret++;
2041.157Saymeric		continue;
2051.147Simp	    }
2061.101Shubertf	}
2071.101Shubertf
2081.101Shubertf	if (was_valid)
2091.144Sichiro	{
2101.173Smartin	    if (verbose)
2111.31Snathanw		printf ("skipping, existing cache is valid: %d fonts, %d dirs\n",
2121.140Simp			FcCacheNumFont (cache), FcCacheNumSubdir (cache));
2131.140Simp	}
2141.158Sjoda	else
2151.158Sjoda	{
2161.158Sjoda	    if (verbose)
2171.140Simp		printf ("caching, new cache contents: %d fonts, %d dirs\n",
2181.140Simp			FcCacheNumFont (cache), FcCacheNumSubdir (cache));
2191.140Simp
2201.140Simp	    if (!FcDirCacheValid (dir))
2211.31Snathanw	    {
2221.32Snathanw		fprintf (stderr, "%s: failed to write cache\n", dir);
2231.140Simp		(void) FcDirCacheUnlink (dir, config);
2241.34Sthorpej		ret++;
2251.191Sitohy	    }
2261.1Schristos	}
2271.1Schristos
2281.103Sdrochner	subdirs = FcStrSetCreate ();
2291.93Ssoren	if (!subdirs)
2301.93Ssoren	{
2311.93Ssoren	    fprintf (stderr, "%s: Can't create subdir set\n", dir);
2321.93Ssoren	    ret++;
2331.149Syamt	    FcDirCacheUnload (cache);
2341.93Ssoren	    continue;
2351.1Schristos	}
2361.191Sitohy	for (i = 0; i < FcCacheNumSubdir (cache); i++)
2371.155Sichiro	    FcStrSetAdd (subdirs, FcCacheSubdir (cache, i));
2381.59Ssoren
2391.1Schristos	FcDirCacheUnload (cache);
2401.90Sgmcgarry
2411.140Simp	sublist = FcStrListCreate (subdirs);
2421.90Sgmcgarry	FcStrSetDestroy (subdirs);
2431.90Sgmcgarry	if (!sublist)
2441.1Schristos	{
2451.56Senami	    fprintf (stderr, "%s: Can't create subdir list\n", dir);
2461.122Sichiro	    ret++;
2471.192Ssekiya	    continue;
2481.92Sonoe	}
2491.140Simp	FcStrSetAdd (processed_dirs, dir);
2501.140Simp	ret += scanDirs (sublist, config, force, really_force, verbose, error_on_no_fonts, changed);
2511.140Simp	FcStrListDone (sublist);
2521.140Simp    }
2531.140Simp    if (error_on_no_fonts && !was_processed)
2541.72Ssoren	ret++;
2551.72Ssoren    return ret;
2561.102Ssoren}
2571.1Schristos
2581.1Schristosstatic FcBool
2591.140SimpcleanCacheDirectories (FcConfig *config, FcBool verbose)
2601.73Sthorpej{
2611.21Sitohy    FcStrList	*cache_dirs = FcConfigGetCacheDirs (config);
2621.1Schristos    FcChar8	*cache_dir;
2631.147Simp    FcBool	ret = FcTrue;
2641.164Sichiro
2651.167Sonoe    if (!cache_dirs)
2661.140Simp	return FcFalse;
2671.204Smycroft    while ((cache_dir = FcStrListNext (cache_dirs)))
2681.204Smycroft    {
2691.204Smycroft	if (!FcDirCacheClean (cache_dir, verbose))
2701.204Smycroft	{
2711.140Simp	    ret = FcFalse;
2721.140Simp	    break;
2731.140Simp	}
2741.1Schristos    }
2751.1Schristos    FcStrListDone (cache_dirs);
2761.203Smycroft    return ret;
2771.1Schristos}
2781.58Ssoren
2791.18Sthorpejint
2801.203Smycroftmain (int argc, char **argv)
2811.1Schristos{
2821.1Schristos    FcStrSet	*dirs;
2831.78Senami    FcStrList	*list;
2841.191Sitohy    FcBool    	verbose = FcFalse;
2851.128Sichiro    FcBool      quick = FcFalse;
2861.140Simp    FcBool	force = FcFalse;
2871.140Simp    FcBool	really_force = FcFalse;
2881.140Simp    FcBool	systemOnly = FcFalse;
2891.147Simp    FcBool	error_on_no_fonts = FcFalse;
2901.140Simp    FcConfig	*config;
2911.140Simp    FcChar8     *sysroot = NULL;
2921.140Simp    int		i;
2931.147Simp    int		changed;
2941.147Simp    int		ret;
2951.147Simp#if HAVE_GETOPT_LONG || HAVE_GETOPT
2961.140Simp    int		c;
2971.147Simp
2981.147Simp#if HAVE_GETOPT_LONG
2991.147Simp    while ((c = getopt_long (argc, argv, "Efqrsy:Vvh", longopts, NULL)) != -1)
3001.147Simp#else
3011.175Srjs    while ((c = getopt (argc, argv, "Efqrsy:Vvh")) != -1)
3021.147Simp#endif
3031.166Schris    {
3041.176Smason	switch (c) {
3051.140Simp	case 'E':
3061.128Sichiro	    error_on_no_fonts = FcTrue;
3071.191Sitohy	    break;
3081.85Smycroft	case 'r':
3091.195Smycroft	    really_force = FcTrue;
3101.195Smycroft	    /* fall through */
3111.195Smycroft	case 'f':
3121.85Smycroft	    force = FcTrue;
3131.85Smycroft	    break;
3141.91Ssoren	case 'q':
3151.91Ssoren	    quick = FcTrue;
3161.91Ssoren	    break;
3171.175Srjs	case 's':
3181.175Srjs	    systemOnly = FcTrue;
3191.175Srjs	    break;
3201.83Smycroft	case 'y':
3211.83Smycroft	    sysroot = FcStrCopy ((const FcChar8 *)optarg);
3221.83Smycroft	    break;
3231.88Sjoda	case 'V':
3241.141Schristos	    fprintf (stderr, "fontconfig version %d.%d.%d\n",
3251.147Simp		     FC_MAJOR, FC_MINOR, FC_REVISION);
3261.147Simp	    exit (0);
3271.147Simp	case 'v':
3281.147Simp	    verbose = FcTrue;
3291.147Simp	    break;
3301.135Spooka	case 'h':
3311.135Spooka	    usage (argv[0], 0);
3321.135Spooka	default:
3331.78Senami	    usage (argv[0], 1);
3341.147Simp	}
3351.185Sichiro    }
3361.147Simp    i = optind;
3371.147Simp#else
3381.140Simp    i = 1;
3391.140Simp#endif
3401.140Simp
3411.140Simp    if (systemOnly)
3421.140Simp	FcConfigEnableHome (FcFalse);
3431.140Simp    if (sysroot)
3441.140Simp    {
3451.140Simp	FcConfigSetSysRoot (NULL, sysroot);
3461.4Sthorpej	FcStrFree (sysroot);
3471.15Skenh	config = FcConfigGetCurrent();
3481.15Skenh    }
3491.183Sgmcgarry    else
3501.183Sgmcgarry    {
3511.183Sgmcgarry	config = FcInitLoadConfig ();
3521.15Skenh    }
3531.4Sthorpej    if (!config)
3541.4Sthorpej    {
3551.39Ssommerfe	fprintf (stderr, "%s: Can't init font config library\n", argv[0]);
3561.39Ssommerfe	return 1;
3571.1Schristos    }
3581.140Simp    FcConfigSetCurrent (config);
3591.140Simp
3601.140Simp    if (argv[i])
3611.140Simp    {
3621.1Schristos	dirs = FcStrSetCreate ();
3631.127Sjhawk	if (!dirs)
3641.156Spooka	{
3651.1Schristos	    fprintf (stderr, "%s: Can't create list of directories\n",
3661.19Sthorpej		     argv[0]);
3671.140Simp	    return 1;
3681.100Stoddpw	}
3691.184Smartin	while (argv[i])
3701.140Simp	{
3711.140Simp	    if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[i]))
3721.140Simp	    {
3731.1Schristos		fprintf (stderr, "%s: Can't add directory\n", argv[0]);
3741.1Schristos		return 1;
3751.140Simp	    }
3761.1Schristos	    i++;
3771.140Simp	}
3781.163Smartin	list = FcStrListCreate (dirs);
3791.1Schristos	FcStrSetDestroy (dirs);
3801.140Simp    }
3811.140Simp    else
3821.140Simp	list = FcConfigGetConfigDirs (config);
3831.140Simp
3841.140Simp    if ((processed_dirs = FcStrSetCreate()) == NULL) {
3851.140Simp	fprintf(stderr, "Cannot malloc\n");
3861.20Sthorpej	return 1;
3871.90Sgmcgarry    }
3881.90Sgmcgarry
3891.147Simp    changed = 0;
3901.90Sgmcgarry    ret = scanDirs (list, config, force, really_force, verbose, error_on_no_fonts, &changed);
3911.145Schristos    FcStrListDone (list);
3921.140Simp
3931.140Simp    /*
3941.114Sthorpej     * Try to create CACHEDIR.TAG anyway.
3951.90Sgmcgarry     * This expects the fontconfig cache directory already exists.
3961.140Simp     * If it doesn't, it won't be simply created.
3971.90Sgmcgarry     */
3981.96Sgmcgarry    FcCacheCreateTagFile (config);
3991.96Sgmcgarry
4001.134Schristos    FcStrSetDestroy (processed_dirs);
4011.140Simp
4021.140Simp    cleanCacheDirectories (config, verbose);
4031.140Simp
4041.195Smycroft    FcConfigDestroy (config);
4051.140Simp    FcFini ();
4061.140Simp    /*
4071.140Simp     * Now we need to sleep a second  (or two, to be extra sure), to make
4081.140Simp     * sure that timestamps for changes after this run of fc-cache are later
4091.191Sitohy     * then any timestamps we wrote.  We don't use gettimeofday() because
4101.174Smjl     * sleep(3) can't be interrupted by a signal here -- this isn't in the
4111.140Simp     * library, and there aren't any signals flying around here.
4121.62Sdanw     */
4131.64Saugustss    /* the resolution of mtime on FAT is 2 seconds */
4141.147Simp    if (!quick && changed)
4151.196Smycroft	sleep (2);
4161.76Senami    if (verbose)
4171.95Sonoe	printf ("%s: %s\n", argv[0], ret ? "failed" : "succeeded");
4181.160Sis    return ret;
4191.140Simp}
4201.147Simp