↑ 1 #include <stdio.h>
↑ 2 #include <sys/types.h>
↑ 3 #include <sys/stat.h>
↑ 4 #include <fcntl.h>
↑ 5 #include <sys/mman.h>
↑ 6 #include <unistd.h>
↑ 7 #include "buffer_readline.h"
↑ 8 #include "rh_string.h"
↑ 9 #include "mimetype.h"
↑ 10 #include "log.h"
↑ 11
↑ 12 #define MAP_WANT_ALNUM MAP_ALNUM
↑ 13 #include "rh_charmap.h"
↑ 14
↑ 15 static void mimetype_remove_internal (mimetype_entry_t *entry);
↑ 16
↑ 17 void mimetype_base_init (mimetype_base_t *base)
↑ 18 {
↑ 19 TYPE_ZERO(base);
↑ 20 }
↑ 21
↑ 22 void mimetype_base_destroy (mimetype_base_t *base)
↑ 23 {
↑ 24 size_t i;
↑ 25 size_t elements;
↑ 26
↑ 27 elements = base->elements;
↑ 28
↑ 29 DEBUGLOG ("base(%p): elements(%zu)", (void*)base, elements);
↑ 30
↑ 31 for (i=0; i<base->nbuckets && elements; ++i) {
↑ 32 struct mimetype_list *list;
↑ 33
↑ 34 list = &base->buckets[i];
↑ 35
↑ 36 while (!LIST_EMPTY(list)) {
↑ 37 mimetype_entry_t *entry;
↑ 38
↑ 39 entry = LIST_FIRST(list);
↑ 40
↑ 41 mimetype_remove_internal (entry);
↑ 42 --elements;
↑ 43 }
↑ 44 }
↑ 45
↑ 46 if (base->buckets)
↑ 47 free (base->buckets);
↑ 48
↑ 49 TYPE_ZERO(base);
↑ 50 }
↑ 51
↑ 52 int mimetype_load (mimetype_base_t *base, enum mimetype_type type,
↑ 53 const char *filename)
↑ 54 {
↑ 55 int return_code = 0;
↑ 56 struct stat stbuf;
↑ 57 int fd;
↑ 58 void *mapping = MAP_FAILED;
↑ 59 struct buffer_readline lines;
↑ 60
↑ 61
↑ 62 /*
↑ 63 *
↑ 64 * open config file
↑ 65 *
↑ 66 *
↑ 67 */
↑ 68
↑ 69 fd = open (filename, O_RDONLY);
↑ 70 if (fd == -1)
↑ 71 return -1;
↑ 72
↑ 73 if (-1 == fstat (fd, &stbuf)) {
↑ 74 return_code = -1;
↑ 75 goto out;
↑ 76 }
↑ 77
↑ 78 mapping = mmap (NULL, stbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
↑ 79 if (MAP_FAILED == mapping) {
↑ 80 return_code = -1;
↑ 81 goto out;
↑ 82 }
↑ 83
↑ 84
↑ 85 /*
↑ 86 *
↑ 87 * parse config file:
↑ 88 *
↑ 89 * # comment
↑ 90 *
↑ 91 * mime/type ext1 ext2 #comment
↑ 92 *
↑ 93 */
↑ 94
↑ 95 BUFFER_READLINE_INIT(&lines, mapping, stbuf.st_size);
↑ 96
↑ 97 while (0 == buffer_readline(&lines)) {
↑ 98 struct buffer_readline split;
↑ 99 const char *val;
↑100 size_t nval;
↑101
↑102 buffer_readline_strip_comment (&lines);
↑103 if (0 == lines.nline)
↑104 continue;
↑105
↑106 buffer_split_init(&split, lines.line, lines.nline, " \t");
↑107
↑108 if (0 != buffer_split(&split))
↑109 break;
↑110
↑111 val = split.line;
↑112 nval = split.nline;
↑113
↑114 while (0 == buffer_split(&split)) {
↑115
↑116 if (0 != mimetype_insert (
↑117 base,
↑118 type,
↑119 split.line, split.nline,
↑120 val, nval) )
↑121 {
↑122 return_code = -1;
↑123 goto out;
↑124 }
↑125 }
↑126 }
↑127
↑128
↑129
↑130
↑131
↑132 /*
↑133 *
↑134 * done
↑135 *
↑136 *
↑137 */
↑138 out:
↑139 if (fd > -1) {
↑140 if (mapping != MAP_FAILED)
↑141 munmap (mapping, stbuf.st_size);
↑142 close (fd);
↑143 }
↑144
↑145 return return_code;
↑146 }
↑147
↑148 /*
↑149 *
↑150 * grows base->buckets if necessary
↑151 *
↑152 */
↑153 static int mimetype_base_grow (mimetype_base_t *base)
↑154 {
↑155 struct mimetype_list *buckets;
↑156 size_t nbuckets;
↑157 size_t i;
↑158 size_t elements;
↑159
↑160 if (0 == base->nbuckets) {
↑161 nbuckets = 15;
↑162 } else {
↑163 /* ~ 5 elements per bucket */
↑164 if ( ( (1+base->elements) / base->nbuckets) < 5)
↑165 return 0;
↑166
↑167 nbuckets = base->nbuckets * 2;
↑168 }
↑169
↑170 /* LIST_INIT() is just setting NULL */
↑171 buckets = calloc(nbuckets, sizeof(*buckets));
↑172
↑173 if (NULL == buckets)
↑174 return -1;
↑175
↑176
↑177 /* re hash */
↑178
↑179 elements = base->elements;
↑180
↑181 for (i=0; i<base->nbuckets && elements; ++i) {
↑182 struct mimetype_list *list_src;
↑183
↑184 list_src = &base->buckets[i];
↑185
↑186 while (!LIST_EMPTY(list_src)) {
↑187 struct mimetype_list *list_dst;
↑188 mimetype_entry_t *entry;
↑189
↑190 entry = LIST_FIRST(list_src);
↑191 LIST_REMOVE(entry, list);
↑192
↑193 list_dst = &buckets[entry->key.hash % nbuckets];
↑194
↑195 LIST_INSERT_HEAD(list_dst, entry, list);
↑196
↑197 --elements;
↑198 }
↑199 }
↑200
↑201 free (base->buckets);
↑202
↑203 base->buckets = buckets;
↑204 base->nbuckets = nbuckets;
↑205
↑206 return 0;
↑207 }
↑208
↑209 static mimetype_entry_t * mimetype_get_internal (
↑210 mimetype_base_t *base, const char *key, size_t nkey)
↑211 {
↑212 size_t hashkey;
↑213 mimetype_entry_t *entry;
↑214 struct mimetype_list *list;
↑215
↑216 if (NULL == base->buckets || 0 == base->nbuckets)
↑217 return NULL;
↑218
↑219 hashkey = rh_hashcase33(key, nkey);
↑220
↑221 list = &base->buckets[hashkey % base->nbuckets];
↑222
↑223 LIST_FOREACH(entry, list, list) {
↑224 #if 0
↑225 printf ("%s(): [%.*s] == [%.*s]?\n", __FUNCTION__,
↑226 entry->key.used, entry->key.data,
↑227 nkey, key);
↑228 #endif
↑229
↑230 if (entry->key.used != nkey)
↑231 continue;
↑232
↑233 if (0 != rh_memcasecmp(entry->key.data, key, nkey))
↑234 continue;
↑235
↑236
↑237 return entry;
↑238 }
↑239
↑240 return NULL;
↑241 }
↑242
↑243 static void mimetype_remove_internal (mimetype_entry_t *entry)
↑244 {
↑245 #if 0
↑246 printf ("%s(%p): removed(%d) used(%zu) key[%.*s] val[%.*s]\n",
↑247 __FUNCTION__,
↑248 (void*)entry,
↑249 entry->removed,
↑250 entry->used,
↑251 entry->key.used, entry->key.data,
↑252 entry->value.used, entry->value.data );
↑253 #endif
↑254 if (entry->base) {
↑255 if (!entry->removed) {
↑256 --entry->base->elements;
↑257 LIST_REMOVE(entry, list);
↑258 entry->removed = 1;
↑259 }
↑260 }
↑261
↑262 if (entry->used)
↑263 --entry->used;
↑264
↑265 if (entry->used)
↑266 return;
↑267
↑268 rh_buffer_destroy(&entry->key);
↑269 rh_buffer_destroy(&entry->value);
↑270
↑271 TYPE_ZERO(entry);
↑272
↑273 free (entry);
↑274 }
↑275
↑276 int mimetype_insert (mimetype_base_t *base,
↑277 enum mimetype_type type,
↑278 const char *key, size_t nkey,
↑279 const char *value, size_t nvalue)
↑280 {
↑281 mimetype_entry_t *entry;
↑282 struct mimetype_list *list;
↑283
↑284 entry = mimetype_get_internal (base, key, nkey);
↑285
↑286 if (entry)
↑287 mimetype_remove_internal(entry);
↑288
↑289 if (0 != mimetype_base_grow (base))
↑290 return -1;
↑291
↑292 entry = calloc(1, sizeof(*entry));
↑293 if (NULL == entry)
↑294 return -1;
↑295
↑296 if (0 != rh_buffer_append(&entry->key, key, nkey))
↑297 goto error_out;
↑298
↑299 if (0 != rh_buffer_append(&entry->value, value, nvalue))
↑300 goto error_out;
↑301
↑302 if (base->max_typelength < nvalue)
↑303 base->max_typelength = nvalue;
↑304
↑305 entry->type = type;
↑306 entry->base = base;
↑307 ++entry->base->elements;
↑308
↑309 rh_buffer_hashcase (&entry->key);
↑310
↑311 list = &base->buckets[entry->key.hash % base->nbuckets];
↑312
↑313 LIST_INSERT_HEAD(list, entry, list);
↑314
↑315 entry->used = 1;
↑316
↑317 return 0;
↑318
↑319 error_out:
↑320 if (entry) {
↑321 rh_buffer_destroy(&entry->key);
↑322 rh_buffer_destroy(&entry->value);
↑323 }
↑324 return -1;
↑325 }
↑326
↑327 int mimetype_remove (mimetype_base_t *base, const char *key, size_t nkey)
↑328 {
↑329 mimetype_entry_t *entry;
↑330
↑331 entry = mimetype_get_internal (base, key, nkey);
↑332 if (entry)
↑333 mimetype_remove_internal(entry);
↑334
↑335 return 0;
↑336 }
↑337
↑338 void mimetype_dump (const mimetype_t *mime)
↑339 {
↑340 size_t i;
↑341
↑342 printf ("%s(%p):\n", __FUNCTION__, (void *)mime);
↑343
↑344 for (i=0; i<MIMETYPE_MAX; ++i) {
↑345 const mimetype_entry_t *entry;
↑346
↑347 entry = mime->entries[i];
↑348
↑349 printf (" %2d : ", i);
↑350
↑351 if (entry) {
↑352 printf ("[%.*s]\n",
↑353 entry->value.used, entry->value.data);
↑354 } else {
↑355 printf ("(NULL)\n");
↑356 }
↑357 }
↑358 }
↑359
↑360 int mimetype_pop (mimetype_base_t *base, mimetype_t *mime,
↑361 const char *filename, size_t nfilename)
↑362 {
↑363 const char *ext, *end;
↑364 size_t next;
↑365
↑366 #if 0
↑367 printf ("%s(): file(%.*s)\n", __FUNCTION__, nfilename, filename);
↑368 #endif
↑369
↑370 TYPE_ZERO(mime);
↑371 mime->initialized = 1;
↑372
↑373 if (0 == nfilename)
↑374 return 0;
↑375
↑376 end = filename;
↑377 next = 0;
↑378
↑379 for ( ext = filename + nfilename - 1;
↑380 ext > end && next < base->max_typelength;
↑381 --ext)
↑382 {
↑383 unsigned char ch = ext[0];
↑384
↑385 if ('.' == ch) {
↑386 mimetype_entry_t *entry;
↑387
↑388 if (0 == next)
↑389 return 0;
↑390 #if 0
↑391 printf ("%s(): ext[%.*s] next(%zu)\n", __FUNCTION__,
↑392 next, ext + 1,
↑393 next);
↑394 #endif
↑395
↑396 entry = mimetype_get_internal (base, ext + 1, next);
↑397 if (NULL == entry)
↑398 return 0;
↑399 #if 0
↑400 printf ("%s(): val[%.*s]\n", __FUNCTION__,
↑401 entry->value.used, entry->value.data);
↑402 #endif
↑403
↑404
↑405 /* found "that" entry already */
↑406 if (mime->entries[entry->type])
↑407 return 0;
↑408
↑409 mime->entries[entry->type] = entry;
↑410
↑411 ++entry->used;
↑412
↑413 next = 0;
↑414
↑415 continue;
↑416 }
↑417
↑418 if (0 == MAP_ALNUM[ch])
↑419 return 0;
↑420
↑421 ++next;
↑422 }
↑423
↑424 return 0;
↑425 }
↑426
↑427 void mimetype_push (mimetype_t *mime)
↑428 {
↑429 unsigned int i;
↑430
↑431 if (NULL == mime)
↑432 return;
↑433
↑434 for (i=0; i<MIMETYPE_MAX; ++i){
↑435 if (mime->entries[i]) {
↑436 mimetype_entry_t *entry = (void*)mime->entries[i];
↑437
↑438 if (entry->used < 1)
↑439 mimetype_remove_internal (entry);
↑440 else
↑441 --entry->used;
↑442 }
↑443 }
↑444
↑445 TYPE_ZERO(mime);
↑446 }
syntax highlighted by Code2HTML, v. 0.9.1