1*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
2*437bfbebSnyanmisaka /**
3*437bfbebSnyanmisaka @file dictionary.c
4*437bfbebSnyanmisaka @author N. Devillard
5*437bfbebSnyanmisaka @brief Implements a dictionary for string variables.
6*437bfbebSnyanmisaka
7*437bfbebSnyanmisaka This module implements a simple dictionary object, i.e. a list
8*437bfbebSnyanmisaka of string/string associations. This object is useful to store e.g.
9*437bfbebSnyanmisaka informations retrieved from a configuration file (ini files).
10*437bfbebSnyanmisaka */
11*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
12*437bfbebSnyanmisaka
13*437bfbebSnyanmisaka /*---------------------------------------------------------------------------
14*437bfbebSnyanmisaka Includes
15*437bfbebSnyanmisaka ---------------------------------------------------------------------------*/
16*437bfbebSnyanmisaka #include "dictionary.h"
17*437bfbebSnyanmisaka
18*437bfbebSnyanmisaka #include <stdio.h>
19*437bfbebSnyanmisaka #include <stdlib.h>
20*437bfbebSnyanmisaka #include <string.h>
21*437bfbebSnyanmisaka #include <unistd.h>
22*437bfbebSnyanmisaka
23*437bfbebSnyanmisaka /** Maximum value size for integers and doubles. */
24*437bfbebSnyanmisaka #define MAXVALSZ 1024
25*437bfbebSnyanmisaka
26*437bfbebSnyanmisaka /** Minimal allocated number of entries in a dictionary */
27*437bfbebSnyanmisaka #define DICTMINSZ 128
28*437bfbebSnyanmisaka
29*437bfbebSnyanmisaka /** Invalid key token */
30*437bfbebSnyanmisaka #define DICT_INVALID_KEY ((char*)-1)
31*437bfbebSnyanmisaka
32*437bfbebSnyanmisaka /*---------------------------------------------------------------------------
33*437bfbebSnyanmisaka Private functions
34*437bfbebSnyanmisaka ---------------------------------------------------------------------------*/
35*437bfbebSnyanmisaka
36*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
37*437bfbebSnyanmisaka /**
38*437bfbebSnyanmisaka @brief Duplicate a string
39*437bfbebSnyanmisaka @param s String to duplicate
40*437bfbebSnyanmisaka @return Pointer to a newly allocated string, to be freed with free()
41*437bfbebSnyanmisaka
42*437bfbebSnyanmisaka This is a replacement for strdup(). This implementation is provided
43*437bfbebSnyanmisaka for systems that do not have it.
44*437bfbebSnyanmisaka */
45*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
xstrdup(const char * s)46*437bfbebSnyanmisaka static char * xstrdup(const char * s)
47*437bfbebSnyanmisaka {
48*437bfbebSnyanmisaka char * t ;
49*437bfbebSnyanmisaka size_t len ;
50*437bfbebSnyanmisaka if (!s)
51*437bfbebSnyanmisaka return NULL ;
52*437bfbebSnyanmisaka
53*437bfbebSnyanmisaka len = strlen(s) + 1 ;
54*437bfbebSnyanmisaka t = (char*) malloc(len) ;
55*437bfbebSnyanmisaka if (t) {
56*437bfbebSnyanmisaka memcpy(t, s, len) ;
57*437bfbebSnyanmisaka }
58*437bfbebSnyanmisaka return t ;
59*437bfbebSnyanmisaka }
60*437bfbebSnyanmisaka
61*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
62*437bfbebSnyanmisaka /**
63*437bfbebSnyanmisaka @brief Double the size of the dictionary
64*437bfbebSnyanmisaka @param d Dictionary to grow
65*437bfbebSnyanmisaka @return This function returns non-zero in case of failure
66*437bfbebSnyanmisaka */
67*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_grow(dictionary * d)68*437bfbebSnyanmisaka static int dictionary_grow(dictionary * d)
69*437bfbebSnyanmisaka {
70*437bfbebSnyanmisaka char ** new_val ;
71*437bfbebSnyanmisaka char ** new_key ;
72*437bfbebSnyanmisaka unsigned * new_hash ;
73*437bfbebSnyanmisaka
74*437bfbebSnyanmisaka new_val = (char**) calloc(d->size * 2, sizeof * d->val);
75*437bfbebSnyanmisaka new_key = (char**) calloc(d->size * 2, sizeof * d->key);
76*437bfbebSnyanmisaka new_hash = (unsigned*) calloc(d->size * 2, sizeof * d->hash);
77*437bfbebSnyanmisaka if (!new_val || !new_key || !new_hash) {
78*437bfbebSnyanmisaka /* An allocation failed, leave the dictionary unchanged */
79*437bfbebSnyanmisaka if (new_val)
80*437bfbebSnyanmisaka free(new_val);
81*437bfbebSnyanmisaka if (new_key)
82*437bfbebSnyanmisaka free(new_key);
83*437bfbebSnyanmisaka if (new_hash)
84*437bfbebSnyanmisaka free(new_hash);
85*437bfbebSnyanmisaka return -1 ;
86*437bfbebSnyanmisaka }
87*437bfbebSnyanmisaka /* Initialize the newly allocated space */
88*437bfbebSnyanmisaka memcpy(new_val, d->val, d->size * sizeof(char *));
89*437bfbebSnyanmisaka memcpy(new_key, d->key, d->size * sizeof(char *));
90*437bfbebSnyanmisaka memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
91*437bfbebSnyanmisaka /* Delete previous data */
92*437bfbebSnyanmisaka free(d->val);
93*437bfbebSnyanmisaka free(d->key);
94*437bfbebSnyanmisaka free(d->hash);
95*437bfbebSnyanmisaka /* Actually update the dictionary */
96*437bfbebSnyanmisaka d->size *= 2 ;
97*437bfbebSnyanmisaka d->val = new_val;
98*437bfbebSnyanmisaka d->key = new_key;
99*437bfbebSnyanmisaka d->hash = new_hash;
100*437bfbebSnyanmisaka return 0 ;
101*437bfbebSnyanmisaka }
102*437bfbebSnyanmisaka
103*437bfbebSnyanmisaka /*---------------------------------------------------------------------------
104*437bfbebSnyanmisaka Function codes
105*437bfbebSnyanmisaka ---------------------------------------------------------------------------*/
106*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
107*437bfbebSnyanmisaka /**
108*437bfbebSnyanmisaka @brief Compute the hash key for a string.
109*437bfbebSnyanmisaka @param key Character string to use for key.
110*437bfbebSnyanmisaka @return 1 unsigned int on at least 32 bits.
111*437bfbebSnyanmisaka
112*437bfbebSnyanmisaka This hash function has been taken from an Article in Dr Dobbs Journal.
113*437bfbebSnyanmisaka This is normally a collision-free function, distributing keys evenly.
114*437bfbebSnyanmisaka The key is stored anyway in the struct so that collision can be avoided
115*437bfbebSnyanmisaka by comparing the key itself in last resort.
116*437bfbebSnyanmisaka */
117*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_hash(const char * key)118*437bfbebSnyanmisaka unsigned dictionary_hash(const char * key)
119*437bfbebSnyanmisaka {
120*437bfbebSnyanmisaka size_t len ;
121*437bfbebSnyanmisaka unsigned hash ;
122*437bfbebSnyanmisaka size_t i ;
123*437bfbebSnyanmisaka
124*437bfbebSnyanmisaka if (!key)
125*437bfbebSnyanmisaka return 0 ;
126*437bfbebSnyanmisaka
127*437bfbebSnyanmisaka len = strlen(key);
128*437bfbebSnyanmisaka for (hash = 0, i = 0 ; i < len ; i++) {
129*437bfbebSnyanmisaka hash += (unsigned)key[i] ;
130*437bfbebSnyanmisaka hash += (hash << 10);
131*437bfbebSnyanmisaka hash ^= (hash >> 6) ;
132*437bfbebSnyanmisaka }
133*437bfbebSnyanmisaka hash += (hash << 3);
134*437bfbebSnyanmisaka hash ^= (hash >> 11);
135*437bfbebSnyanmisaka hash += (hash << 15);
136*437bfbebSnyanmisaka return hash ;
137*437bfbebSnyanmisaka }
138*437bfbebSnyanmisaka
139*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
140*437bfbebSnyanmisaka /**
141*437bfbebSnyanmisaka @brief Create a new dictionary object.
142*437bfbebSnyanmisaka @param size Optional initial size of the dictionary.
143*437bfbebSnyanmisaka @return 1 newly allocated dictionary objet.
144*437bfbebSnyanmisaka
145*437bfbebSnyanmisaka This function allocates a new dictionary object of given size and returns
146*437bfbebSnyanmisaka it. If you do not know in advance (roughly) the number of entries in the
147*437bfbebSnyanmisaka dictionary, give size=0.
148*437bfbebSnyanmisaka */
149*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
dictionary_new(size_t size)150*437bfbebSnyanmisaka dictionary * dictionary_new(size_t size)
151*437bfbebSnyanmisaka {
152*437bfbebSnyanmisaka dictionary * d ;
153*437bfbebSnyanmisaka
154*437bfbebSnyanmisaka /* If no size was specified, allocate space for DICTMINSZ */
155*437bfbebSnyanmisaka if (size < DICTMINSZ) size = DICTMINSZ ;
156*437bfbebSnyanmisaka
157*437bfbebSnyanmisaka d = (dictionary*) calloc(1, sizeof * d) ;
158*437bfbebSnyanmisaka
159*437bfbebSnyanmisaka if (d) {
160*437bfbebSnyanmisaka d->size = size ;
161*437bfbebSnyanmisaka d->val = (char**) calloc(size, sizeof * d->val);
162*437bfbebSnyanmisaka d->key = (char**) calloc(size, sizeof * d->key);
163*437bfbebSnyanmisaka d->hash = (unsigned*) calloc(size, sizeof * d->hash);
164*437bfbebSnyanmisaka }
165*437bfbebSnyanmisaka return d ;
166*437bfbebSnyanmisaka }
167*437bfbebSnyanmisaka
168*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
169*437bfbebSnyanmisaka /**
170*437bfbebSnyanmisaka @brief Delete a dictionary object
171*437bfbebSnyanmisaka @param d dictionary object to deallocate.
172*437bfbebSnyanmisaka @return void
173*437bfbebSnyanmisaka
174*437bfbebSnyanmisaka Deallocate a dictionary object and all memory associated to it.
175*437bfbebSnyanmisaka */
176*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_del(dictionary * d)177*437bfbebSnyanmisaka void dictionary_del(dictionary * d)
178*437bfbebSnyanmisaka {
179*437bfbebSnyanmisaka ssize_t i ;
180*437bfbebSnyanmisaka
181*437bfbebSnyanmisaka if (d == NULL) return ;
182*437bfbebSnyanmisaka for (i = 0 ; i < d->size ; i++) {
183*437bfbebSnyanmisaka if (d->key[i] != NULL)
184*437bfbebSnyanmisaka free(d->key[i]);
185*437bfbebSnyanmisaka if (d->val[i] != NULL)
186*437bfbebSnyanmisaka free(d->val[i]);
187*437bfbebSnyanmisaka }
188*437bfbebSnyanmisaka free(d->val);
189*437bfbebSnyanmisaka free(d->key);
190*437bfbebSnyanmisaka free(d->hash);
191*437bfbebSnyanmisaka free(d);
192*437bfbebSnyanmisaka return ;
193*437bfbebSnyanmisaka }
194*437bfbebSnyanmisaka
195*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
196*437bfbebSnyanmisaka /**
197*437bfbebSnyanmisaka @brief Get a value from a dictionary.
198*437bfbebSnyanmisaka @param d dictionary object to search.
199*437bfbebSnyanmisaka @param key Key to look for in the dictionary.
200*437bfbebSnyanmisaka @param def Default value to return if key not found.
201*437bfbebSnyanmisaka @return 1 pointer to internally allocated character string.
202*437bfbebSnyanmisaka
203*437bfbebSnyanmisaka This function locates a key in a dictionary and returns a pointer to its
204*437bfbebSnyanmisaka value, or the passed 'def' pointer if no such key can be found in
205*437bfbebSnyanmisaka dictionary. The returned character pointer points to data internal to the
206*437bfbebSnyanmisaka dictionary object, you should not try to free it or modify it.
207*437bfbebSnyanmisaka */
208*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_get(const dictionary * d,const char * key,const char * def)209*437bfbebSnyanmisaka const char * dictionary_get(const dictionary * d, const char * key, const char * def)
210*437bfbebSnyanmisaka {
211*437bfbebSnyanmisaka unsigned hash ;
212*437bfbebSnyanmisaka ssize_t i ;
213*437bfbebSnyanmisaka
214*437bfbebSnyanmisaka hash = dictionary_hash(key);
215*437bfbebSnyanmisaka for (i = 0 ; i < d->size ; i++) {
216*437bfbebSnyanmisaka if (d->key[i] == NULL)
217*437bfbebSnyanmisaka continue ;
218*437bfbebSnyanmisaka /* Compare hash */
219*437bfbebSnyanmisaka if (hash == d->hash[i]) {
220*437bfbebSnyanmisaka /* Compare string, to avoid hash collisions */
221*437bfbebSnyanmisaka if (!strcmp(key, d->key[i])) {
222*437bfbebSnyanmisaka return d->val[i] ;
223*437bfbebSnyanmisaka }
224*437bfbebSnyanmisaka }
225*437bfbebSnyanmisaka }
226*437bfbebSnyanmisaka return def ;
227*437bfbebSnyanmisaka }
228*437bfbebSnyanmisaka
229*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
230*437bfbebSnyanmisaka /**
231*437bfbebSnyanmisaka @brief Set a value in a dictionary.
232*437bfbebSnyanmisaka @param d dictionary object to modify.
233*437bfbebSnyanmisaka @param key Key to modify or add.
234*437bfbebSnyanmisaka @param val Value to add.
235*437bfbebSnyanmisaka @return int 0 if Ok, anything else otherwise
236*437bfbebSnyanmisaka
237*437bfbebSnyanmisaka If the given key is found in the dictionary, the associated value is
238*437bfbebSnyanmisaka replaced by the provided one. If the key cannot be found in the
239*437bfbebSnyanmisaka dictionary, it is added to it.
240*437bfbebSnyanmisaka
241*437bfbebSnyanmisaka It is Ok to provide a NULL value for val, but NULL values for the dictionary
242*437bfbebSnyanmisaka or the key are considered as errors: the function will return immediately
243*437bfbebSnyanmisaka in such a case.
244*437bfbebSnyanmisaka
245*437bfbebSnyanmisaka Notice that if you dictionary_set a variable to NULL, a call to
246*437bfbebSnyanmisaka dictionary_get will return a NULL value: the variable will be found, and
247*437bfbebSnyanmisaka its value (NULL) is returned. In other words, setting the variable
248*437bfbebSnyanmisaka content to NULL is equivalent to deleting the variable from the
249*437bfbebSnyanmisaka dictionary. It is not possible (in this implementation) to have a key in
250*437bfbebSnyanmisaka the dictionary without value.
251*437bfbebSnyanmisaka
252*437bfbebSnyanmisaka This function returns non-zero in case of failure.
253*437bfbebSnyanmisaka */
254*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_set(dictionary * d,const char * key,const char * val)255*437bfbebSnyanmisaka int dictionary_set(dictionary * d, const char * key, const char * val)
256*437bfbebSnyanmisaka {
257*437bfbebSnyanmisaka ssize_t i ;
258*437bfbebSnyanmisaka unsigned hash ;
259*437bfbebSnyanmisaka
260*437bfbebSnyanmisaka if (d == NULL || key == NULL) return -1 ;
261*437bfbebSnyanmisaka
262*437bfbebSnyanmisaka /* Compute hash for this key */
263*437bfbebSnyanmisaka hash = dictionary_hash(key) ;
264*437bfbebSnyanmisaka /* Find if value is already in dictionary */
265*437bfbebSnyanmisaka if (d->n > 0) {
266*437bfbebSnyanmisaka for (i = 0 ; i < d->size ; i++) {
267*437bfbebSnyanmisaka if (d->key[i] == NULL)
268*437bfbebSnyanmisaka continue ;
269*437bfbebSnyanmisaka if (hash == d->hash[i]) { /* Same hash value */
270*437bfbebSnyanmisaka if (!strcmp(key, d->key[i])) { /* Same key */
271*437bfbebSnyanmisaka /* Found a value: modify and return */
272*437bfbebSnyanmisaka if (d->val[i] != NULL)
273*437bfbebSnyanmisaka free(d->val[i]);
274*437bfbebSnyanmisaka d->val[i] = (val ? xstrdup(val) : NULL);
275*437bfbebSnyanmisaka /* Value has been modified: return */
276*437bfbebSnyanmisaka return 0 ;
277*437bfbebSnyanmisaka }
278*437bfbebSnyanmisaka }
279*437bfbebSnyanmisaka }
280*437bfbebSnyanmisaka }
281*437bfbebSnyanmisaka /* Add a new value */
282*437bfbebSnyanmisaka /* See if dictionary needs to grow */
283*437bfbebSnyanmisaka if (d->n == d->size) {
284*437bfbebSnyanmisaka /* Reached maximum size: reallocate dictionary */
285*437bfbebSnyanmisaka if (dictionary_grow(d) != 0)
286*437bfbebSnyanmisaka return -1;
287*437bfbebSnyanmisaka }
288*437bfbebSnyanmisaka
289*437bfbebSnyanmisaka /* Insert key in the first empty slot. Start at d->n and wrap at
290*437bfbebSnyanmisaka d->size. Because d->n < d->size this will necessarily
291*437bfbebSnyanmisaka terminate. */
292*437bfbebSnyanmisaka for (i = d->n ; d->key[i] ; ) {
293*437bfbebSnyanmisaka if (++i == d->size) i = 0;
294*437bfbebSnyanmisaka }
295*437bfbebSnyanmisaka /* Copy key */
296*437bfbebSnyanmisaka d->key[i] = xstrdup(key);
297*437bfbebSnyanmisaka d->val[i] = (val ? xstrdup(val) : NULL) ;
298*437bfbebSnyanmisaka d->hash[i] = hash;
299*437bfbebSnyanmisaka d->n ++ ;
300*437bfbebSnyanmisaka return 0 ;
301*437bfbebSnyanmisaka }
302*437bfbebSnyanmisaka
303*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
304*437bfbebSnyanmisaka /**
305*437bfbebSnyanmisaka @brief Delete a key in a dictionary
306*437bfbebSnyanmisaka @param d dictionary object to modify.
307*437bfbebSnyanmisaka @param key Key to remove.
308*437bfbebSnyanmisaka @return void
309*437bfbebSnyanmisaka
310*437bfbebSnyanmisaka This function deletes a key in a dictionary. Nothing is done if the
311*437bfbebSnyanmisaka key cannot be found.
312*437bfbebSnyanmisaka */
313*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_unset(dictionary * d,const char * key)314*437bfbebSnyanmisaka void dictionary_unset(dictionary * d, const char * key)
315*437bfbebSnyanmisaka {
316*437bfbebSnyanmisaka unsigned hash ;
317*437bfbebSnyanmisaka ssize_t i ;
318*437bfbebSnyanmisaka
319*437bfbebSnyanmisaka if (key == NULL || d == NULL) {
320*437bfbebSnyanmisaka return;
321*437bfbebSnyanmisaka }
322*437bfbebSnyanmisaka
323*437bfbebSnyanmisaka hash = dictionary_hash(key);
324*437bfbebSnyanmisaka for (i = 0 ; i < d->size ; i++) {
325*437bfbebSnyanmisaka if (d->key[i] == NULL)
326*437bfbebSnyanmisaka continue ;
327*437bfbebSnyanmisaka /* Compare hash */
328*437bfbebSnyanmisaka if (hash == d->hash[i]) {
329*437bfbebSnyanmisaka /* Compare string, to avoid hash collisions */
330*437bfbebSnyanmisaka if (!strcmp(key, d->key[i])) {
331*437bfbebSnyanmisaka /* Found key */
332*437bfbebSnyanmisaka break ;
333*437bfbebSnyanmisaka }
334*437bfbebSnyanmisaka }
335*437bfbebSnyanmisaka }
336*437bfbebSnyanmisaka if (i >= d->size)
337*437bfbebSnyanmisaka /* Key not found */
338*437bfbebSnyanmisaka return ;
339*437bfbebSnyanmisaka
340*437bfbebSnyanmisaka free(d->key[i]);
341*437bfbebSnyanmisaka d->key[i] = NULL ;
342*437bfbebSnyanmisaka if (d->val[i] != NULL) {
343*437bfbebSnyanmisaka free(d->val[i]);
344*437bfbebSnyanmisaka d->val[i] = NULL ;
345*437bfbebSnyanmisaka }
346*437bfbebSnyanmisaka d->hash[i] = 0 ;
347*437bfbebSnyanmisaka d->n -- ;
348*437bfbebSnyanmisaka return ;
349*437bfbebSnyanmisaka }
350*437bfbebSnyanmisaka
351*437bfbebSnyanmisaka /*-------------------------------------------------------------------------*/
352*437bfbebSnyanmisaka /**
353*437bfbebSnyanmisaka @brief Dump a dictionary to an opened file pointer.
354*437bfbebSnyanmisaka @param d Dictionary to dump
355*437bfbebSnyanmisaka @param f Opened file pointer.
356*437bfbebSnyanmisaka @return void
357*437bfbebSnyanmisaka
358*437bfbebSnyanmisaka Dumps a dictionary onto an opened file pointer. Key pairs are printed out
359*437bfbebSnyanmisaka as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
360*437bfbebSnyanmisaka output file pointers.
361*437bfbebSnyanmisaka */
362*437bfbebSnyanmisaka /*--------------------------------------------------------------------------*/
dictionary_dump(const dictionary * d,FILE * out)363*437bfbebSnyanmisaka void dictionary_dump(const dictionary * d, FILE * out)
364*437bfbebSnyanmisaka {
365*437bfbebSnyanmisaka ssize_t i ;
366*437bfbebSnyanmisaka
367*437bfbebSnyanmisaka if (d == NULL || out == NULL) return ;
368*437bfbebSnyanmisaka if (d->n < 1) {
369*437bfbebSnyanmisaka fprintf(out, "empty dictionary\n");
370*437bfbebSnyanmisaka return ;
371*437bfbebSnyanmisaka }
372*437bfbebSnyanmisaka for (i = 0 ; i < d->size ; i++) {
373*437bfbebSnyanmisaka if (d->key[i]) {
374*437bfbebSnyanmisaka fprintf(out, "%20s\t[%s]\n",
375*437bfbebSnyanmisaka d->key[i],
376*437bfbebSnyanmisaka d->val[i] ? d->val[i] : "UNDEF");
377*437bfbebSnyanmisaka }
378*437bfbebSnyanmisaka }
379*437bfbebSnyanmisaka return ;
380*437bfbebSnyanmisaka }
381