Another typo.
[framework/uifw/eet.git] / src / lib / eet_lib.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #ifdef HAVE_ALLOCA_H
10 # include <alloca.h>
11 #elif defined __GNUC__
12 # define alloca __builtin_alloca
13 #elif defined _AIX
14 # define alloca __alloca
15 #elif defined _MSC_VER
16 # include <malloc.h>
17 # define alloca _alloca
18 #else
19 # include <stddef.h>
20 # ifdef  __cplusplus
21 extern "C"
22 # endif
23 void *alloca (size_t);
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <time.h>
32 #include <string.h>
33 #include <fnmatch.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <zlib.h>
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #if defined(_WIN32) && ! defined(__CEGCC__)
43 # include <winsock2.h>
44 #endif
45
46 #ifdef HAVE_EVIL
47 # include <Evil.h>
48 #endif
49
50 #include "Eet.h"
51 #include "Eet_private.h"
52
53 #ifdef HAVE_REALPATH
54 #undef HAVE_REALPATH
55 #endif
56
57 #define EET_MAGIC_FILE                  0x1ee7ff00
58 #define EET_MAGIC_FILE_HEADER           0x1ee7ff01
59
60 #define EET_MAGIC_FILE2                 0x1ee70f42
61
62 typedef struct _Eet_File_Header         Eet_File_Header;
63 typedef struct _Eet_File_Node           Eet_File_Node;
64 typedef struct _Eet_File_Directory      Eet_File_Directory;
65
66 struct _Eet_File
67 {
68    char                 *path;
69    FILE                 *fp;
70    FILE                 *readfp;
71    Eet_File_Header      *header;
72    Eet_Dictionary       *ed;
73    Eet_Key              *key;
74    const unsigned char  *data;
75    const void           *x509_der;
76
77    int                   magic;
78    int                   references;
79
80    Eet_File_Mode         mode;
81    int                   data_size;
82    int                   x509_length;
83    time_t                mtime;
84
85    unsigned char         writes_pending : 1;
86    unsigned char         delete_me_now : 1;
87 };
88
89 struct _Eet_File_Header
90 {
91    int                 magic;
92    Eet_File_Directory *directory;
93 };
94
95 struct _Eet_File_Directory
96 {
97    int             size;
98    Eet_File_Node **nodes;
99 };
100
101 struct _Eet_File_Node
102 {
103    char                 *name;
104    void                 *data;
105    Eet_File_Node        *next; /* FIXME: make buckets linked lists */
106
107    int                   offset;
108    int                   dictionary_offset;
109    int                   name_offset;
110
111    int                   name_size;
112    int                   size;
113    int                   data_size;
114
115    unsigned char         free_name : 1;
116    unsigned char         compression : 1;
117 };
118
119 #if 0
120 /* Version 2 */
121 /* NB: all int's are stored in network byte order on disk */
122 /* file format: */
123 int magic; /* magic number ie 0x1ee7ff00 */
124 int num_directory_entries; /* number of directory entries to follow */
125 int bytes_directory_entries; /* bytes of directory entries to follow */
126 struct
127 {
128    int offset; /* bytes offset into file for data chunk */
129    int flags; /* flags - for now 0 = uncompressed, 1 = compressed */
130    int size; /* size of the data chunk */
131    int data_size; /* size of the (uncompressed) data chunk */
132    int name_size; /* length in bytes of the name field */
133    char name[name_size]; /* name string (variable length) and \0 terminated */
134 } directory[num_directory_entries];
135 /* and now startes the data stream... */
136 #endif
137
138 #if 0
139 /* Version 3 */
140 /* NB: all int's are stored in network byte order on disk */
141 /* file format: */
142 int magic; /* magic number ie 0x1ee70f42 */
143 int num_directory_entries; /* number of directory entries to follow */
144 int num_dictionary_entries; /* number of dictionary entries to follow */
145 struct
146 {
147   int data_offset; /* bytes offset into file for data chunk */
148   int size; /* size of the data chunk */
149   int data_size; /* size of the (uncompressed) data chunk */
150   int name_offset; /* bytes offset into file for name string */
151   int name_size; /* length in bytes of the name field */
152   int flags; /* flags - for now 0 = uncompressed, 1 = compressed */
153 } directory[num_directory_entries];
154 struct
155 {
156   int hash;
157   int offset;
158   int size;
159   int prev;
160   int next;
161 } dictionary[num_dictionary_entries];
162 /* now start the string stream. */
163 /* and right after them the data stream. */
164 int magic_sign; /* Optional, only if the eet file is signed. */
165 int signature_length; /* Signature length. */
166 int x509_length; /* Public certificate that signed the file. */
167 char signature[signature_length]; /* The signature. */
168 char x509[x509_length]; /* The public certificate. */
169 #endif
170
171 #define EET_FILE2_HEADER_COUNT                  3
172 #define EET_FILE2_DIRECTORY_ENTRY_COUNT         6
173 #define EET_FILE2_DICTIONARY_ENTRY_COUNT        5
174
175 #define EET_FILE2_HEADER_SIZE                   (sizeof(int) * EET_FILE2_HEADER_COUNT)
176 #define EET_FILE2_DIRECTORY_ENTRY_SIZE          (sizeof(int) * EET_FILE2_DIRECTORY_ENTRY_COUNT)
177 #define EET_FILE2_DICTIONARY_ENTRY_SIZE         (sizeof(int) * EET_FILE2_DICTIONARY_ENTRY_COUNT)
178
179 /* prototypes of internal calls */
180 static Eet_File         *eet_cache_find(const char *path, Eet_File **cache, int cache_num);
181 static void             eet_cache_add(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc);
182 static void             eet_cache_del(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc);
183 static int              eet_string_match(const char *s1, const char *s2);
184 #if 0 /* Unused */
185 static Eet_Error        eet_flush(Eet_File *ef);
186 #endif
187 static Eet_Error        eet_flush2(Eet_File *ef);
188 static Eet_File_Node    *find_node_by_name(Eet_File *ef, const char *name);
189 static int              read_data_from_disk(Eet_File *ef, Eet_File_Node *efn, void *buf, int len);
190
191 /* cache. i don't expect this to ever be large, so arrays will do */
192 static int        eet_writers_num     = 0;
193 static int        eet_writers_alloc   = 0;
194 static Eet_File **eet_writers         = NULL;
195 static int        eet_readers_num     = 0;
196 static int        eet_readers_alloc   = 0;
197 static Eet_File **eet_readers         = NULL;
198 static int        eet_initcount       = 0;
199
200 /* Check to see its' an eet file pointer */
201 static inline int
202 eet_check_pointer(const Eet_File *ef)
203 {
204   if ((!ef) || (ef->magic != EET_MAGIC_FILE))
205     return 1;
206   return 0;
207 }
208
209 static inline int
210 eet_check_header(const Eet_File *ef)
211 {
212    if (!ef->header)
213      return 1;
214    if (!ef->header->directory)
215      return 1;
216    return 0;
217 }
218
219 static inline int
220 eet_test_close(int test, Eet_File *ef)
221 {
222    if (test)
223      {
224         ef->delete_me_now = 1;
225         eet_close(ef);
226      }
227    return test;
228 }
229
230 /* find an eet file in the currently in use cache */
231 static Eet_File *
232 eet_cache_find(const char *path, Eet_File **cache, int cache_num)
233 {
234    int i;
235
236    /* walk list */
237    for (i = 0; i < cache_num; i++)
238      {
239         /* if matches real path - return it */
240         if (eet_string_match(cache[i]->path, path))
241           {
242              if (!cache[i]->delete_me_now)
243                return cache[i];
244           }
245      }
246
247    /* not found */
248    return NULL;
249 }
250
251 /* add to end of cache */
252 static void
253 eet_cache_add(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc)
254 {
255    Eet_File     **new_cache;
256    int          new_cache_num;
257    int          new_cache_alloc;
258
259    new_cache_num = *cache_num;
260    if (new_cache_num >= 64) /* avoid fd overruns - limit to 128 (most recent) in the cache */
261      {
262         Eet_File        *del_ef = NULL;
263         int             i;
264
265         new_cache = *cache;
266         for (i = 0; i < new_cache_num; i++)
267           {
268              if (new_cache[i]->references == 0)
269                {
270                   del_ef = new_cache[i];
271                   break;
272                }
273           }
274
275         if (del_ef)
276           {
277              del_ef->delete_me_now = 1;
278              eet_close(del_ef);
279           }
280      }
281
282    new_cache = *cache;
283    new_cache_num = *cache_num;
284    new_cache_alloc = *cache_alloc;
285    new_cache_num++;
286    if (new_cache_num > new_cache_alloc)
287      {
288         new_cache_alloc += 16;
289         new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
290         if (!new_cache)
291           {
292              fprintf(stderr, "BAD ERROR! Eet realloc of cache list failed. Abort\n");
293              abort();
294           }
295      }
296    new_cache[new_cache_num - 1] = ef;
297    *cache = new_cache;
298    *cache_num = new_cache_num;
299    *cache_alloc = new_cache_alloc;
300 }
301
302 /* delete from cache */
303 static void
304 eet_cache_del(Eet_File *ef, Eet_File ***cache, int *cache_num, int *cache_alloc)
305 {
306    Eet_File **new_cache;
307    int new_cache_num, new_cache_alloc;
308    int i, j;
309
310    new_cache = *cache;
311    new_cache_num = *cache_num;
312    new_cache_alloc = *cache_alloc;
313    if (new_cache_num <= 0)
314      return;
315
316    for (i = 0; i < new_cache_num; i++)
317      {
318         if (new_cache[i] == ef)
319           break;
320      }
321
322    if (i >= new_cache_num)
323      return;
324
325    new_cache_num--;
326    for (j = i; j < new_cache_num; j++)
327      new_cache[j] = new_cache[j + 1];
328
329    if (new_cache_num <= (new_cache_alloc - 16))
330      {
331         new_cache_alloc -= 16;
332         if (new_cache_num > 0)
333           {
334              new_cache = realloc(new_cache, new_cache_alloc * sizeof(Eet_File *));
335              if (!new_cache)
336                {
337                   fprintf(stderr, "BAD ERROR! Eet realloc of cache list failed. Abort\n");
338                   abort();
339                }
340           }
341         else
342           {
343              free(new_cache);
344              new_cache = NULL;
345           }
346      }
347    *cache = new_cache;
348    *cache_num = new_cache_num;
349    *cache_alloc = new_cache_alloc;
350 }
351
352 /* internal string match. null friendly, catches same ptr */
353 static int
354 eet_string_match(const char *s1, const char *s2)
355 {
356    /* both null- no match */
357    if ((!s1) || (!s2)) return 0;
358    if (s1 == s2) return 1;
359    return (!strcmp(s1, s2));
360 }
361
362 /* flush out writes to a v2 eet file */
363 static Eet_Error
364 eet_flush2(Eet_File *ef)
365 {
366    Eet_File_Node        *efn;
367    Eet_Error             error = EET_ERROR_NONE;
368    int                   head[EET_FILE2_HEADER_COUNT];
369    int                   num_directory_entries = 0;
370    int                   num_dictionary_entries = 0;
371    int                   bytes_directory_entries = 0;
372    int                   bytes_dictionary_entries = 0;
373    int                   bytes_strings = 0;
374    int                   data_offset = 0;
375    int                   strings_offset = 0;
376    int                   num;
377    int                   i;
378    int                   j;
379
380    if (eet_check_pointer(ef))
381      return EET_ERROR_BAD_OBJECT;
382    if (eet_check_header(ef))
383      return EET_ERROR_EMPTY;
384    if ((ef->mode != EET_FILE_MODE_WRITE) && (ef->mode != EET_FILE_MODE_READ_WRITE))
385      return EET_ERROR_NOT_WRITABLE;
386    if (!ef->writes_pending)
387      return EET_ERROR_NONE;
388    if (ef->mode == EET_FILE_MODE_READ_WRITE && ef->fp == NULL)
389      {
390         int fd;
391
392         unlink(ef->path);
393         fd = open(ef->path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
394         ef->fp = fdopen(fd, "wb");
395         if (!ef->fp) return EET_ERROR_NOT_WRITABLE;
396         fcntl(fileno(ef->fp), F_SETFD, FD_CLOEXEC);
397      }
398
399    /* calculate string base offset and data base offset */
400    num = (1 << ef->header->directory->size);
401    for (i = 0; i < num; ++i)
402      {
403         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
404           {
405              num_directory_entries++;
406              bytes_strings += strlen(efn->name) + 1;
407           }
408      }
409    if (ef->ed)
410      {
411         num_dictionary_entries = ef->ed->count;
412
413         for (i = 0; i < num_dictionary_entries; ++i)
414           bytes_strings += ef->ed->all[i].len;
415      }
416
417    /* calculate section bytes size */
418    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE * num_directory_entries + EET_FILE2_HEADER_SIZE;
419    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE * num_dictionary_entries;
420
421    /* calculate per entry offset */
422    strings_offset = bytes_directory_entries + bytes_dictionary_entries;
423    data_offset = bytes_directory_entries + bytes_dictionary_entries + bytes_strings;
424
425    for (i = 0; i < num; ++i)
426      {
427         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
428           {
429              efn->offset = data_offset;
430              data_offset += efn->size;
431
432              efn->name_offset = strings_offset;
433              strings_offset += efn->name_size;
434           }
435      }
436
437    /* calculate dictionary strings offset */
438    if (ef->ed)
439      ef->ed->offset = strings_offset;
440
441    /* go thru and write the header */
442    head[0] = (int) htonl ((unsigned int) EET_MAGIC_FILE2);
443    head[1] = (int) htonl ((unsigned int) num_directory_entries);
444    head[2] = (int) htonl ((unsigned int) num_dictionary_entries);
445
446    fseek(ef->fp, 0, SEEK_SET);
447    if (fwrite(head, sizeof (head), 1, ef->fp) != 1)
448      goto write_error;
449
450    /* write directories entry */
451    j = 0;
452    for (i = 0; i < num; i++)
453      {
454         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
455           {
456              int        ibuf[EET_FILE2_DIRECTORY_ENTRY_COUNT];
457
458              ibuf[0] = (int) htonl ((unsigned int) efn->offset);
459              ibuf[1] = (int) htonl ((unsigned int) efn->size);
460              ibuf[2] = (int) htonl ((unsigned int) efn->data_size);
461              ibuf[3] = (int) htonl ((unsigned int) efn->name_offset);
462              ibuf[4] = (int) htonl ((unsigned int) efn->name_size);
463              ibuf[5] = (int) htonl ((unsigned int) efn->compression);
464
465              if (fwrite(ibuf, sizeof(ibuf), 1, ef->fp) != 1)
466                goto write_error;
467           }
468      }
469
470    /* write dictionnary */
471    if (ef->ed)
472      {
473         int     offset = strings_offset;
474
475         for (j = 0; j < ef->ed->count; ++j)
476           {
477              int      sbuf[EET_FILE2_DICTIONARY_ENTRY_COUNT];
478
479              sbuf[0] = (int) htonl ((unsigned int) ef->ed->all[j].hash);
480              sbuf[1] = (int) htonl ((unsigned int) offset);
481              sbuf[2] = (int) htonl ((unsigned int) ef->ed->all[j].len);
482              sbuf[3] = (int) htonl ((unsigned int) ef->ed->all[j].prev);
483              sbuf[4] = (int) htonl ((unsigned int) ef->ed->all[j].next);
484
485              offset += ef->ed->all[j].len;
486
487              if (fwrite(sbuf, sizeof (sbuf), 1, ef->fp) != 1)
488                goto write_error;
489           }
490      }
491
492    /* write directories name */
493    for (i = 0; i < num; i++)
494      {
495         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
496           {
497              if (fwrite(efn->name, efn->name_size, 1, ef->fp) != 1)
498                goto write_error;
499           }
500      }
501
502    /* write strings */
503    if (ef->ed)
504      {
505         for (j = 0; j < ef->ed->count; ++j)
506           {
507              if (ef->ed->all[j].str)
508                {
509                   if (fwrite(ef->ed->all[j].str, ef->ed->all[j].len, 1, ef->fp) != 1)
510                     goto write_error;
511                }
512              else
513                {
514                   if (fwrite(ef->ed->all[j].mmap, ef->ed->all[j].len, 1, ef->fp) != 1)
515                     goto write_error;
516                }
517           }
518      }
519
520    /* write data */
521    for (i = 0; i < num; i++)
522      {
523         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
524           {
525              if (fwrite(efn->data, efn->size, 1, ef->fp) != 1)
526                goto write_error;
527           }
528      }
529
530    /* flush all write to the file. */
531    fflush(ef->fp);
532
533    /* append signature if required */
534    if (ef->key)
535      {
536         error = eet_identity_sign(ef->fp, ef->key);
537         if (error != EET_ERROR_NONE)
538           goto sign_error;
539      }
540
541    /* no more writes pending */
542    ef->writes_pending = 0;
543
544    return EET_ERROR_NONE;
545
546    write_error:
547    switch (ferror(ef->fp))
548      {
549       case EFBIG: error = EET_ERROR_WRITE_ERROR_FILE_TOO_BIG; break;
550       case EIO: error = EET_ERROR_WRITE_ERROR_IO_ERROR; break;
551       case ENOSPC: error = EET_ERROR_WRITE_ERROR_OUT_OF_SPACE; break;
552       case EPIPE: error = EET_ERROR_WRITE_ERROR_FILE_CLOSED; break;
553       default: error = EET_ERROR_WRITE_ERROR; break;
554      }
555    sign_error:
556    if (ef->fp) fclose(ef->fp);
557    ef->fp = NULL;
558    return error;
559 }
560
561 #if 0 /* Unused */
562 /* flush out writes to an eet file */
563 static Eet_Error
564 eet_flush(Eet_File *ef)
565 {
566    Eet_File_Node        *efn;
567    int                  head[3];
568    int                  count = 0;
569    int                  size = 0;
570    int                  offset = 0;
571    int                  i;
572    int                  num;
573
574    /* check to see its' an eet file pointer */
575    if (eet_check_pointer(ef))
576      return EET_ERROR_BAD_OBJECT;
577    if (eet_check_header(ef))
578      return EET_ERROR_EMPTY;
579    if ((ef->mode != EET_FILE_MODE_WRITE) && (ef->mode != EET_FILE_MODE_READ_WRITE))
580      return EET_ERROR_NOT_WRITABLE;
581    if (!ef->writes_pending)
582      return EET_ERROR_NONE;
583
584    /* calculate total size in bytes of directory block */
585    num = (1 << ef->header->directory->size);
586    for (i = 0; i < num; i++)
587      {
588         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
589           {
590              size += 20 + strlen(efn->name) + 1;
591              count++;
592           }
593      }
594
595    /* calculate offsets per entry */
596    offset = 0;
597    for (i = 0; i < num; i++)
598      {
599         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
600           {
601              efn->offset = 12 + size + offset;
602              offset += efn->size;
603           }
604      }
605
606    /* go thru and write the header */
607    head[0] = (int) htonl ((unsigned int) EET_MAGIC_FILE);
608    head[1] = (int) htonl ((unsigned int) count);
609    head[2] = (int) htonl ((unsigned int) size);
610
611    fseek(ef->fp, 0, SEEK_SET);
612    if (fwrite(head, 12, 1, ef->fp) != 1)
613      goto write_error;
614
615    for (i = 0; i < num; i++)
616      {
617         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
618           {
619              unsigned int       ibuf[5];
620              int                name_size;
621
622              name_size = strlen(efn->name) + 1;
623
624              ibuf[0] = (int) htonl ((unsigned int) efn->offset);
625              ibuf[1] = (int) htonl ((unsigned int) efn->compression);
626              ibuf[2] = (int) htonl ((unsigned int) efn->size);
627              ibuf[3] = (int) htonl ((unsigned int) efn->data_size);
628              ibuf[4] = (int) htonl ((unsigned int) name_size);
629
630
631              if (fwrite(ibuf, sizeof(ibuf), 1, ef->fp) != 1)
632                goto write_error;
633              if (fwrite(efn->name, name_size, 1, ef->fp) != 1)
634                goto write_error;
635           }
636      }
637
638    /* write data */
639    for (i = 0; i < num; i++)
640      {
641         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
642           {
643              if (fwrite(efn->data, efn->size, 1, ef->fp) != 1)
644                goto write_error;
645           }
646      }
647
648    /* no more writes pending */
649    ef->writes_pending = 0;
650
651    return EET_ERROR_NONE;
652
653    write_error:
654    switch (ferror(ef->fp))
655      {
656       case EFBIG:
657         fclose(ef->fp);
658         ef->fp = NULL;
659         return EET_ERROR_WRITE_ERROR_FILE_TOO_BIG;
660       case EIO:
661         fclose(ef->fp);
662         ef->fp = NULL;
663         return EET_ERROR_WRITE_ERROR_IO_ERROR;
664       case ENOSPC:
665         fclose(ef->fp);
666         ef->fp = NULL;
667         return EET_ERROR_WRITE_ERROR_OUT_OF_SPACE;
668       case EPIPE:
669         fclose(ef->fp);
670         ef->fp = NULL;
671         return EET_ERROR_WRITE_ERROR_FILE_CLOSED;
672       default:
673         fclose(ef->fp);
674         ef->fp = NULL;
675         return EET_ERROR_WRITE_ERROR;
676      }
677    sign_error:
678    fclose(ef->fp);
679    ef->fp = NULL;
680    return EET_ERROR_WRITE_ERROR;
681 }
682 #endif
683
684 EAPI int
685 eet_init(void)
686 {
687 #ifdef HAVE_OPENSSL
688    /* Just load the crypto library error strings,
689     * SSL_load_error_strings() loads the crypto AND the SSL ones */
690    /* SSL_load_error_strings();*/
691    static int call_once = 0;
692
693    if (call_once == 0)
694      {
695         call_once = 1;
696         ERR_load_crypto_strings();
697      }
698
699 #endif
700    return ++eet_initcount;
701 }
702
703 EAPI int
704 eet_shutdown(void)
705 {
706    if (--eet_initcount == 0)
707      {
708         eet_clearcache();
709      }
710
711    return eet_initcount;
712 }
713
714 EAPI void
715 eet_clearcache(void)
716 {
717    int  num = 0;
718    int  i;
719
720    /*
721     * We need to compute the list of eet file to close separately from the cache,
722     * due to eet_close removing them from the cache after each call.
723     */
724    for (i = 0; i < eet_writers_num; i++)
725      {
726         if (eet_writers[i]->references <= 0) num++;
727      }
728
729    for (i = 0; i < eet_readers_num; i++)
730      {
731         if (eet_readers[i]->references <= 0) num++;
732      }
733
734    if (num > 0)
735      {
736         Eet_File **closelist = NULL;
737
738         closelist = alloca(num * sizeof(Eet_File *));
739         num = 0;
740         for (i = 0; i < eet_writers_num; i++)
741           {
742              if (eet_writers[i]->references <= 0)
743                {
744                   closelist[num] = eet_writers[i];
745                   eet_writers[i]->delete_me_now = 1;
746                   num++;
747                }
748           }
749
750         for (i = 0; i < eet_readers_num; i++)
751           {
752              if (eet_readers[i]->references <= 0)
753                {
754                   closelist[num] = eet_readers[i];
755                   eet_readers[i]->delete_me_now = 1;
756                   num++;
757                }
758           }
759
760         for (i = 0; i < num; i++)
761           {
762              eet_close(closelist[i]);
763           }
764      }
765 }
766
767 /* FIXME: MMAP race condition in READ_WRITE_MODE */
768 static Eet_File *
769 eet_internal_read2(Eet_File *ef)
770 {
771    const int    *data = (const int*) ef->data;
772    const char   *start = (const char*) ef->data;
773    int           index = 0;
774    int           num_directory_entries;
775    int           bytes_directory_entries;
776    int           num_dictionary_entries;
777    int           bytes_dictionary_entries;
778    int           signature_base_offset;
779    int           i;
780
781    index += sizeof(int);
782    if (eet_test_close((int) ntohl(*data) != EET_MAGIC_FILE2, ef))
783      return NULL;
784    data++;
785
786 #define GET_INT(Value, Pointer, Index)          \
787    {                                            \
788       Value = ntohl(*Pointer);                  \
789       Pointer++;                                \
790       Index += sizeof(int);                     \
791    }
792
793    /* get entries count and byte count */
794    GET_INT(num_directory_entries, data, index);
795    /* get dictionary count and byte count */
796    GET_INT(num_dictionary_entries, data, index);
797
798    bytes_directory_entries = EET_FILE2_DIRECTORY_ENTRY_SIZE * num_directory_entries + EET_FILE2_HEADER_SIZE;
799    bytes_dictionary_entries = EET_FILE2_DICTIONARY_ENTRY_SIZE * num_dictionary_entries;
800
801    /* we cant have <= 0 values here - invalid */
802    if (eet_test_close((num_directory_entries <= 0), ef))
803      return NULL;
804
805    /* we cant have more bytes directory and bytes in dictionaries than the size of the file */
806    if (eet_test_close((bytes_directory_entries + bytes_dictionary_entries) > ef->data_size, ef))
807      return NULL;
808
809    /* allocate header */
810    ef->header = calloc(1, sizeof(Eet_File_Header));
811    if (eet_test_close(!ef->header, ef))
812      return NULL;
813
814    ef->header->magic = EET_MAGIC_FILE_HEADER;
815
816    /* allocate directory block in ram */
817    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
818    if (eet_test_close(!ef->header->directory, ef))
819      return NULL;
820
821    /* 8 bit hash table (256 buckets) */
822    ef->header->directory->size = 8;
823    /* allocate base hash table */
824    ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
825    if (eet_test_close(!ef->header->directory->nodes, ef))
826      return NULL;
827
828    signature_base_offset = 0;
829
830    /* actually read the directory block - all of it, into ram */
831    for (i = 0; i < num_directory_entries; ++i)
832      {
833         const char      *name;
834         Eet_File_Node   *efn;
835         int              name_offset;
836         int              name_size;
837         int              hash;
838
839         /* out directory block is inconsistent - we have oveerun our */
840         /* dynamic block buffer before we finished scanning dir entries */
841         efn = malloc (sizeof(Eet_File_Node));
842         if (eet_test_close(!efn, ef))
843           return NULL;
844
845         /* get entrie header */
846         GET_INT(efn->offset, data, index);
847         GET_INT(efn->size, data, index);
848         GET_INT(efn->data_size, data, index);
849         GET_INT(name_offset, data, index);
850         GET_INT(name_size, data, index);
851         GET_INT(efn->compression, data, index);
852
853 #define EFN_TEST(Test, Ef, Efn)                 \
854         if (eet_test_close(Test, Ef))           \
855           {                                     \
856              free(Efn);                         \
857              return NULL;                       \
858           }
859
860         /* check data pointer position */
861         EFN_TEST(!((efn->size > 0)
862                    && (efn->offset + efn->size <= ef->data_size)
863                    && (efn->offset > bytes_dictionary_entries + bytes_directory_entries)), ef, efn);
864
865         /* check name position */
866         EFN_TEST(!((name_size > 0)
867                    && (name_offset + name_size < ef->data_size)
868                    && (name_offset >= bytes_dictionary_entries + bytes_directory_entries)), ef, efn);
869
870         name = start + name_offset;
871
872         /* check '\0' at the end of name string */
873         EFN_TEST(name[name_size - 1] != '\0', ef, efn);
874
875         efn->free_name = 0;
876         efn->name = (char*) name;
877         efn->name_size = name_size;
878
879         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
880         efn->next = ef->header->directory->nodes[hash];
881         ef->header->directory->nodes[hash] = efn;
882
883         /* read-only mode, so currently we have no data loaded */
884         if (ef->mode == EET_FILE_MODE_READ)
885           efn->data = NULL;
886         /* read-write mode - read everything into ram */
887         else
888           {
889              efn->data = malloc(efn->size);
890              if (efn->data)
891                memcpy(efn->data, ef->data + efn->offset, efn->size);
892           }
893
894         /* compute the possible position of a signature */
895         if (signature_base_offset < efn->offset + efn->size)
896           signature_base_offset = efn->offset + efn->size;
897      }
898
899    ef->ed = NULL;
900
901    if (num_dictionary_entries)
902      {
903         const int       *dico = (const int*) ef->data + EET_FILE2_DIRECTORY_ENTRY_COUNT * num_directory_entries + EET_FILE2_HEADER_COUNT;
904         int              j;
905
906         if (eet_test_close((num_dictionary_entries * EET_FILE2_DICTIONARY_ENTRY_SIZE + index) > (bytes_dictionary_entries + bytes_directory_entries), ef))
907             return NULL;
908
909         ef->ed = calloc(1, sizeof (Eet_Dictionary));
910         if (eet_test_close(!ef->ed, ef)) return NULL;
911
912         ef->ed->all = calloc(num_dictionary_entries, sizeof (Eet_String));
913         if (eet_test_close(!ef->ed->all, ef)) return NULL;
914
915         ef->ed->count = num_dictionary_entries;
916         ef->ed->total = num_dictionary_entries;
917         ef->ed->start = start + bytes_dictionary_entries + bytes_directory_entries;
918         ef->ed->end = ef->ed->start;
919
920         for (j = 0; j < ef->ed->count; ++j)
921           {
922              int   hash;
923              int   offset;
924
925              GET_INT(hash, dico, index);
926              GET_INT(offset, dico, index);
927              GET_INT(ef->ed->all[j].len, dico, index);
928              GET_INT(ef->ed->all[j].prev, dico, index);
929              GET_INT(ef->ed->all[j].next, dico, index);
930
931              /* Hash value could be stored on 8bits data, but this will break alignment of all the others data.
932                 So stick to int and check the value. */
933              if (eet_test_close(hash & 0xFFFFFF00, ef)) return NULL;
934
935              /* Check string position */
936              if (eet_test_close(!((ef->ed->all[j].len > 0)
937                                   && (offset > (bytes_dictionary_entries + bytes_directory_entries))
938                                   && (offset + ef->ed->all[j].len < ef->data_size)), ef))
939                return NULL;
940
941              ef->ed->all[j].mmap = start + offset;
942              ef->ed->all[j].str = NULL;
943
944              if (ef->ed->all[j].mmap + ef->ed->all[j].len > ef->ed->end)
945                ef->ed->end = ef->ed->all[j].mmap + ef->ed->all[j].len;
946
947              /* Check '\0' at the end of the string */
948              if (eet_test_close(ef->ed->all[j].mmap[ef->ed->all[j].len - 1] != '\0', ef)) return NULL;
949
950              ef->ed->all[j].hash = hash;
951              if (ef->ed->all[j].prev == -1)
952                ef->ed->hash[hash] = j;
953
954              /* compute the possible position of a signature */
955              if (signature_base_offset < offset + ef->ed->all[j].len)
956                signature_base_offset = offset + ef->ed->all[j].len;
957           }
958      }
959
960    /* Check if the file is signed */
961    ef->x509_der = NULL;
962    ef->x509_length = 0;
963    if (signature_base_offset < ef->data_size)
964      {
965 #ifdef HAVE_SIGNATURE
966         const unsigned char *buffer = ((const unsigned char*) ef->data) + signature_base_offset;
967         ef->x509_der = eet_identity_check(ef->data, signature_base_offset,
968                                           buffer, ef->data_size - signature_base_offset,
969                                           &ef->x509_length);
970
971         if (ef->x509_der == NULL)
972           {
973              ef->delete_me_now = 1;
974              eet_close(ef);
975              return NULL;
976           }
977 #else
978         fprintf(stderr, "This file could be signed but you didn't compile the necessary code to check the signature.\n");
979 #endif
980      }
981
982    return ef;
983 }
984
985 #if EET_OLD_EET_FILE_FORMAT
986 static Eet_File *
987 eet_internal_read1(Eet_File *ef)
988 {
989    const unsigned char  *dyn_buf = NULL;
990    const unsigned char  *p = NULL;
991    int                   index = 0;
992    int                   num_entries;
993    int                   byte_entries;
994    int                   i;
995
996    fprintf(stderr, "EET file format of '%s' is deprecated. You should just open it one time with mode == EET_FILE_MODE_READ_WRITE to solve this issue.\n", ef->path);
997
998    /* build header table if read mode */
999    /* geat header */
1000    index += sizeof(int);
1001    if (eet_test_close((int)ntohl(*((int *)ef->data)) != EET_MAGIC_FILE, ef))
1002      return NULL;
1003
1004 #define EXTRACT_INT(Value, Pointer, Index) \
1005         { \
1006            int tmp; \
1007            memcpy(&tmp, Pointer + Index, sizeof(int)); \
1008            Value = ntohl(tmp); \
1009            Index += sizeof(int); \
1010         }
1011
1012    /* get entries count and byte count */
1013    EXTRACT_INT(num_entries, ef->data, index);
1014    EXTRACT_INT(byte_entries, ef->data, index);
1015
1016    /* we cant have <= 0 values here - invalid */
1017    if (eet_test_close((num_entries <= 0) || (byte_entries <= 0), ef))
1018      return NULL;
1019
1020    /* we can't have more entires than minimum bytes for those! invalid! */
1021    if (eet_test_close((num_entries * 20) > byte_entries, ef))
1022      return NULL;
1023
1024    /* check we will not outrun the file limit */
1025    if (eet_test_close(((byte_entries + sizeof(int) * 3) > ef->data_size), ef))
1026      return NULL;
1027
1028    /* allocate header */
1029    ef->header = calloc(1, sizeof(Eet_File_Header));
1030    if (eet_test_close(!ef->header, ef))
1031      return NULL;
1032
1033    ef->header->magic = EET_MAGIC_FILE_HEADER;
1034
1035    /* allocate directory block in ram */
1036    ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
1037    if (eet_test_close(!ef->header->directory, ef))
1038      return NULL;
1039
1040    /* 8 bit hash table (256 buckets) */
1041    ef->header->directory->size = 8;
1042    /* allocate base hash table */
1043    ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
1044    if (eet_test_close(!ef->header->directory->nodes, ef))
1045      return NULL;
1046
1047    /* actually read the directory block - all of it, into ram */
1048    dyn_buf = ef->data + index;
1049
1050    /* parse directory block */
1051    p = dyn_buf;
1052
1053    for (i = 0; i < num_entries; i++)
1054      {
1055         Eet_File_Node   *efn;
1056         void            *data = NULL;
1057         int             indexn = 0;
1058         int             name_size;
1059         int             hash;
1060         int             k;
1061
1062 #define HEADER_SIZE (sizeof(int) * 5)
1063
1064         /* out directory block is inconsistent - we have oveerun our */
1065         /* dynamic block buffer before we finished scanning dir entries */
1066         if (eet_test_close(p + HEADER_SIZE >= (dyn_buf + byte_entries), ef))
1067           return NULL;
1068
1069         /* allocate all the ram needed for this stored node accounting */
1070         efn = malloc (sizeof(Eet_File_Node));
1071         if (eet_test_close(!efn, ef))
1072           return NULL;
1073
1074         /* get entrie header */
1075         EXTRACT_INT(efn->offset, p, indexn);
1076         EXTRACT_INT(efn->compression, p, indexn);
1077         EXTRACT_INT(efn->size, p, indexn);
1078         EXTRACT_INT(efn->data_size, p, indexn);
1079         EXTRACT_INT(name_size, p, indexn);
1080
1081         efn->name_size = name_size;
1082
1083         /* invalid size */
1084         if (eet_test_close(efn->size <= 0, ef))
1085           {
1086              free (efn);
1087              return NULL;
1088           }
1089
1090         /* invalid name_size */
1091         if (eet_test_close(name_size <= 0, ef))
1092           {
1093              free (efn);
1094              return NULL;
1095           }
1096
1097         /* reading name would mean falling off end of dyn_buf - invalid */
1098         if (eet_test_close((p + 16 + name_size) > (dyn_buf + byte_entries), ef))
1099           {
1100              free (efn);
1101              return NULL;
1102           }
1103
1104         /* This code is useless if we dont want backward compatibility */
1105         for (k = name_size; k > 0 && ((unsigned char) * (p + HEADER_SIZE + k)) != 0; --k)
1106           ;
1107
1108         efn->free_name = ((unsigned char) * (p + HEADER_SIZE + k)) != 0;
1109
1110         if (efn->free_name)
1111           {
1112              efn->name = malloc(sizeof(char) * name_size + 1);
1113              if (eet_test_close(efn->name == NULL, ef))
1114                {
1115                   free (efn);
1116                   return NULL;
1117                }
1118
1119              strncpy(efn->name, (char *)p + HEADER_SIZE, name_size);
1120              efn->name[name_size] = 0;
1121
1122              printf("File: %s is not up to date for key \"%s\" - needs rebuilding sometime\n", ef->path, efn->name);
1123           }
1124         else
1125           /* The only really usefull peace of code for efn->name (no backward compatibility) */
1126           efn->name = (char*)((unsigned char*)(p + HEADER_SIZE));
1127
1128         /* get hash bucket it should go in */
1129         hash = _eet_hash_gen(efn->name, ef->header->directory->size);
1130         efn->next = ef->header->directory->nodes[hash];
1131         ef->header->directory->nodes[hash] = efn;
1132
1133         /* read-only mode, so currently we have no data loaded */
1134         if (ef->mode == EET_FILE_MODE_READ)
1135           efn->data = NULL;
1136         /* read-write mode - read everything into ram */
1137         else
1138           {
1139              data = malloc(efn->size);
1140              if (data)
1141                memcpy(data, ef->data + efn->offset, efn->size);
1142              efn->data = data;
1143           }
1144         /* advance */
1145         p += HEADER_SIZE + name_size;
1146      }
1147    return ef;
1148 }
1149 #endif
1150
1151 static Eet_File *
1152 eet_internal_read(Eet_File *ef)
1153 {
1154    const int    *data = (const int*) ef->data;
1155
1156    if (eet_test_close((ef->data == (void *)-1) || (ef->data == NULL), ef))
1157      return NULL;
1158
1159    if (eet_test_close(ef->data_size < sizeof(int) * 3, ef))
1160      return NULL;
1161
1162    switch (ntohl(*data))
1163      {
1164 #if EET_OLD_EET_FILE_FORMAT
1165       case EET_MAGIC_FILE:
1166         return eet_internal_read1(ef);
1167 #endif
1168       case EET_MAGIC_FILE2:
1169         return eet_internal_read2(ef);
1170       default:
1171         ef->delete_me_now = 1;
1172         eet_close(ef);
1173         break;
1174      }
1175
1176    return NULL;
1177 }
1178
1179 EAPI Eet_File *
1180 eet_memopen_read(const void *data, size_t size)
1181 {
1182    Eet_File     *ef;
1183
1184    if (data == NULL || size == 0)
1185      return NULL;
1186
1187    ef = malloc (sizeof (Eet_File));
1188    if (!ef)
1189      return NULL;
1190
1191    ef->ed = NULL;
1192    ef->path = NULL;
1193    ef->key = NULL;
1194    ef->magic = EET_MAGIC_FILE;
1195    ef->references = 1;
1196    ef->mode = EET_FILE_MODE_READ;
1197    ef->header = NULL;
1198    ef->mtime = 0;
1199    ef->delete_me_now = 1;
1200    ef->fp = NULL;
1201    ef->readfp = NULL;
1202    ef->data = data;
1203    ef->data_size = size;
1204
1205    return eet_internal_read(ef);
1206 }
1207
1208 EAPI Eet_File *
1209 eet_open(const char *file, Eet_File_Mode mode)
1210 {
1211    FILE         *fp;
1212    Eet_File     *ef;
1213    int           file_len;
1214    struct stat   file_stat;
1215
1216    if (!file)
1217      return NULL;
1218
1219    /* find the current file handle in cache*/
1220    ef = NULL;
1221    if (mode == EET_FILE_MODE_READ)
1222      {
1223         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1224         if (ef)
1225           {
1226              eet_flush2(ef);
1227              ef->references++;
1228              ef->delete_me_now = 1;
1229              eet_close(ef);
1230           }
1231         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1232      }
1233    else if ((mode == EET_FILE_MODE_WRITE) ||
1234             (mode == EET_FILE_MODE_READ_WRITE))
1235      {
1236         ef = eet_cache_find((char *)file, eet_readers, eet_readers_num);
1237         if (ef)
1238           {
1239              ef->delete_me_now = 1;
1240              ef->references++;
1241              eet_close(ef);
1242           }
1243         ef = eet_cache_find((char *)file, eet_writers, eet_writers_num);
1244      }
1245
1246     /* try open the file based on mode */
1247    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1248      {
1249         fp = fopen(file, "rb");
1250         if (!fp) goto on_error;
1251         if (fstat(fileno(fp), &file_stat))
1252           {
1253              fclose(fp);
1254              fp = NULL;
1255              goto on_error;
1256           }
1257         if ((mode == EET_FILE_MODE_READ) &&
1258             (file_stat.st_size < (sizeof(int) * 3)))
1259           {
1260              fclose(fp);
1261              fp = NULL;
1262              goto on_error;
1263           }
1264
1265      on_error:
1266         if (fp == NULL && mode == EET_FILE_MODE_READ) return NULL;
1267      }
1268    else
1269      {
1270         if (mode != EET_FILE_MODE_WRITE) return NULL;
1271         memset(&file_stat, 0, sizeof(file_stat));
1272         /* opening for write - delete old copy of file right away */
1273         unlink(file);
1274         fp = fopen(file, "wb");
1275      }
1276
1277    /* We found one */
1278    if (ef && (file_stat.st_mtime != ef->mtime))
1279      {
1280         ef->delete_me_now = 1;
1281         ef->references++;
1282         eet_close(ef);
1283         ef = NULL;
1284      }
1285
1286    if (ef)
1287      {
1288         /* reference it up and return it */
1289         if (fp != NULL) fclose(fp);
1290         ef->references++;
1291         return ef;
1292      }
1293
1294    file_len = strlen(file) + 1;
1295
1296    /* Allocate struct for eet file and have it zero'd out */
1297    ef = malloc(sizeof(Eet_File) + file_len);
1298    if (!ef)
1299      return NULL;
1300
1301    /* fill some of the members */
1302    ef->fp = fp;
1303    ef->key = NULL;
1304    ef->readfp = NULL;
1305    ef->path = ((char *)ef) + sizeof(Eet_File);
1306    memcpy(ef->path, file, file_len);
1307    ef->magic = EET_MAGIC_FILE;
1308    ef->references = 1;
1309    ef->mode = mode;
1310    ef->header = NULL;
1311    ef->mtime = file_stat.st_mtime;
1312    ef->writes_pending = 0;
1313    ef->delete_me_now = 0;
1314    ef->data = NULL;
1315    ef->data_size = 0;
1316
1317    ef->ed = (mode == EET_FILE_MODE_WRITE)
1318      || (ef->fp == NULL && mode == EET_FILE_MODE_READ_WRITE) ?
1319      eet_dictionary_add() : NULL;
1320
1321    if (ef->fp == NULL && mode == EET_FILE_MODE_READ_WRITE) goto empty_file;
1322
1323    /* if we can't open - bail out */
1324    if (eet_test_close(!ef->fp, ef))
1325      return NULL;
1326
1327    fcntl(fileno(ef->fp), F_SETFD, FD_CLOEXEC);
1328    /* if we opened for read or read-write */
1329    if ((mode == EET_FILE_MODE_READ) || (mode == EET_FILE_MODE_READ_WRITE))
1330      {
1331         ef->data_size = file_stat.st_size;
1332         ef->data = mmap(NULL, ef->data_size, PROT_READ,
1333                         MAP_SHARED, fileno(ef->fp), 0);
1334         if (eet_test_close((ef->data == MAP_FAILED), ef))
1335           return NULL;
1336         ef = eet_internal_read(ef);
1337         if (!ef)
1338           return NULL;
1339      }
1340
1341  empty_file:
1342    /* we need to delete the original file in read-write mode and re-open for writing */
1343    if (ef->mode == EET_FILE_MODE_READ_WRITE)
1344      {
1345         ef->readfp = ef->fp;
1346         ef->fp = NULL;
1347      }
1348
1349    /* add to cache */
1350    if (ef->references == 1)
1351      {
1352         if (ef->mode == EET_FILE_MODE_READ)
1353           eet_cache_add(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1354         else
1355           if ((ef->mode == EET_FILE_MODE_WRITE) || (ef->mode == EET_FILE_MODE_READ_WRITE))
1356             eet_cache_add(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1357      }
1358
1359    return ef;
1360 }
1361
1362 EAPI Eet_File_Mode
1363 eet_mode_get(Eet_File *ef)
1364 {
1365    /* check to see its' an eet file pointer */
1366    if ((!ef) || (ef->magic != EET_MAGIC_FILE))
1367      return EET_FILE_MODE_INVALID;
1368    else
1369      return ef->mode;
1370 }
1371
1372 EAPI const void *
1373 eet_identity_x509(Eet_File *ef, int *der_length)
1374 {
1375    if (!ef->x509_der) return NULL;
1376
1377    if (der_length) *der_length = ef->x509_length;
1378    return ef->x509_der;
1379 }
1380
1381 EAPI Eet_Error
1382 eet_identity_set(Eet_File *ef, Eet_Key *key)
1383 {
1384    Eet_Key *tmp = ef->key;
1385
1386    if (!ef) return EET_ERROR_BAD_OBJECT;
1387
1388    ef->key = key;
1389    eet_identity_ref(ef->key);
1390    eet_identity_unref(tmp);
1391
1392    /* flags that writes are pending */
1393    ef->writes_pending = 1;
1394
1395    return EET_ERROR_NONE;
1396 }
1397
1398 EAPI Eet_Error
1399 eet_close(Eet_File *ef)
1400 {
1401    Eet_Error err;
1402
1403    /* check to see its' an eet file pointer */
1404    if (eet_check_pointer(ef))
1405      return EET_ERROR_BAD_OBJECT;
1406    /* deref */
1407    ef->references--;
1408    /* if its still referenced - dont go any further */
1409    if (ef->references > 0) return EET_ERROR_NONE;
1410    /* flush any writes */
1411    err = eet_flush2(ef);
1412
1413    eet_identity_unref(ef->key);
1414    ef->key = NULL;
1415
1416    /* if not urgent to delete it - dont free it - leave it in cache */
1417    if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
1418      return EET_ERROR_NONE;
1419
1420    /* remove from cache */
1421    if (ef->mode == EET_FILE_MODE_READ)
1422      eet_cache_del(ef, &eet_readers, &eet_readers_num, &eet_readers_alloc);
1423    else if ((ef->mode == EET_FILE_MODE_WRITE) || (ef->mode == EET_FILE_MODE_READ_WRITE))
1424      eet_cache_del(ef, &eet_writers, &eet_writers_num, &eet_writers_alloc);
1425
1426    /* free up data */
1427    if (ef->header)
1428      {
1429         if (ef->header->directory)
1430           {
1431              if (ef->header->directory->nodes)
1432                {
1433                   int i, num;
1434
1435                   num = (1 << ef->header->directory->size);
1436                   for (i = 0; i < num; i++)
1437                     {
1438                        Eet_File_Node *efn;
1439
1440                        while ((efn = ef->header->directory->nodes[i]))
1441                          {
1442                             if (efn->data)
1443                               free(efn->data);
1444
1445                             ef->header->directory->nodes[i] = efn->next;
1446
1447                             if (efn->free_name)
1448                               free(efn->name);
1449
1450                             free(efn);
1451                          }
1452                     }
1453                   free(ef->header->directory->nodes);
1454                }
1455              free(ef->header->directory);
1456           }
1457         free(ef->header);
1458      }
1459
1460    eet_dictionary_free(ef->ed);
1461
1462    if (ef->data) munmap((void*)ef->data, ef->data_size);
1463    if (ef->fp) fclose(ef->fp);
1464    if (ef->readfp) fclose(ef->readfp);
1465
1466    /* zero out ram for struct - caution tactic against stale memory use */
1467    memset(ef, 0, sizeof(Eet_File));
1468
1469    /* free it */
1470    free(ef);
1471    return err;
1472 }
1473
1474 EAPI void *
1475 eet_read(Eet_File *ef, const char *name, int *size_ret)
1476 {
1477    void                 *data = NULL;
1478    int                  size = 0;
1479    Eet_File_Node        *efn;
1480
1481    if (size_ret)
1482      *size_ret = 0;
1483
1484    /* check to see its' an eet file pointer */
1485    if (eet_check_pointer(ef))
1486      return NULL;
1487    if (!name)
1488      return NULL;
1489    if ((ef->mode != EET_FILE_MODE_READ) &&
1490        (ef->mode != EET_FILE_MODE_READ_WRITE))
1491      return NULL;
1492
1493    /* no header, return NULL */
1494    if (eet_check_header(ef))
1495      return NULL;
1496
1497    /* hunt hash bucket */
1498    efn = find_node_by_name(ef, name);
1499    if (!efn)
1500      return NULL;
1501
1502    /* get size (uncompressed, if compressed at all) */
1503    size = efn->data_size;
1504
1505    /* allocate data */
1506    data = malloc(size);
1507    if (!data)
1508      return NULL;
1509
1510    /* uncompressed data */
1511    if (efn->compression == 0)
1512      {
1513         /* if we alreayd have the data in ram... copy that */
1514         if (efn->data)
1515           memcpy(data, efn->data, efn->size);
1516         else
1517           if (!read_data_from_disk(ef, efn, data, size))
1518             {
1519                free(data);
1520                return NULL;
1521             }
1522      }
1523    /* compressed data */
1524    else
1525      {
1526         void    *tmp_data;
1527         int     free_tmp = 0;
1528         int     compr_size = efn->size;
1529         uLongf  dlen;
1530
1531         /* if we already have the data in ram... copy that */
1532         if (efn->data)
1533           tmp_data = efn->data;
1534         else
1535           {
1536              tmp_data = malloc(compr_size);
1537              if (!tmp_data)
1538                {
1539                   free(data);
1540                   return NULL;
1541                }
1542
1543              free_tmp = 1;
1544
1545              if (!read_data_from_disk(ef, efn, tmp_data, compr_size))
1546                {
1547                   free(tmp_data);
1548                   free(data);
1549                   return NULL;
1550                }
1551           }
1552
1553         /* decompress it */
1554         dlen = size;
1555         if (uncompress((Bytef *)data, &dlen,
1556                  tmp_data, (uLongf)compr_size))
1557           {
1558              free(data);
1559              return NULL;
1560           }
1561
1562         if (free_tmp)
1563           free(tmp_data);
1564      }
1565
1566    /* fill in return values */
1567    if (size_ret)
1568      *size_ret = size;
1569
1570    return data;
1571 }
1572
1573 EAPI const void *
1574 eet_read_direct(Eet_File *ef, const char *name, int *size_ret)
1575 {
1576    const void   *data = NULL;
1577    int           size = 0;
1578    Eet_File_Node *efn;
1579
1580    if (size_ret)
1581      *size_ret = 0;
1582
1583    /* check to see its' an eet file pointer */
1584    if (eet_check_pointer(ef))
1585      return NULL;
1586    if (!name)
1587      return NULL;
1588    if ((ef->mode != EET_FILE_MODE_READ) &&
1589        (ef->mode != EET_FILE_MODE_READ_WRITE))
1590      return NULL;
1591
1592    /* no header, return NULL */
1593    if (eet_check_header(ef))
1594      return NULL;
1595
1596    /* hunt hash bucket */
1597    efn = find_node_by_name(ef, name);
1598    if (!efn)
1599      return NULL;
1600
1601    if (efn->offset < 0 && efn->data == NULL)
1602      return NULL;
1603
1604    /* get size (uncompressed, if compressed at all) */
1605    size = efn->data_size;
1606
1607    /* uncompressed data */
1608    if (efn->compression == 0)
1609      data = efn->data ? efn->data : ef->data + efn->offset;
1610    /* compressed data */
1611    else
1612      data = NULL;
1613
1614    /* fill in return values */
1615    if (size_ret)
1616      *size_ret = size;
1617
1618    return data;
1619 }
1620
1621 EAPI int
1622 eet_write(Eet_File *ef, const char *name, const void *data, int size, int compress)
1623 {
1624    Eet_File_Node        *efn;
1625    void                 *data2;
1626    int                  exists_already = 0;
1627    int                  data_size;
1628    int                  hash;
1629
1630    /* check to see its' an eet file pointer */
1631    if (eet_check_pointer(ef))
1632      return 0;
1633    if ((!name) || (!data) || (size <= 0))
1634      return 0;
1635    if ((ef->mode != EET_FILE_MODE_WRITE) &&
1636        (ef->mode != EET_FILE_MODE_READ_WRITE))
1637      return 0;
1638
1639    if (!ef->header)
1640      {
1641         /* allocate header */
1642         ef->header = calloc(1, sizeof(Eet_File_Header));
1643         if (!ef->header)
1644           return 0;
1645
1646         ef->header->magic = EET_MAGIC_FILE_HEADER;
1647         /* allocate directory block in ram */
1648         ef->header->directory = calloc(1, sizeof(Eet_File_Directory));
1649         if (!ef->header->directory)
1650           return 0;
1651
1652         /* 8 bit hash table (256 buckets) */
1653         ef->header->directory->size = 8;
1654         /* allocate base hash table */
1655         ef->header->directory->nodes = calloc(1, sizeof(Eet_File_Node *) * (1 << ef->header->directory->size));
1656         if (!ef->header->directory->nodes)
1657           return 0;
1658      }
1659
1660    /* figure hash bucket */
1661    hash = _eet_hash_gen(name, ef->header->directory->size);
1662
1663    data_size = compress ? 12 + ((size * 101) / 100) : size;
1664
1665    data2 = malloc(data_size);
1666    if (!data2)
1667      return 0;
1668
1669    /* if we want to compress */
1670    if (compress)
1671      {
1672         uLongf buflen;
1673
1674         /* compress the data with max compression */
1675         buflen = (uLongf)data_size;
1676         if (compress2((Bytef *)data2, &buflen, (Bytef *)data,
1677                            (uLong)size, Z_BEST_COMPRESSION) != Z_OK)
1678           {
1679              free(data2);
1680              return 0;
1681           }
1682         /* record compressed chunk size */
1683         data_size = (int)buflen;
1684         if (data_size < 0 || data_size >= size)
1685           {
1686              compress = 0;
1687              data_size = size;
1688           }
1689         else
1690           {
1691              void *data3;
1692
1693              data3 = realloc(data2, data_size);
1694              if (data3)
1695                data2 = data3;
1696           }
1697      }
1698    if (!compress)
1699      memcpy(data2, data, size);
1700
1701    /* Does this node already exist? */
1702    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
1703      {
1704         /* if it matches */
1705         if ((efn->name) && (eet_string_match(efn->name, name)))
1706           {
1707              free(efn->data);
1708              efn->compression = !!compress;
1709              efn->size = data_size;
1710              efn->data_size = size;
1711              efn->data = data2;
1712              efn->offset = -1;
1713              exists_already = 1;
1714              break;
1715           }
1716      }
1717    if (!exists_already)
1718      {
1719         efn = malloc(sizeof(Eet_File_Node));
1720         if (!efn)
1721           {
1722              free(data2);
1723              return 0;
1724           }
1725         efn->name = strdup(name);
1726         efn->name_size = strlen(efn->name) + 1;
1727         efn->free_name = 1;
1728
1729         efn->next = ef->header->directory->nodes[hash];
1730         ef->header->directory->nodes[hash] = efn;
1731         efn->offset = -1;
1732         efn->compression = !!compress;
1733         efn->size = data_size;
1734         efn->data_size = size;
1735         efn->data = data2;
1736      }
1737
1738    /* flags that writes are pending */
1739    ef->writes_pending = 1;
1740    return data_size;
1741 }
1742
1743 EAPI int
1744 eet_delete(Eet_File *ef, const char *name)
1745 {
1746    Eet_File_Node        *efn;
1747    Eet_File_Node        *pefn;
1748    int                  hash;
1749    int                  exists_already = 0;
1750
1751    /* check to see its' an eet file pointer */
1752    if (eet_check_pointer(ef))
1753      return 0;
1754    if (!name)
1755      return 0;
1756
1757    /* deleting keys is only possible in RW or WRITE mode */
1758    if (ef->mode == EET_FILE_MODE_READ)
1759      return 0;
1760
1761    if (eet_check_header(ef))
1762      return 0;
1763
1764    /* figure hash bucket */
1765    hash = _eet_hash_gen(name, ef->header->directory->size);
1766
1767    /* Does this node already exist? */
1768    for (pefn = NULL, efn = ef->header->directory->nodes[hash];
1769         efn;
1770         pefn = efn, efn = efn->next)
1771      {
1772         /* if it matches */
1773         if (eet_string_match(efn->name, name))
1774           {
1775              if (efn->data)
1776                free(efn->data);
1777
1778              if (efn == ef->header->directory->nodes[hash])
1779                ef->header->directory->nodes[hash] = efn->next;
1780              else
1781                pefn->next = efn->next;
1782
1783              if (efn->free_name) free(efn->name);
1784              free(efn);
1785              exists_already = 1;
1786              break;
1787           }
1788      }
1789    /* flags that writes are pending */
1790    if (exists_already)
1791      ef->writes_pending = 1;
1792
1793    /* update access time */
1794    return exists_already;
1795 }
1796
1797 EAPI Eet_Dictionary *
1798 eet_dictionary_get(Eet_File *ef)
1799 {
1800    if (eet_check_pointer(ef)) return NULL;
1801
1802    return ef->ed;
1803 }
1804
1805
1806 EAPI char **
1807 eet_list(Eet_File *ef, const char *glob, int *count_ret)
1808 {
1809    Eet_File_Node        *efn;
1810    char                 **list_ret = NULL;
1811    int                  list_count = 0;
1812    int                  list_count_alloc = 0;
1813    int                  i, num;
1814
1815    /* check to see its' an eet file pointer */
1816    if (eet_check_pointer(ef) || eet_check_header(ef) ||
1817        (!glob) ||
1818        ((ef->mode != EET_FILE_MODE_READ) &&
1819         (ef->mode != EET_FILE_MODE_READ_WRITE)))
1820      {
1821         if (count_ret)
1822           *count_ret = 0;
1823
1824         return NULL;
1825      }
1826
1827    /* loop through all entries */
1828    num = (1 << ef->header->directory->size);
1829    for (i = 0; i < num; i++)
1830      {
1831         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
1832           {
1833              /* if the entry matches the input glob
1834               * check for * explicitly, because on some systems, * isn't well
1835               * supported
1836               */
1837              if ((!strcmp (glob, "*")) || !fnmatch(glob, efn->name, 0))
1838                {
1839                   /* add it to our list */
1840                   list_count++;
1841
1842                   /* only realloc in 32 entry chunks */
1843                   if (list_count > list_count_alloc)
1844                     {
1845                        char     **new_list = NULL;
1846
1847                        list_count_alloc += 64;
1848                        new_list = realloc(list_ret, list_count_alloc * (sizeof(char *)));
1849                        if (!new_list)
1850                          {
1851                             free(list_ret);
1852
1853                             if (count_ret)
1854                               *count_ret = 0;
1855
1856                             return NULL;
1857                          }
1858                        list_ret = new_list;
1859                     }
1860
1861                   /* put pointer of name string in */
1862                   list_ret[list_count - 1] = efn->name;
1863                }
1864           }
1865      }
1866
1867    /* return count and list */
1868    if (count_ret)
1869      *count_ret = list_count;
1870
1871    return list_ret;
1872 }
1873
1874 EAPI int
1875 eet_num_entries(Eet_File *ef)
1876 {
1877    int i, num, ret = 0;
1878    Eet_File_Node *efn;
1879
1880    /* check to see its' an eet file pointer */
1881    if (eet_check_pointer(ef) || eet_check_header(ef) ||
1882        ((ef->mode != EET_FILE_MODE_READ) &&
1883         (ef->mode != EET_FILE_MODE_READ_WRITE)))
1884      return -1;
1885
1886    /* loop through all entries */
1887    num = (1 << ef->header->directory->size);
1888    for (i = 0; i < num; i++)
1889      {
1890         for (efn = ef->header->directory->nodes[i]; efn; efn = efn->next)
1891           ret++;
1892      }
1893
1894    return ret;
1895 }
1896
1897 static Eet_File_Node *
1898 find_node_by_name(Eet_File *ef, const char *name)
1899 {
1900    Eet_File_Node *efn;
1901    int hash;
1902
1903    /* get hash bucket this should be in */
1904    hash = _eet_hash_gen(name, ef->header->directory->size);
1905
1906    for (efn = ef->header->directory->nodes[hash]; efn; efn = efn->next)
1907      {
1908         if (eet_string_match(efn->name, name))
1909           return efn;
1910      }
1911
1912    return NULL;
1913 }
1914
1915 static int
1916 read_data_from_disk(Eet_File *ef, Eet_File_Node *efn, void *buf, int len)
1917 {
1918    if (efn->offset < 0) return 0;
1919
1920    if (ef->data)
1921      {
1922         if ((efn->offset + len) > ef->data_size) return 0;
1923         memcpy(buf, ef->data + efn->offset, len);
1924      }
1925    else
1926      {
1927         /* seek to data location */
1928         if (fseek(ef->fp, efn->offset, SEEK_SET) < 0)
1929           return 0;
1930
1931         /* read it */
1932         len = fread(buf, len, 1, ef->fp);
1933      }
1934    return len;
1935 }