1ea6ae205Smrg/*
2ea6ae205Smrg  Copyright (c) 2003 by Juliusz Chroboczek
3ea6ae205Smrg
4ea6ae205Smrg  Permission is hereby granted, free of charge, to any person obtaining a copy
5ea6ae205Smrg  of this software and associated documentation files (the "Software"), to deal
6ea6ae205Smrg  in the Software without restriction, including without limitation the rights
7ea6ae205Smrg  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8ea6ae205Smrg  copies of the Software, and to permit persons to whom the Software is
9ea6ae205Smrg  furnished to do so, subject to the following conditions:
10ea6ae205Smrg
11ea6ae205Smrg  The above copyright notice and this permission notice shall be included in
12ea6ae205Smrg  all copies or substantial portions of the Software.
13ea6ae205Smrg
14ea6ae205Smrg  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15ea6ae205Smrg  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16ea6ae205Smrg  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17ea6ae205Smrg  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18ea6ae205Smrg  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19ea6ae205Smrg  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20ea6ae205Smrg  THE SOFTWARE.
21ea6ae205Smrg*/
22ea6ae205Smrg
2340cc5db4Smrg#ifdef HAVE_CONFIG_H
2487aef7c3Smrg#include "config.h"
2540cc5db4Smrg#endif
2687aef7c3Smrg
27ea6ae205Smrg#include <stdlib.h>
28ea6ae205Smrg#include <stdio.h>
29ea6ae205Smrg#include <string.h>
30ea6ae205Smrg#include <ctype.h>
31ea6ae205Smrg#include "hash.h"
32ea6ae205Smrg#include "list.h"
33ea6ae205Smrg
34ea6ae205Smrg#define LOG2_NUMBUCKETS 10
35ea6ae205Smrg#define NUMBUCKETS (1 << LOG2_NUMBUCKETS)
36ea6ae205Smrg
37ea6ae205Smrgstatic unsigned
38663cdc11Smrghash(const char *string)
39ea6ae205Smrg{
40ea6ae205Smrg    unsigned u = 0;
416ae2c069Smrg
426ae2c069Smrg    for (int i = 0; string[i] != '\0'; i++)
436ae2c069Smrg        u = (u << 5) + (u >> (LOG2_NUMBUCKETS - 5)) + (unsigned char) string[i];
44ea6ae205Smrg    return (u & (NUMBUCKETS - 1));
45ea6ae205Smrg}
46ea6ae205Smrg
47ea6ae205Smrgstatic void
4887aef7c3Smrgstr_tolower(char *s)
49ea6ae205Smrg{
506ae2c069Smrg    while (*s != '\0') {
5187aef7c3Smrg        *s = tolower(*s);
5287aef7c3Smrg        s++;
53ea6ae205Smrg    }
54ea6ae205Smrg}
55ea6ae205Smrg
56ea6ae205SmrgHashTablePtr
57ea6ae205SmrgmakeHashTable(void)
58ea6ae205Smrg{
59ea6ae205Smrg    return calloc(NUMBUCKETS, sizeof(HashBucketPtr));
60ea6ae205Smrg}
61ea6ae205Smrg
62ea6ae205Smrgvoid
63ea6ae205SmrgdestroyHashTable(HashTablePtr table)
64ea6ae205Smrg{
656ae2c069Smrg    for (int i = 0; i < NUMBUCKETS; i++) {
666ae2c069Smrg        while (table[i]) {
676ae2c069Smrg            HashBucketPtr bp = table[i];
68ea6ae205Smrg            table[i] = table[i]->next;
69ea6ae205Smrg            free(bp->key);
70ea6ae205Smrg            free(bp->value);
71ea6ae205Smrg            free(bp);
72ea6ae205Smrg        }
73ea6ae205Smrg    }
74ea6ae205Smrg    free(table);
75ea6ae205Smrg}
76ea6ae205Smrg
77ea6ae205Smrgchar *
78663cdc11SmrggetHash(HashTablePtr table, const char *key)
79ea6ae205Smrg{
8087aef7c3Smrg    unsigned int i = hash(key);
816ae2c069Smrg
826ae2c069Smrg    for (HashBucketPtr bp = table[i]; bp; bp = bp->next) {
836ae2c069Smrg        if (strcasecmp(bp->key, key) == 0)
84ea6ae205Smrg            return bp->value;
85ea6ae205Smrg    }
86ea6ae205Smrg    return NULL;
87ea6ae205Smrg}
88ea6ae205Smrg
89ea6ae205Smrgint
90ea6ae205SmrgputHash(HashTablePtr table, char *key, char *value, int prio)
91ea6ae205Smrg{
9287aef7c3Smrg    unsigned int i = hash(key);
93ea6ae205Smrg    char *keycopy = NULL, *valuecopy = NULL;
94ea6ae205Smrg    HashBucketPtr bp;
956ae2c069Smrg
966ae2c069Smrg    for (bp = table[i]; bp; bp = bp->next) {
976ae2c069Smrg        if (strcasecmp(bp->key, key) == 0) {
986ae2c069Smrg            if (prio > bp->prio) {
9987aef7c3Smrg                keycopy = strdup(key);
1006ae2c069Smrg                if (keycopy == NULL)
1016ae2c069Smrg                    goto fail;
10287aef7c3Smrg                str_tolower(keycopy);
10387aef7c3Smrg                valuecopy = strdup(value);
1046ae2c069Smrg                if (valuecopy == NULL)
1056ae2c069Smrg                    goto fail;
106ea6ae205Smrg                free(bp->key);
107ea6ae205Smrg                free(bp->value);
108ea6ae205Smrg                bp->key = keycopy;
109ea6ae205Smrg                bp->value = valuecopy;
110ea6ae205Smrg            }
111ea6ae205Smrg            return 1;
112ea6ae205Smrg        }
113ea6ae205Smrg    }
11487aef7c3Smrg    keycopy = strdup(key);
1156ae2c069Smrg    if (keycopy == NULL)
116ea6ae205Smrg        goto fail;
11787aef7c3Smrg    str_tolower(keycopy);
11887aef7c3Smrg    valuecopy = strdup(value);
1196ae2c069Smrg    if (valuecopy == NULL)
120ea6ae205Smrg        goto fail;
121ea6ae205Smrg    bp = malloc(sizeof(HashBucketRec));
1226ae2c069Smrg    if (bp == NULL)
123ea6ae205Smrg        goto fail;
124ea6ae205Smrg    bp->key = keycopy;
125ea6ae205Smrg    bp->value = valuecopy;
126ea6ae205Smrg    bp->prio = prio;
127ea6ae205Smrg    bp->next = table[i];
128ea6ae205Smrg    table[i] = bp;
129ea6ae205Smrg    return 1;
130ea6ae205Smrg
131ea6ae205Smrg fail:
1326ae2c069Smrg    if (keycopy)
1336ae2c069Smrg        free(keycopy);
1346ae2c069Smrg    if (valuecopy)
1356ae2c069Smrg        free(valuecopy);
136ea6ae205Smrg    return -1;
137ea6ae205Smrg}
138ea6ae205Smrg
139ea6ae205Smrgint
140ea6ae205SmrghashElements(HashTablePtr table)
141ea6ae205Smrg{
1426ae2c069Smrg    int n = 0;
143ea6ae205Smrg
1446ae2c069Smrg    for (int i = 0; i < NUMBUCKETS; i++) {
1456ae2c069Smrg        for (HashBucketPtr bp = table[i]; bp; bp = bp->next) {
146ea6ae205Smrg            n++;
147ea6ae205Smrg        }
148ea6ae205Smrg    }
149ea6ae205Smrg    return n;
150ea6ae205Smrg}
151ea6ae205Smrg
152ea6ae205Smrgstatic int
153ea6ae205Smrgkey_first_cmp(const void *v1, const void *v2)
154ea6ae205Smrg{
155ea6ae205Smrg    const HashBucketPtr *b1 = v1, *b2 = v2;
156ea6ae205Smrg    int c1 = strcasecmp((*b1)->key, (*b2)->key);
1576ae2c069Smrg
1586ae2c069Smrg    if (c1 != 0)
1596ae2c069Smrg        return c1;
160ea6ae205Smrg    return strcmp((*b1)->value, (*b2)->value);
161ea6ae205Smrg}
162ea6ae205Smrg
163ea6ae205Smrgstatic int
164ea6ae205Smrgvalue_first_cmp(const void *v1, const void *v2)
165ea6ae205Smrg{
166ea6ae205Smrg    const HashBucketPtr *b1 = v1, *b2 = v2;
167ea6ae205Smrg    int c1 = strcmp((*b1)->value, (*b2)->value);
1686ae2c069Smrg
1696ae2c069Smrg    if (c1 != 0)
1706ae2c069Smrg        return c1;
171ea6ae205Smrg    return strcasecmp((*b1)->key, (*b2)->key);
172ea6ae205Smrg}
173ea6ae205Smrg
174ea6ae205SmrgHashBucketPtr *
175ea6ae205SmrghashArray(HashTablePtr table, int value_first)
176ea6ae205Smrg{
1776ae2c069Smrg    unsigned int j;
1786ae2c069Smrg    int n = hashElements(table);
1796ae2c069Smrg    HashBucketPtr *dst = malloc((n + 1) * sizeof(HashBucketPtr));
1806ae2c069Smrg    if (dst == NULL)
181ea6ae205Smrg        return NULL;
182ea6ae205Smrg
183ea6ae205Smrg    j = 0;
1846ae2c069Smrg    for (unsigned int i = 0; i < NUMBUCKETS; i++) {
1856ae2c069Smrg        while (table[i]) {
186ea6ae205Smrg            dst[j++] = table[i];
187ea6ae205Smrg            table[i] = table[i]->next;
188ea6ae205Smrg        }
189ea6ae205Smrg    }
190ea6ae205Smrg    qsort(dst, j, sizeof(HashBucketPtr),
191ea6ae205Smrg          value_first ? value_first_cmp : key_first_cmp);
192ea6ae205Smrg    dst[j++] = NULL;
193ea6ae205Smrg    free(table);
194ea6ae205Smrg
195ea6ae205Smrg    return dst;
196ea6ae205Smrg}
197ea6ae205Smrg
198ea6ae205Smrgvoid
199ea6ae205SmrgdestroyHashArray(HashBucketPtr *array)
200ea6ae205Smrg{
2016ae2c069Smrg    unsigned int i = 0;
2026ae2c069Smrg
2036ae2c069Smrg    while (array[i]) {
204ea6ae205Smrg        free(array[i]->key);
205ea6ae205Smrg        free(array[i]->value);
206ea6ae205Smrg        free(array[i]);
207ea6ae205Smrg        i++;
208ea6ae205Smrg    }
209ea6ae205Smrg    free(array);
210ea6ae205Smrg}
211