1 /* spmfilter - mail filtering framework
2 * Copyright (C) 2009-2012 Axel Steiner and SpaceNet AG
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define THIS_MODULE "dict"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <assert.h>
25
26 #include "smf_internal.h"
27 #include "smf_dict.h"
28 #include "smf_list.h"
29
30 #define _set_ptr(ptr, val) if (ptr != NULL) { *ptr = val; }
31
32 /* Doubles the allocated size associated to a pointer */
33 /* 'size' is the current allocated size. */
_mem_double(void * ptr,int size)34 static void *_mem_double(void * ptr, int size) {
35 void * newptr ;
36
37 newptr = calloc(2*size, 1);
38 if (newptr==NULL) {
39 return NULL ;
40 }
41 memcpy(newptr, ptr, size);
42 free(ptr);
43 return newptr ;
44 }
45
_dict_hash(const char * key)46 unsigned _dict_hash(const char * key) {
47 int len;
48 unsigned hash;
49 int i;
50
51 len = strlen(key);
52 for (hash=0, i=0 ; i<len ; i++) {
53 hash += (unsigned)key[i] ;
54 hash += (hash<<10);
55 hash ^= (hash>>6) ;
56 }
57 hash += (hash <<3);
58 hash ^= (hash >>11);
59 hash += (hash <<15);
60 return hash;
61 }
62
smf_dict_new(void)63 SMFDict_T *smf_dict_new(void) {
64 SMFDict_T *dict = NULL;
65
66 if (!(dict = (SMFDict_T *)calloc(1, sizeof(SMFDict_T))))
67 return NULL;
68
69 dict->size = 128;
70 dict->val = (char **)calloc(128, sizeof(char*));
71 dict->key = (char **)calloc(128, sizeof(char*));
72 dict->hash = (unsigned int *)calloc(128, sizeof(unsigned));
73 return dict;
74 }
75
smf_dict_free(SMFDict_T * dict)76 void smf_dict_free(SMFDict_T *dict) {
77 int i ;
78
79 assert(dict);
80
81 for (i=0; i<dict->size; i++) {
82 if (dict->key[i] != NULL)
83 free(dict->key[i]);
84 if (dict->val[i] != NULL)
85 free(dict->val[i]);
86 }
87
88 free(dict->val);
89 free(dict->key);
90 free(dict->hash);
91 free(dict);
92 }
93
smf_dict_set(SMFDict_T * dict,const char * key,const char * val)94 int smf_dict_set(SMFDict_T *dict, const char * key, const char * val) {
95 int i;
96 unsigned hash;
97
98 assert(dict);
99 assert(key);
100 assert(val);
101
102 /* Compute hash for this key */
103 hash = _dict_hash(key);
104
105 /* Find if value is already in dictionary */
106 if (dict->n>0) {
107 for (i = 0; i < dict->size; i++) {
108 if (dict->key[i] == NULL)
109 continue;
110 if (hash == dict->hash[i]) { /* Same hash value */
111 if (!strcmp(key, dict->key[i])) { /* Same key */
112 /* Found a value: modify and return */
113 if (dict->val[i]!=NULL)
114 free(dict->val[i]);
115 dict->val[i] = val ? strdup(val) : NULL;
116 /* Value has been modified: return */
117 return 0;
118 }
119 }
120 }
121 }
122
123 /* Add a new value */
124 /* See if dictionary needs to grow */
125 if (dict->n == dict->size) {
126 /* Reached maximum size: reallocate dictionary */
127 dict->val = (char **)_mem_double(dict->val, dict->size * sizeof(char*));
128 dict->key = (char **)_mem_double(dict->key, dict->size * sizeof(char*)) ;
129 dict->hash = (unsigned int *)_mem_double(dict->hash, dict->size * sizeof(unsigned)) ;
130 if ((dict->val == NULL) || (dict->key == NULL) || (dict->hash == NULL)) {
131 /* Cannot grow dictionary */
132 return -1;
133 }
134 /* Double size */
135 dict->size *= 2 ;
136 }
137
138 /* Insert key in the first empty slot. Start at d->n and wrap at
139 d->size. Because d->n < d->size this will necessarily
140 terminate. */
141 for (i = dict->n; dict->key[i]; ) {
142 if(++i == dict->size) i = 0;
143 }
144 /* Copy key */
145 dict->key[i] = strdup(key);
146 dict->val[i] = val ? strdup(val) : NULL ;
147 dict->hash[i] = hash;
148 dict->n ++;
149 return 0;
150 }
151
smf_dict_get(SMFDict_T * dict,const char * key)152 char *smf_dict_get(SMFDict_T *dict, const char * key) {
153 unsigned hash;
154 int i;
155
156 assert(dict);
157 assert(key);
158
159 hash = _dict_hash(key);
160 for (i = 0; i < dict->size; i++) {
161 if (dict->key[i] == NULL)
162 continue ;
163 /* Compare hash */
164 if (hash == dict->hash[i]) {
165 /* Compare string, to avoid hash collisions */
166 if (!strcmp(key, dict->key[i])) {
167 return dict->val[i] ;
168 }
169 }
170 }
171 return NULL;
172 }
173
smf_dict_get_ulong(SMFDict_T * dict,const char * key,int * success)174 unsigned long smf_dict_get_ulong(SMFDict_T *dict, const char * key, int *success) {
175 char *value;
176 char *endptr;
177 unsigned long lval;
178
179 assert(dict);
180 assert(key);
181
182 if ((value = smf_dict_get(dict, key)) == NULL) {
183 _set_ptr(success, 0);
184 return -1;
185 }
186
187 lval = strtoul(value, &endptr, 10);
188
189 if (endptr == value || *endptr != '\0') {
190 _set_ptr(success, 0);
191 return -1;
192 }
193
194 _set_ptr(success, 1);
195 return lval;
196 }
197
smf_dict_remove(SMFDict_T * dict,const char * key)198 void smf_dict_remove(SMFDict_T *dict, const char * key) {
199 unsigned hash;
200 int i;
201
202 assert(dict);
203 assert(key);
204
205 hash = _dict_hash(key);
206 for (i=0; i<dict->size; i++) {
207 if (dict->key[i]==NULL)
208 continue ;
209 /* Compare hash */
210 if (hash==dict->hash[i]) {
211 /* Compare string, to avoid hash collisions */
212 if (!strcmp(key, dict->key[i])) {
213 /* Found key */
214 break;
215 }
216 }
217 }
218 if (i>=dict->size)
219 /* Key not found */
220 return;
221
222 free(dict->key[i]);
223 dict->key[i] = NULL ;
224 if (dict->val[i]!=NULL) {
225 free(dict->val[i]);
226 dict->val[i] = NULL;
227 }
228 dict->hash[i] = 0;
229 dict->n --;
230 }
231
smf_dict_get_keys(SMFDict_T * dict)232 SMFList_T *smf_dict_get_keys(SMFDict_T *dict) {
233 int i = 0;
234 SMFList_T *l = NULL;
235 assert(dict);
236
237 if (smf_list_new(&l,smf_internal_string_list_destroy)!=0)
238 return NULL;
239
240 for (i=0 ; i<dict->size ; i++) {
241 if (dict->key[i]) {
242 if (smf_list_append(l, strdup(dict->key[i])) != 0) {
243 smf_list_free(l);
244 return NULL;
245 }
246 }
247 }
248 return l;
249 }
250
smf_dict_map(SMFDict_T * dict,void (* func)(char * key,char * value,void * args),void * args)251 void smf_dict_map(SMFDict_T *dict, void(*func)(char *key,char *value, void *args), void *args) {
252 assert(dict);
253 SMFList_T *list = NULL;
254 SMFListElem_T *elem = NULL;
255 char *key = NULL;
256 char *value = NULL;
257
258 assert(dict);
259
260 list = smf_dict_get_keys(dict);
261 elem = cmime_list_head(list);
262 while(elem != NULL) {
263 key = (char *)smf_list_data(elem);
264 value = smf_dict_get(dict,key);
265 func(key,value,args);
266 elem = elem->next;
267 }
268
269 smf_list_free(list);
270 }
271
272