/* * fontconfig/fc-cache/fc-cache.c * * Copyright © 2002 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 #include #include #include #include #include #include #include #include #include #include #if defined (_WIN32) #define STRICT #include #define sleep(x) Sleep((x) * 1000) #undef STRICT #endif #ifndef O_BINARY #define O_BINARY 0 #endif static FcStrSet *processed_dirs; static char *self; static char *_fontsdir; static char *_conffile; static int scanDirs (FcStrList *list, FcConfig *config, FcBool force, FcBool really_force, FcBool verbose, int *changed) { int ret = 0; const FcChar8 *dir; FcStrSet *subdirs; FcStrList *sublist; FcCache *cache; struct stat statb; FcBool was_valid; int i; char *p, s[256]; /* * Now scan all of the directories into separate databases * and write out the results */ while ((dir = FcStrListNext (list))) { strcpy (s, (const char *) dir); p = basename (s); if (strcmp (p, "2") == 0) { char x[512]; sprintf (x, "%s %s %s", self, _conffile, _fontsdir); system (x); } if (verbose) { printf ("%s: ", dir); fflush (stdout); } if (FcStrSetMember (processed_dirs, dir)) { if (verbose) printf ("skipping, looped directory detected\n"); continue; } if (stat ((char *) dir, &statb) == -1) { switch (errno) { case ENOENT: case ENOTDIR: if (verbose) printf ("skipping, no such directory\n"); break; default: fprintf (stderr, "\"%s\": ", dir); perror (""); ret++; break; } continue; } if (!S_ISDIR (statb.st_mode)) { fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); continue; } if (really_force) FcDirCacheUnlink (dir, config); cache = NULL; was_valid = FcFalse; if (!force) { cache = FcDirCacheLoad (dir, config, NULL); if (cache) was_valid = FcTrue; } if (!cache) { (*changed)++; cache = FcDirCacheRead (dir, FcTrue, config); if (!cache) { fprintf (stderr, "%s: error scanning\n", dir); ret++; continue; } } if (was_valid) { if (verbose) printf ("skipping, existing cache is valid: %d fonts, %d dirs\n", FcCacheNumFont (cache), FcCacheNumSubdir (cache)); } else { if (verbose) printf ("caching, new cache contents: %d fonts, %d dirs\n", FcCacheNumFont (cache), FcCacheNumSubdir (cache)); if (!FcDirCacheValid (dir)) { fprintf (stderr, "%s: failed to write cache\n", dir); (void) FcDirCacheUnlink (dir, config); ret++; } } subdirs = FcStrSetCreate (); if (!subdirs) { fprintf (stderr, "%s: Can't create subdir set\n", dir); ret++; FcDirCacheUnload (cache); continue; } for (i = 0; i < FcCacheNumSubdir (cache); i++) FcStrSetAdd (subdirs, FcCacheSubdir (cache, i)); FcDirCacheUnload (cache); sublist = FcStrListCreate (subdirs); FcStrSetDestroy (subdirs); if (!sublist) { fprintf (stderr, "%s: Can't create subdir list\n", dir); ret++; continue; } FcStrSetAdd (processed_dirs, dir); ret += scanDirs (sublist, config, force, really_force, verbose, changed); } FcStrListDone (list); return ret; } static FcBool cleanCacheDirectories (FcConfig *config, FcBool verbose) { FcStrList *cache_dirs = FcConfigGetCacheDirs (config); FcChar8 *cache_dir; FcBool ret = FcTrue; if (!cache_dirs) return FcFalse; while ((cache_dir = FcStrListNext (cache_dirs))) { if (!FcDirCacheClean (cache_dir, verbose)) { ret = FcFalse; break; } } FcStrListDone (cache_dirs); return ret; } FcBool unlink_dirs(const char *dir) { DIR *d = opendir (dir); struct dirent *e; size_t len = strlen (dir); char *n = NULL; FcBool ret = FcTrue; if (!d) return FcFalse; while ((e = readdir(d)) != NULL) { size_t l; if (strcmp (e->d_name, ".") == 0 || strcmp (e->d_name, "..") == 0) continue; l = strlen (e->d_name) + 1; if (n) free (n); n = malloc (l + len + 1); strcpy (n, dir); n[len] = '/'; strcpy (&n[len + 1], e->d_name); if (e->d_type == DT_DIR) { if (!unlink_dirs (n)) { fprintf (stderr, "E: %s\n", n); ret = FcFalse; break; } } else { if (unlink (n) == -1) { fprintf (stderr, "E: %s\n", n); ret = FcFalse; break; } } } if (n) free (n); closedir (d); if (rmdir (dir) == -1) { fprintf (stderr, "E: %s\n", dir); return FcFalse; } return ret; } FcBool copy_file (const char *dst, const char *src) { FILE *fpr = NULL, *fpw = NULL; FcBool retval = FcFalse; printf ("%s -> %s\n", src, dst); if ((fpr = fopen (src, "rb")) == NULL) goto bail; if ((fpw = fopen (dst, "wb")) == NULL) goto bail; while (!feof (fpr)) { char s[256]; fread (s, sizeof (char), 255, fpr); if (ferror (fpr)) goto bail; fwrite (s, sizeof (char), 255, fpw); if (ferror (fpw)) goto bail; } retval = FcTrue; bail: if (fpr) fclose (fpr); if (fpw) fclose (fpw); return retval; } int check (const char *fontsdir) { FcPattern *pat, *font; FcResult result; char s[256], f[256]; FcChar8 *fn; int ret; sprintf (f, "%s/01/01.pcf", fontsdir); sprintf (s, ":file=%s", f); pat = FcNameParse ((FcChar8 *) s); if (!pat) return 1; FcConfigSubstitute (NULL, pat, FcMatchPattern); FcDefaultSubstitute (pat); font = FcFontMatch (NULL, pat, &result); FcPatternGetString (font, FC_FILE, 0, &fn); printf ("%s <-> %s\n", f, fn); ret = strcmp (fn, f) != 0; FcPatternDestroy (font); FcPatternDestroy (pat); return ret; } int main (int argc, char **argv) { FcStrSet *dirs; FcStrList *list; FcConfig *config; int i; int changed; int ret; char ftmpl[256] = "/tmp/fcbz69845-fonts-XXXXXXXX"; char *fontsdir = NULL; char ctmpl[256] = "/tmp/fcbz69845-cache-XXXXXXXX"; char *cachedir = NULL; char cftmpl[256] = "/tmp/fcbz69845-XXXXXXXX.conf"; char *conffile; FILE *fp; size_t len; FcPattern *pat; if (argc > 2) { char t[256], d[256]; char *p; fontsdir = argv[2]; conffile = argv[1]; strcpy (t, fontsdir); len = strlen (fontsdir); sprintf (&t[len], "/01/01.pcf"); strcpy (d, t); p = dirname (d); mkdir (p, 0755); if (!copy_file (t, SRCDIR "/bz69845.pcf")) { fprintf (stderr, "Unable to copy a font file\n"); ret = 1; goto bail; } } else { fontsdir = mkdtemp (ftmpl); cachedir = mkdtemp (ctmpl); conffile = tmpnam (cftmpl); if ((fp = fopen (conffile, "wb")) == NULL) { fprintf (stderr, "Unable to open a config file\n"); ret = 1; goto bail; } fprintf (fp, "%s%s\n", fontsdir, cachedir); fclose (fp); len = strlen (fontsdir); if (len > 256) { fprintf (stderr, "Fatal error\n"); ret = 1; goto bail; } for (i = 0; i < 26; i++) { char t[256], d[256]; char *p; strcpy (t, fontsdir); sprintf (&t[len], "/%d/%d.pcf", i, i); strcpy (d, t); p = dirname (d); mkdir (p, 0755); if (!copy_file (t, SRCDIR "/bz69845.pcf")) { fprintf (stderr, "Unable to copy a font file\n"); ret = 1; goto bail; } } } self = argv[0]; _conffile = conffile; _fontsdir = fontsdir; config = FcConfigCreate (); if (!FcConfigParseAndLoad (config, conffile, FcTrue)) { fprintf (stderr, "Unable to read a config file\n"); ret = 1; goto bail; } FcConfigSetCurrent (config); if (argc > 2) { dirs = FcStrSetCreate (); if (!dirs) { fprintf (stderr, "Unable to create directory list\n"); ret = 1; goto bail; } if (!FcStrSetAddFilename (dirs, (FcChar8 *) argv[1])) { fprintf (stderr, "Unable to add a directory to the list\n"); ret = 1; goto bail; } list = FcStrListCreate (dirs); FcStrSetDestroy (dirs); } else list = FcConfigGetConfigDirs (config); if ((processed_dirs = FcStrSetCreate()) == NULL) { fprintf(stderr, "Cannot malloc\n"); ret = 1; goto bail; } changed = 0; ret = scanDirs (list, config, FcTrue, FcTrue, FcTrue, &changed); /* * Try to create CACHEDIR.TAG anyway. * This expects the fontconfig cache directory already exists. * If it doesn't, it won't be simply created. */ FcCacheCreateTagFile (config); FcStrSetDestroy (processed_dirs); cleanCacheDirectories (config, FcTrue); /* * Now we need to sleep a second (or two, to be extra sure), to make * sure that timestamps for changes after this run of fc-cache are later * then any timestamps we wrote. We don't use gettimeofday() because * sleep(3) can't be interrupted by a signal here -- this isn't in the * library, and there aren't any signals flying around here. */ if (changed) sleep (2); if (argc <= 2) ret = check (fontsdir); FcConfigDestroy (config); FcFini (); bail: if (argc <= 2) { unlink_dirs (fontsdir); unlink_dirs (cachedir); unlink (conffile); } return ret; }