]> git.wh0rd.org Git - fontconfig.git/blob - src/fccache.c
Revert ABI changes from version 2.3
[fontconfig.git] / src / fccache.c
1 /*
2  * Copyright © 2000 Keith Packard
3  * Copyright © 2005 Patrick Lam
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of Keith Packard not be used in
10  * advertising or publicity pertaining to distribution of the software without
11  * specific, written prior permission.  Keith Packard makes no
12  * representations about the suitability of this software for any purpose.  It
13  * is provided "as is" without express or implied warranty.
14  *
15  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23
24 #include "fcint.h"
25 #include "../fc-arch/fcarch.h"
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
32 #  include <unistd.h>
33 #  include <sys/mman.h>
34 #elif defined(_WIN32)
35 #  include <windows.h>
36 #endif
37
38 #ifndef O_BINARY
39 #define O_BINARY 0
40 #endif
41
42 struct MD5Context {
43         FcChar32 buf[4];
44         FcChar32 bits[2];
45         unsigned char in[64];
46 };
47
48 static void MD5Init(struct MD5Context *ctx);
49 static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len);
50 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
51 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
52
53 #define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
54
55 static const char bin2hex[] = { '0', '1', '2', '3',
56                                 '4', '5', '6', '7',
57                                 '8', '9', 'a', 'b',
58                                 'c', 'd', 'e', 'f' };
59
60 static FcChar8 *
61 FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
62 {
63     unsigned char       hash[16];
64     FcChar8             *hex_hash;
65     int                 cnt;
66     struct MD5Context   ctx;
67
68     MD5Init (&ctx);
69     MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir));
70
71     MD5Final (hash, &ctx);
72
73     cache_base[0] = '/';
74     hex_hash = cache_base + 1;
75     for (cnt = 0; cnt < 16; ++cnt)
76     {
77         hex_hash[2*cnt  ] = bin2hex[hash[cnt] >> 4];
78         hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
79     }
80     hex_hash[2*cnt] = 0;
81     strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
82
83     return cache_base;
84 }
85
86 FcBool
87 FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
88 {
89     FcChar8     *cache_hashed = NULL;
90     FcChar8     cache_base[CACHEBASE_LEN];
91     FcStrList   *list;
92     FcChar8     *cache_dir;
93
94     FcDirCacheBasename (dir, cache_base);
95
96     list = FcStrListCreate (config->cacheDirs);
97     if (!list)
98         return FcFalse;
99         
100     while ((cache_dir = FcStrListNext (list)))
101     {
102         cache_hashed = FcStrPlus (cache_dir, cache_base);
103         if (!cache_hashed)
104             break;
105         (void) unlink ((char *) cache_hashed);
106     }
107     FcStrListDone (list);
108     /* return FcFalse if something went wrong */
109     if (cache_dir)
110         return FcFalse;
111     return FcTrue;
112 }
113
114 static int
115 FcCacheReadDirs (FcConfig * config,
116                  FcStrList *list, FcFontSet * set, FcStrSet *processed_dirs)
117 {
118     int                 ret = 0;
119     FcChar8             *dir;
120     FcStrSet            *subdirs;
121     FcStrList           *sublist;
122
123     /*
124      * Read in the results from 'list'.
125      */
126     while ((dir = FcStrListNext (list)))
127     {
128         if (!FcConfigAcceptFilename (config, dir))
129             continue;
130
131         /* Skip this directory if already updated
132          * to avoid the looped directories via symlinks
133          * Clearly a dir not in fonts.conf shouldn't be globally cached.
134          */
135
136         if (FcStrSetMember (processed_dirs, dir))
137             continue;
138         if (!FcStrSetAdd (processed_dirs, dir))
139             continue;
140
141         subdirs = FcStrSetCreate ();
142         if (!subdirs)
143         {
144             fprintf (stderr, "Can't create directory set\n");
145             ret++;
146             continue;
147         }
148         
149         FcDirScanConfig (set, subdirs,
150                          config->blanks, dir, FcFalse, config);
151         
152         sublist = FcStrListCreate (subdirs);
153         FcStrSetDestroy (subdirs);
154         if (!sublist)
155         {
156             fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
157             ret++;
158             continue;
159         }
160         ret += FcCacheReadDirs (config, sublist, set, processed_dirs);
161     }
162     FcStrListDone (list);
163     return ret;
164 }
165
166 FcFontSet *
167 FcCacheRead (FcConfig *config)
168 {
169     FcFontSet   *s = FcFontSetCreate();
170     FcStrSet    *processed_dirs;
171
172     if (!s) 
173         return 0;
174
175     processed_dirs = FcStrSetCreate();
176     if (!processed_dirs)
177         goto bail;
178
179     if (FcCacheReadDirs (config, FcConfigGetConfigDirs (config), s, processed_dirs))
180         goto bail1;
181
182     FcStrSetDestroy (processed_dirs);
183     return s;
184
185  bail1:
186     FcStrSetDestroy (processed_dirs);
187  bail:
188     FcFontSetDestroy (s);
189     return 0;
190 }
191
192 /* 
193  * Look for a cache file for the specified dir. Attempt
194  * to use each one we find, stopping when the callback
195  * indicates success
196  */
197 static FcBool
198 FcDirCacheProcess (FcConfig *config, const FcChar8 *dir, 
199                    FcBool (*callback) (int fd, off_t size, void *closure),
200                    void *closure, FcChar8 **cache_file_ret)
201 {
202     int         fd = -1;
203     FcChar8     cache_base[CACHEBASE_LEN];
204     FcStrList   *list;
205     FcChar8     *cache_dir;
206     struct stat file_stat, dir_stat;
207     FcBool      ret = FcFalse;
208
209     if (stat ((char *) dir, &dir_stat) < 0)
210         return FcFalse;
211
212     FcDirCacheBasename (dir, cache_base);
213
214     list = FcStrListCreate (config->cacheDirs);
215     if (!list)
216         return FcFalse;
217         
218     while ((cache_dir = FcStrListNext (list)))
219     {
220         FcChar8 *cache_hashed = FcStrPlus (cache_dir, cache_base);
221         if (!cache_hashed)
222             break;
223         fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
224         if (fd >= 0) {
225             if (fstat (fd, &file_stat) >= 0 &&
226                 dir_stat.st_mtime <= file_stat.st_mtime)
227             {
228                 ret = (*callback) (fd, file_stat.st_size, closure);
229                 if (ret)
230                 {
231                     if (cache_file_ret)
232                         *cache_file_ret = cache_hashed;
233                     else
234                         FcStrFree (cache_hashed);
235                     close (fd);
236                     break;
237                 }
238             }
239             close (fd);
240         }
241         FcStrFree (cache_hashed);
242     }
243     FcStrListDone (list);
244     
245     return ret;
246 }
247
248 FcBool
249 FcDirCacheLoad (int fd, off_t size, void *closure)
250 {
251     FcCache     *cache;
252     FcBool      allocated = FcFalse;
253
254     if (size < sizeof (FcCache))
255         return FcFalse;
256 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
257     cache = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
258 #elif defined(_WIN32)
259     {
260         HANDLE hFileMap;
261
262         cache = NULL;
263         hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
264                                      PAGE_READONLY, 0, 0, NULL);
265         if (hFileMap != NULL)
266         {
267             cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, size);
268             CloseHandle (hFileMap);
269         }
270     }
271 #endif
272     if (!cache)
273     {
274         cache = malloc (size);
275         if (!cache)
276             return FcFalse;
277
278         if (read (fd, cache, size) != size)
279         {
280             free (cache);
281             return FcFalse;
282         }
283         allocated = FcTrue;
284     } 
285     if (cache->magic != FC_CACHE_MAGIC ||
286         cache->size != size)
287     {
288         if (allocated)
289             free (cache);
290         else
291         {
292 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
293             munmap (cache, size);
294 #elif defined(_WIN32)
295             UnmapViewOfFile (cache);
296 #endif
297         }
298         return FcFalse;
299     }
300
301     /* Mark allocated caches so they're freed rather than unmapped */
302     if (allocated)
303         cache->magic = FC_CACHE_MAGIC_COPY;
304         
305     *((FcCache **) closure) = cache;
306     return FcTrue;
307 }
308
309 FcCache *
310 FcDirCacheMap (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
311 {
312     FcCache *cache = NULL;
313
314     if (!FcDirCacheProcess (config, dir,
315                             FcDirCacheLoad,
316                             &cache, cache_file))
317         return NULL;
318     return cache;
319 }
320
321 FcBool
322 FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, 
323                 const FcChar8 *dir, FcConfig *config)
324 {
325     FcCache     *cache;
326     int         i;
327     FcFontSet   *cache_set;
328     intptr_t    *cache_dirs;
329     FcPattern   **cache_fonts;
330
331     cache = FcDirCacheMap (dir, config, NULL);
332     if (!cache)
333         return FcFalse;
334     
335     cache_set = FcCacheSet (cache);
336     cache_fonts = FcFontSetFonts(cache_set);
337     if (FcDebug() & FC_DBG_CACHE)
338         printf ("FcDirCacheRead mapped cache for %s (%d fonts %d subdirs)\n",
339                 dir, cache_set->nfont, cache->dirs_count);
340     for (i = 0; i < cache_set->nfont; i++)
341     {
342         FcPattern   *font = FcEncodedOffsetToPtr (cache_set,
343                                                   cache_fonts[i],
344                                                   FcPattern);
345         if (FcDebug() & FC_DBG_CACHEV) {
346             printf ("Mapped font %d\n", i);
347             FcPatternPrint (font);
348         }
349         FcFontSetAdd (set, font);
350     }
351     
352     cache_dirs = FcCacheDirs (cache);
353     for (i = 0; i < cache->dirs_count; i++) 
354         FcStrSetAdd (dirs, FcOffsetToPtr (cache_dirs,
355                                           cache_dirs[i],
356                                           FcChar8));
357          
358     if (config)
359         FcConfigAddFontDir (config, (FcChar8 *)dir);
360     
361     return FcTrue;
362 }
363     
364 static FcBool
365 FcDirCacheValidate (int fd, off_t size, void *closure)
366 {
367     FcBool  ret = FcTrue;
368     FcCache     c;
369     struct stat file_stat;
370     
371     if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
372         ret = FcFalse;
373     else if (fstat (fd, &file_stat) < 0)
374         ret = FcFalse;
375     else if (c.magic != FC_CACHE_MAGIC)
376         ret = FcFalse;
377     else if (file_stat.st_size != c.size)
378         ret = FcFalse;
379     return ret;
380 }
381
382 static FcBool
383 FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config)
384 {
385     return FcDirCacheProcess (config, dir, FcDirCacheValidate, NULL, NULL);
386 }
387
388 FcBool
389 FcDirCacheValid (const FcChar8 *dir)
390 {
391     FcConfig    *config;
392     
393     config = FcConfigGetCurrent ();
394     if (!config)
395         return FcFalse;
396
397     return FcDirCacheValidConfig (dir, config);
398 }
399
400 void
401 FcDirCacheUnmap (FcCache *cache)
402 {
403     if (cache->magic == FC_CACHE_MAGIC_COPY)
404     {
405         free (cache);
406         return;
407     }
408 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
409     munmap (cache, cache->size);
410 #elif defined(_WIN32)
411     UnmapViewOfFile (cache);
412 #endif
413 }
414
415 /*
416  * Cache file is:
417  *
418  * FcCache
419  * dir name
420  * subdirs
421  * FcFontSet
422  */
423
424 static FcCache *
425 FcDirCacheProduce (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs)
426 {
427     FcSerialize *serialize = FcSerializeCreate ();
428     FcCache *cache;
429     int i;
430     intptr_t    cache_offset;
431     intptr_t    dirs_offset;
432     FcChar8     *dir_serialize;
433     intptr_t    *dirs_serialize;
434     FcFontSet   *set_serialize;
435     
436     if (!serialize)
437         return NULL;
438     /*
439      * Space for cache structure
440      */
441     cache_offset = FcSerializeReserve (serialize, sizeof (FcCache));
442     /*
443      * Directory name
444      */
445     if (!FcStrSerializeAlloc (serialize, dir))
446         goto bail1;
447     /*
448      * Subdirs
449      */
450     dirs_offset = FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *));
451     for (i = 0; i < dirs->num; i++)
452         if (!FcStrSerializeAlloc (serialize, dirs->strs[i]))
453             goto bail1;
454
455     /*
456      * Patterns
457      */
458     if (!FcFontSetSerializeAlloc (serialize, set))
459         goto bail1;
460     
461     /* Serialize layout complete. Now allocate space and fill it */
462     cache = malloc (serialize->size);
463     if (!cache)
464         goto bail1;
465     /* shut up valgrind */
466     memset (cache, 0, serialize->size);
467
468     serialize->linear = cache;
469
470     cache->magic = FC_CACHE_MAGIC;
471     cache->size = serialize->size;
472
473     /*
474      * Serialize directory name
475      */
476     dir_serialize = FcStrSerialize (serialize, dir);
477     if (!dir_serialize)
478         goto bail2;
479     cache->dir = FcPtrToOffset (cache, dir_serialize);
480     
481     /*
482      * Serialize sub dirs
483      */
484     dirs_serialize = FcSerializePtr (serialize, dirs);
485     if (!dirs_serialize)
486         goto bail2;
487     cache->dirs = FcPtrToOffset (cache, dirs_serialize);
488     cache->dirs_count = dirs->num;
489     for (i = 0; i < dirs->num; i++) 
490     {
491         FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]);
492         if (!d_serialize)
493             goto bail2;
494         dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize);
495     }
496     
497     /*
498      * Serialize font set
499      */
500     set_serialize = FcFontSetSerialize (serialize, set);
501     if (!set_serialize)
502         goto bail2;
503     cache->set = FcPtrToOffset (cache, set_serialize);
504
505     FcSerializeDestroy (serialize);
506     
507     return cache;
508
509 bail2:
510     free (cache);
511 bail1:
512     FcSerializeDestroy (serialize);
513     return NULL;
514 }
515
516 static FcBool
517 FcMakeDirectory (const FcChar8 *dir)
518 {
519     FcChar8 *parent;
520     FcBool  ret;
521     
522     if (strlen ((char *) dir) == 0)
523         return FcFalse;
524     
525     parent = FcStrDirname (dir);
526     if (!parent)
527         return FcFalse;
528     if (access ((char *) parent, W_OK|X_OK) == 0)
529         ret = mkdir ((char *) dir, 0777) == 0;
530     else if (access ((char *) parent, F_OK) == -1)
531         ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0777) == 0);
532     else
533         ret = FcFalse;
534     FcStrFree (parent);
535     return ret;
536 }
537
538 /* write serialized state to the cache file */
539 FcBool
540 FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config)
541 {
542     FcChar8         cache_base[CACHEBASE_LEN];
543     FcChar8         *cache_hashed;
544     int             fd;
545     FcAtomic        *atomic;
546     FcCache         *cache;
547     FcStrList       *list;
548     FcChar8         *cache_dir = NULL;
549     FcChar8         *test_dir;
550
551     /*
552      * Write it to the first directory in the list which is writable
553      */
554     
555     list = FcStrListCreate (config->cacheDirs);
556     if (!list)
557         return FcFalse;
558     while ((test_dir = FcStrListNext (list))) {
559         if (access ((char *) test_dir, W_OK|X_OK) == 0)
560         {
561             cache_dir = test_dir;
562             break;
563         }
564         else
565         {
566             /*
567              * If the directory doesn't exist, try to create it
568              */
569             if (access ((char *) test_dir, F_OK) == -1) {
570                 if (FcMakeDirectory (test_dir))
571                 {
572                     cache_dir = test_dir;
573                     break;
574                 }
575             }
576         }
577     }
578     FcStrListDone (list);
579     if (!cache_dir)
580         return FcFalse;
581
582     FcDirCacheBasename (dir, cache_base);
583     cache_hashed = FcStrPlus (cache_dir, cache_base);
584     if (!cache_hashed)
585         return FcFalse;
586
587     cache = FcDirCacheProduce (set, dir, dirs);
588
589     if (!cache)
590         goto bail1;
591
592     if (FcDebug () & FC_DBG_CACHE)
593         printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
594                 dir, cache_hashed);
595
596     atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
597     if (!atomic)
598         goto bail2;
599
600     if (!FcAtomicLock (atomic))
601         goto bail3;
602
603     fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
604     if (fd == -1)
605         goto bail4;
606     
607     if (write (fd, cache, cache->size) != cache->size)
608     {
609         perror ("write cache");
610         goto bail5;
611     }
612
613     close(fd);
614     if (!FcAtomicReplaceOrig(atomic))
615         goto bail4;
616     FcStrFree ((FcChar8 *)cache_hashed);
617     FcAtomicUnlock (atomic);
618     FcAtomicDestroy (atomic);
619     return FcTrue;
620
621  bail5:
622     close (fd);
623  bail4:
624     FcAtomicUnlock (atomic);
625  bail3:
626     FcAtomicDestroy (atomic);
627  bail2:
628     free (cache);
629  bail1:
630     FcStrFree ((FcChar8 *)cache_hashed);
631     return FcFalse;
632 }
633
634 /*
635  * This code implements the MD5 message-digest algorithm.
636  * The algorithm is due to Ron Rivest.  This code was
637  * written by Colin Plumb in 1993, no copyright is claimed.
638  * This code is in the public domain; do with it what you wish.
639  *
640  * Equivalent code is available from RSA Data Security, Inc.
641  * This code has been tested against that, and is equivalent,
642  * except that you don't need to include two pages of legalese
643  * with every copy.
644  *
645  * To compute the message digest of a chunk of bytes, declare an
646  * MD5Context structure, pass it to MD5Init, call MD5Update as
647  * needed on buffers full of bytes, and then call MD5Final, which
648  * will fill a supplied 16-byte array with the digest.
649  */
650
651 #ifndef HIGHFIRST
652 #define byteReverse(buf, len)   /* Nothing */
653 #else
654 /*
655  * Note: this code is harmless on little-endian machines.
656  */
657 void byteReverse(unsigned char *buf, unsigned longs)
658 {
659     FcChar32 t;
660     do {
661         t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
662             ((unsigned) buf[1] << 8 | buf[0]);
663         *(FcChar32 *) buf = t;
664         buf += 4;
665     } while (--longs);
666 }
667 #endif
668
669 /*
670  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
671  * initialization constants.
672  */
673 static void MD5Init(struct MD5Context *ctx)
674 {
675     ctx->buf[0] = 0x67452301;
676     ctx->buf[1] = 0xefcdab89;
677     ctx->buf[2] = 0x98badcfe;
678     ctx->buf[3] = 0x10325476;
679
680     ctx->bits[0] = 0;
681     ctx->bits[1] = 0;
682 }
683
684 /*
685  * Update context to reflect the concatenation of another buffer full
686  * of bytes.
687  */
688 static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len)
689 {
690     FcChar32 t;
691
692     /* Update bitcount */
693
694     t = ctx->bits[0];
695     if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t)
696         ctx->bits[1]++;         /* Carry from low to high */
697     ctx->bits[1] += len >> 29;
698
699     t = (t >> 3) & 0x3f;        /* Bytes already in shsInfo->data */
700
701     /* Handle any leading odd-sized chunks */
702
703     if (t) {
704         unsigned char *p = (unsigned char *) ctx->in + t;
705
706         t = 64 - t;
707         if (len < t) {
708             memcpy(p, buf, len);
709             return;
710         }
711         memcpy(p, buf, t);
712         byteReverse(ctx->in, 16);
713         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
714         buf += t;
715         len -= t;
716     }
717     /* Process data in 64-byte chunks */
718
719     while (len >= 64) {
720         memcpy(ctx->in, buf, 64);
721         byteReverse(ctx->in, 16);
722         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
723         buf += 64;
724         len -= 64;
725     }
726
727     /* Handle any remaining bytes of data. */
728
729     memcpy(ctx->in, buf, len);
730 }
731
732 /*
733  * Final wrapup - pad to 64-byte boundary with the bit pattern 
734  * 1 0* (64-bit count of bits processed, MSB-first)
735  */
736 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
737 {
738     unsigned count;
739     unsigned char *p;
740
741     /* Compute number of bytes mod 64 */
742     count = (ctx->bits[0] >> 3) & 0x3F;
743
744     /* Set the first char of padding to 0x80.  This is safe since there is
745        always at least one byte free */
746     p = ctx->in + count;
747     *p++ = 0x80;
748
749     /* Bytes of padding needed to make 64 bytes */
750     count = 64 - 1 - count;
751
752     /* Pad out to 56 mod 64 */
753     if (count < 8) {
754         /* Two lots of padding:  Pad the first block to 64 bytes */
755         memset(p, 0, count);
756         byteReverse(ctx->in, 16);
757         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
758
759         /* Now fill the next block with 56 bytes */
760         memset(ctx->in, 0, 56);
761     } else {
762         /* Pad block to 56 bytes */
763         memset(p, 0, count - 8);
764     }
765     byteReverse(ctx->in, 14);
766
767     /* Append length in bits and transform */
768     ((FcChar32 *) ctx->in)[14] = ctx->bits[0];
769     ((FcChar32 *) ctx->in)[15] = ctx->bits[1];
770
771     MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
772     byteReverse((unsigned char *) ctx->buf, 4);
773     memcpy(digest, ctx->buf, 16);
774     memset(ctx, 0, sizeof(ctx));        /* In case it's sensitive */
775 }
776
777
778 /* The four core functions - F1 is optimized somewhat */
779
780 /* #define F1(x, y, z) (x & y | ~x & z) */
781 #define F1(x, y, z) (z ^ (x & (y ^ z)))
782 #define F2(x, y, z) F1(z, x, y)
783 #define F3(x, y, z) (x ^ y ^ z)
784 #define F4(x, y, z) (y ^ (x | ~z))
785
786 /* This is the central step in the MD5 algorithm. */
787 #define MD5STEP(f, w, x, y, z, data, s) \
788         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
789
790 /*
791  * The core of the MD5 algorithm, this alters an existing MD5 hash to
792  * reflect the addition of 16 longwords of new data.  MD5Update blocks
793  * the data and converts bytes into longwords for this routine.
794  */
795 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16])
796 {
797     register FcChar32 a, b, c, d;
798
799     a = buf[0];
800     b = buf[1];
801     c = buf[2];
802     d = buf[3];
803
804     MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
805     MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
806     MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
807     MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
808     MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
809     MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
810     MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
811     MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
812     MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
813     MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
814     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
815     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
816     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
817     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
818     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
819     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
820
821     MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
822     MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
823     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
824     MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
825     MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
826     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
827     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
828     MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
829     MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
830     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
831     MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
832     MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
833     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
834     MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
835     MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
836     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
837
838     MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
839     MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
840     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
841     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
842     MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
843     MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
844     MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
845     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
846     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
847     MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
848     MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
849     MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
850     MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
851     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
852     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
853     MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
854
855     MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
856     MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
857     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
858     MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
859     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
860     MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
861     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
862     MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
863     MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
864     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
865     MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
866     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
867     MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
868     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
869     MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
870     MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
871
872     buf[0] += a;
873     buf[1] += b;
874     buf[2] += c;
875     buf[3] += d;
876 }