| /* |
| * $XFree86: $ |
| * |
| * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. |
| * |
| * 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 Keith Packard not be used in |
| * advertising or publicity pertaining to distribution of the software without |
| * specific, written prior permission. Keith Packard makes no |
| * representations about the suitability of this software for any purpose. It |
| * is provided "as is" without express or implied warranty. |
| * |
| * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| * EVENT SHALL KEITH PACKARD 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 "fcint.h" |
| |
| static unsigned int |
| FcFileCacheHash (const char *string) |
| { |
| unsigned int h = 0; |
| char c; |
| |
| while ((c = *string++)) |
| h = (h << 1) ^ c; |
| return h; |
| } |
| |
| char * |
| FcFileCacheFind (FcFileCache *cache, |
| const char *file, |
| int id, |
| int *count) |
| { |
| unsigned int hash; |
| const char *match; |
| FcFileCacheEnt *c, *name; |
| int maxid; |
| struct stat statb; |
| |
| match = file; |
| |
| hash = FcFileCacheHash (match); |
| name = 0; |
| maxid = -1; |
| for (c = cache->ents[hash % FC_FILE_CACHE_HASH_SIZE]; c; c = c->next) |
| { |
| if (c->hash == hash && !strcmp (match, c->file)) |
| { |
| if (c->id > maxid) |
| maxid = c->id; |
| if (c->id == id) |
| { |
| if (stat (file, &statb) < 0) |
| { |
| if (FcDebug () & FC_DBG_CACHE) |
| printf (" file missing\n"); |
| return 0; |
| } |
| if (statb.st_mtime != c->time) |
| { |
| if (FcDebug () & FC_DBG_CACHE) |
| printf (" timestamp mismatch (was %d is %d)\n", |
| (int) c->time, (int) statb.st_mtime); |
| return 0; |
| } |
| if (!c->referenced) |
| { |
| cache->referenced++; |
| c->referenced = FcTrue; |
| } |
| name = c; |
| } |
| } |
| } |
| if (!name) |
| return 0; |
| *count = maxid + 1; |
| return name->name; |
| } |
| |
| /* |
| * Cache file syntax is quite simple: |
| * |
| * "file_name" id time "font_name" \n |
| */ |
| |
| static FcBool |
| FcFileCacheReadString (FILE *f, char *dest, int len) |
| { |
| int c; |
| FcBool escape; |
| |
| while ((c = getc (f)) != EOF) |
| if (c == '"') |
| break; |
| if (c == EOF) |
| return FcFalse; |
| if (len == 0) |
| return FcFalse; |
| |
| escape = FcFalse; |
| while ((c = getc (f)) != EOF) |
| { |
| if (!escape) |
| { |
| switch (c) { |
| case '"': |
| *dest++ = '\0'; |
| return FcTrue; |
| case '\\': |
| escape = FcTrue; |
| continue; |
| } |
| } |
| if (--len <= 1) |
| return FcFalse; |
| *dest++ = c; |
| escape = FcFalse; |
| } |
| return FcFalse; |
| } |
| |
| static FcBool |
| FcFileCacheReadUlong (FILE *f, unsigned long *dest) |
| { |
| unsigned long t; |
| int c; |
| |
| while ((c = getc (f)) != EOF) |
| { |
| if (!isspace (c)) |
| break; |
| } |
| if (c == EOF) |
| return FcFalse; |
| t = 0; |
| for (;;) |
| { |
| if (c == EOF || isspace (c)) |
| break; |
| if (!isdigit (c)) |
| return FcFalse; |
| t = t * 10 + (c - '0'); |
| c = getc (f); |
| } |
| *dest = t; |
| return FcTrue; |
| } |
| |
| static FcBool |
| FcFileCacheReadInt (FILE *f, int *dest) |
| { |
| unsigned long t; |
| FcBool ret; |
| |
| ret = FcFileCacheReadUlong (f, &t); |
| if (ret) |
| *dest = (int) t; |
| return ret; |
| } |
| |
| static FcBool |
| FcFileCacheReadTime (FILE *f, time_t *dest) |
| { |
| unsigned long t; |
| FcBool ret; |
| |
| ret = FcFileCacheReadUlong (f, &t); |
| if (ret) |
| *dest = (time_t) t; |
| return ret; |
| } |
| |
| static FcBool |
| FcFileCacheAdd (FcFileCache *cache, |
| const char *file, |
| int id, |
| time_t time, |
| const char *name, |
| FcBool replace) |
| { |
| FcFileCacheEnt *c; |
| FcFileCacheEnt **prev, *old; |
| unsigned int hash; |
| |
| if (FcDebug () & FC_DBG_CACHE) |
| { |
| printf ("%s face %s/%d as %s\n", replace ? "Replace" : "Add", |
| file, id, name); |
| } |
| hash = FcFileCacheHash (file); |
| for (prev = &cache->ents[hash % FC_FILE_CACHE_HASH_SIZE]; |
| (old = *prev); |
| prev = &(*prev)->next) |
| { |
| if (old->hash == hash && old->id == id && !strcmp (old->file, file)) |
| break; |
| } |
| if (*prev) |
| { |
| if (!replace) |
| return FcFalse; |
| |
| old = *prev; |
| if (old->referenced) |
| cache->referenced--; |
| *prev = old->next; |
| free (old); |
| cache->entries--; |
| } |
| |
| c = malloc (sizeof (FcFileCacheEnt) + |
| strlen (file) + 1 + |
| strlen (name) + 1); |
| if (!c) |
| return FcFalse; |
| c->next = *prev; |
| *prev = c; |
| c->hash = hash; |
| c->file = (char *) (c + 1); |
| c->id = id; |
| c->name = c->file + strlen (file) + 1; |
| strcpy (c->file, file); |
| c->time = time; |
| c->referenced = replace; |
| strcpy (c->name, name); |
| cache->entries++; |
| return FcTrue; |
| } |
| |
| FcFileCache * |
| FcFileCacheCreate (void) |
| { |
| FcFileCache *cache; |
| int h; |
| |
| cache = malloc (sizeof (FcFileCache)); |
| if (!cache) |
| return 0; |
| for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++) |
| cache->ents[h] = 0; |
| cache->entries = 0; |
| cache->referenced = 0; |
| cache->updated = FcFalse; |
| return cache; |
| } |
| |
| void |
| FcFileCacheDestroy (FcFileCache *cache) |
| { |
| FcFileCacheEnt *c, *next; |
| int h; |
| |
| for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++) |
| { |
| for (c = cache->ents[h]; c; c = next) |
| { |
| next = c->next; |
| free (c); |
| } |
| } |
| free (cache); |
| } |
| |
| void |
| FcFileCacheLoad (FcFileCache *cache, |
| const char *cache_file) |
| { |
| FILE *f; |
| char file[8192]; |
| int id; |
| time_t time; |
| char name[8192]; |
| |
| f = fopen (cache_file, "r"); |
| if (!f) |
| return; |
| |
| cache->updated = FcFalse; |
| while (FcFileCacheReadString (f, file, sizeof (file)) && |
| FcFileCacheReadInt (f, &id) && |
| FcFileCacheReadTime (f, &time) && |
| FcFileCacheReadString (f, name, sizeof (name))) |
| { |
| (void) FcFileCacheAdd (cache, file, id, time, name, FcFalse); |
| } |
| fclose (f); |
| } |
| |
| FcBool |
| FcFileCacheUpdate (FcFileCache *cache, |
| const char *file, |
| int id, |
| const char *name) |
| { |
| const char *match; |
| struct stat statb; |
| FcBool ret; |
| |
| match = file; |
| |
| if (stat (file, &statb) < 0) |
| return FcFalse; |
| ret = FcFileCacheAdd (cache, match, id, |
| statb.st_mtime, name, FcTrue); |
| if (ret) |
| cache->updated = FcTrue; |
| return ret; |
| } |
| |
| static FcBool |
| FcFileCacheWriteString (FILE *f, char *string) |
| { |
| char c; |
| |
| if (putc ('"', f) == EOF) |
| return FcFalse; |
| while ((c = *string++)) |
| { |
| switch (c) { |
| case '"': |
| case '\\': |
| if (putc ('\\', f) == EOF) |
| return FcFalse; |
| /* fall through */ |
| default: |
| if (putc (c, f) == EOF) |
| return FcFalse; |
| } |
| } |
| if (putc ('"', f) == EOF) |
| return FcFalse; |
| return FcTrue; |
| } |
| |
| static FcBool |
| FcFileCacheWriteUlong (FILE *f, unsigned long t) |
| { |
| int pow; |
| unsigned long temp, digit; |
| |
| temp = t; |
| pow = 1; |
| while (temp >= 10) |
| { |
| temp /= 10; |
| pow *= 10; |
| } |
| temp = t; |
| while (pow) |
| { |
| digit = temp / pow; |
| if (putc ((char) digit + '0', f) == EOF) |
| return FcFalse; |
| temp = temp - pow * digit; |
| pow = pow / 10; |
| } |
| return FcTrue; |
| } |
| |
| static FcBool |
| FcFileCacheWriteInt (FILE *f, int i) |
| { |
| return FcFileCacheWriteUlong (f, (unsigned long) i); |
| } |
| |
| static FcBool |
| FcFileCacheWriteTime (FILE *f, time_t t) |
| { |
| return FcFileCacheWriteUlong (f, (unsigned long) t); |
| } |
| |
| FcBool |
| FcFileCacheSave (FcFileCache *cache, |
| const char *cache_file) |
| { |
| char *lck; |
| char *tmp; |
| FILE *f; |
| int h; |
| FcFileCacheEnt *c; |
| |
| if (!cache->updated && cache->referenced == cache->entries) |
| return FcTrue; |
| |
| lck = malloc (strlen (cache_file)*2 + 4); |
| if (!lck) |
| goto bail0; |
| tmp = lck + strlen (cache_file) + 2; |
| strcpy (lck, cache_file); |
| strcat (lck, "L"); |
| strcpy (tmp, cache_file); |
| strcat (tmp, "T"); |
| if (link (lck, cache_file) < 0 && errno != ENOENT) |
| goto bail1; |
| if (access (tmp, F_OK) == 0) |
| goto bail2; |
| f = fopen (tmp, "w"); |
| if (!f) |
| goto bail2; |
| |
| for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++) |
| { |
| for (c = cache->ents[h]; c; c = c->next) |
| { |
| if (!c->referenced) |
| continue; |
| if (!FcFileCacheWriteString (f, c->file)) |
| goto bail4; |
| if (putc (' ', f) == EOF) |
| goto bail4; |
| if (!FcFileCacheWriteInt (f, c->id)) |
| goto bail4; |
| if (putc (' ', f) == EOF) |
| goto bail4; |
| if (!FcFileCacheWriteTime (f, c->time)) |
| goto bail4; |
| if (putc (' ', f) == EOF) |
| goto bail4; |
| if (!FcFileCacheWriteString (f, c->name)) |
| goto bail4; |
| if (putc ('\n', f) == EOF) |
| goto bail4; |
| } |
| } |
| |
| if (fclose (f) == EOF) |
| goto bail3; |
| |
| if (rename (tmp, cache_file) < 0) |
| goto bail3; |
| |
| unlink (lck); |
| cache->updated = FcFalse; |
| return FcTrue; |
| |
| bail4: |
| fclose (f); |
| bail3: |
| unlink (tmp); |
| bail2: |
| unlink (lck); |
| bail1: |
| free (lck); |
| bail0: |
| return FcFalse; |
| } |
| |
| FcBool |
| FcFileCacheReadDir (FcFontSet *set, const char *cache_file) |
| { |
| FcPattern *font; |
| FILE *f; |
| char *path; |
| char *base; |
| char file[8192]; |
| int id; |
| char name[8192]; |
| FcBool ret = FcFalse; |
| |
| if (FcDebug () & FC_DBG_CACHE) |
| { |
| printf ("FcFileCacheReadDir cache_file \"%s\"\n", cache_file); |
| } |
| |
| f = fopen (cache_file, "r"); |
| if (!f) |
| { |
| if (FcDebug () & FC_DBG_CACHE) |
| { |
| printf (" no cache file\n"); |
| } |
| goto bail0; |
| } |
| |
| base = strrchr (cache_file, '/'); |
| if (!base) |
| goto bail1; |
| base++; |
| path = malloc (base - cache_file + 8192 + 1); |
| if (!path) |
| goto bail1; |
| memcpy (path, cache_file, base - cache_file); |
| base = path + (base - cache_file); |
| |
| while (FcFileCacheReadString (f, file, sizeof (file)) && |
| FcFileCacheReadInt (f, &id) && |
| FcFileCacheReadString (f, name, sizeof (name))) |
| { |
| font = FcNameParse (name); |
| if (font) |
| { |
| strcpy (base, file); |
| if (FcDebug () & FC_DBG_CACHEV) |
| { |
| printf (" dir cache file \"%s\"\n", file); |
| } |
| FcPatternAddString (font, FC_FILE, path); |
| if (!FcFontSetAdd (set, font)) |
| goto bail2; |
| } |
| } |
| if (FcDebug () & FC_DBG_CACHE) |
| { |
| printf (" cache loaded\n"); |
| } |
| |
| ret = FcTrue; |
| bail2: |
| free (path); |
| bail1: |
| fclose (f); |
| bail0: |
| return ret; |
| } |
| |
| FcBool |
| FcFileCacheWriteDir (FcFontSet *set, const char *cache_file) |
| { |
| FcPattern *font; |
| FILE *f; |
| char *name; |
| char *file, *base; |
| int n; |
| int id; |
| FcBool ret; |
| |
| if (FcDebug () & FC_DBG_CACHE) |
| printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file); |
| |
| f = fopen (cache_file, "w"); |
| if (!f) |
| { |
| if (FcDebug () & FC_DBG_CACHE) |
| printf (" can't create \"%s\"\n", cache_file); |
| goto bail0; |
| } |
| for (n = 0; n < set->nfont; n++) |
| { |
| font = set->fonts[n]; |
| if (FcPatternGetString (font, FC_FILE, 0, &file) != FcResultMatch) |
| goto bail1; |
| base = strrchr (file, '/'); |
| if (base) |
| base = base + 1; |
| else |
| base = file; |
| if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch) |
| goto bail1; |
| if (FcDebug () & FC_DBG_CACHEV) |
| printf (" write file \"%s\"\n", base); |
| if (!FcFileCacheWriteString (f, base)) |
| goto bail1; |
| if (putc (' ', f) == EOF) |
| goto bail1; |
| if (!FcFileCacheWriteInt (f, id)) |
| goto bail1; |
| if (putc (' ', f) == EOF) |
| goto bail1; |
| name = FcNameUnparse (font); |
| if (!name) |
| goto bail1; |
| ret = FcFileCacheWriteString (f, name); |
| free (name); |
| if (!ret) |
| goto bail1; |
| if (putc ('\n', f) == EOF) |
| goto bail1; |
| } |
| if (fclose (f) == EOF) |
| goto bail0; |
| |
| if (FcDebug () & FC_DBG_CACHE) |
| printf (" cache written\n"); |
| return FcTrue; |
| |
| bail1: |
| fclose (f); |
| bail0: |
| unlink (cache_file); |
| return FcFalse; |
| } |