• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "str_params"
18 //#define LOG_NDEBUG 0
19 
20 #define _GNU_SOURCE 1
21 #include <errno.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <cutils/hashmap.h>
28 #include <cutils/log.h>
29 #include <cutils/memory.h>
30 
31 #include <cutils/str_parms.h>
32 
33 struct str_parms {
34     Hashmap *map;
35 };
36 
37 
str_eq(void * key_a,void * key_b)38 static bool str_eq(void *key_a, void *key_b)
39 {
40     return !strcmp((const char *)key_a, (const char *)key_b);
41 }
42 
43 /* use djb hash unless we find it inadequate */
str_hash_fn(void * str)44 static int str_hash_fn(void *str)
45 {
46     uint32_t hash = 5381;
47     char *p;
48 
49     for (p = str; p && *p; p++)
50         hash = ((hash << 5) + hash) + *p;
51     return (int)hash;
52 }
53 
str_parms_create(void)54 struct str_parms *str_parms_create(void)
55 {
56     struct str_parms *str_parms;
57 
58     str_parms = calloc(1, sizeof(struct str_parms));
59     if (!str_parms)
60         return NULL;
61 
62     str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
63     if (!str_parms->map)
64         goto err;
65 
66     return str_parms;
67 
68 err:
69     free(str_parms);
70     return NULL;
71 }
72 
73 struct remove_ctxt {
74     struct str_parms *str_parms;
75     const char *key;
76 };
77 
remove_pair(void * key,void * value,void * context)78 static bool remove_pair(void *key, void *value, void *context)
79 {
80     struct remove_ctxt *ctxt = context;
81     bool should_continue;
82 
83     /*
84      * - if key is not supplied, then we are removing all entries,
85      *   so remove key and continue (i.e. return true)
86      * - if key is supplied and matches, then remove it and don't
87      *   continue (return false). Otherwise, return true and keep searching
88      *   for key.
89      *
90      */
91     if (!ctxt->key) {
92         should_continue = true;
93         goto do_remove;
94     } else if (!strcmp(ctxt->key, key)) {
95         should_continue = false;
96         goto do_remove;
97     }
98 
99     return true;
100 
101 do_remove:
102     hashmapRemove(ctxt->str_parms->map, key);
103     free(key);
104     free(value);
105     return should_continue;
106 }
107 
str_parms_del(struct str_parms * str_parms,const char * key)108 void str_parms_del(struct str_parms *str_parms, const char *key)
109 {
110     struct remove_ctxt ctxt = {
111         .str_parms = str_parms,
112         .key = key,
113     };
114     hashmapForEach(str_parms->map, remove_pair, &ctxt);
115 }
116 
str_parms_destroy(struct str_parms * str_parms)117 void str_parms_destroy(struct str_parms *str_parms)
118 {
119     struct remove_ctxt ctxt = {
120         .str_parms = str_parms,
121     };
122 
123     hashmapForEach(str_parms->map, remove_pair, &ctxt);
124     hashmapFree(str_parms->map);
125     free(str_parms);
126 }
127 
str_parms_create_str(const char * _string)128 struct str_parms *str_parms_create_str(const char *_string)
129 {
130     struct str_parms *str_parms;
131     char *str;
132     char *kvpair;
133     char *tmpstr;
134     int items = 0;
135 
136     str_parms = str_parms_create();
137     if (!str_parms)
138         goto err_create_str_parms;
139 
140     str = strdup(_string);
141     if (!str)
142         goto err_strdup;
143 
144     ALOGV("%s: source string == '%s'\n", __func__, _string);
145 
146     kvpair = strtok_r(str, ";", &tmpstr);
147     while (kvpair && *kvpair) {
148         char *eq = strchr(kvpair, '='); /* would love strchrnul */
149         char *value;
150         char *key;
151         void *old_val;
152 
153         if (eq == kvpair)
154             goto next_pair;
155 
156         if (eq) {
157             key = strndup(kvpair, eq - kvpair);
158             if (*(++eq))
159                 value = strdup(eq);
160             else
161                 value = strdup("");
162         } else {
163             key = strdup(kvpair);
164             value = strdup("");
165         }
166 
167         /* if we replaced a value, free it */
168         old_val = hashmapPut(str_parms->map, key, value);
169         if (old_val) {
170             free(old_val);
171             free(key);
172         }
173 
174         items++;
175 next_pair:
176         kvpair = strtok_r(NULL, ";", &tmpstr);
177     }
178 
179     if (!items)
180         ALOGV("%s: no items found in string\n", __func__);
181 
182     free(str);
183 
184     return str_parms;
185 
186 err_strdup:
187     str_parms_destroy(str_parms);
188 err_create_str_parms:
189     return NULL;
190 }
191 
str_parms_add_str(struct str_parms * str_parms,const char * key,const char * value)192 int str_parms_add_str(struct str_parms *str_parms, const char *key,
193                       const char *value)
194 {
195     void *old_val;
196     void *tmp_key;
197     void *tmp_val;
198 
199     tmp_key = strdup(key);
200     tmp_val = strdup(value);
201     old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
202 
203     if (old_val) {
204         free(old_val);
205         free(tmp_key);
206     } else if (errno == ENOMEM) {
207         free(tmp_key);
208         free(tmp_val);
209         return -ENOMEM;
210     }
211     return 0;
212 }
213 
str_parms_add_int(struct str_parms * str_parms,const char * key,int value)214 int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
215 {
216     char val_str[12];
217     int ret;
218 
219     ret = snprintf(val_str, sizeof(val_str), "%d", value);
220     if (ret < 0)
221         return -EINVAL;
222 
223     ret = str_parms_add_str(str_parms, key, val_str);
224     return ret;
225 }
226 
str_parms_add_float(struct str_parms * str_parms,const char * key,float value)227 int str_parms_add_float(struct str_parms *str_parms, const char *key,
228                         float value)
229 {
230     char val_str[23];
231     int ret;
232 
233     ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
234     if (ret < 0)
235         return -EINVAL;
236 
237     ret = str_parms_add_str(str_parms, key, val_str);
238     return ret;
239 }
240 
str_parms_get_str(struct str_parms * str_parms,const char * key,char * val,int len)241 int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
242                       int len)
243 {
244     char *value;
245 
246     value = hashmapGet(str_parms->map, (void *)key);
247     if (value)
248         return strlcpy(val, value, len);
249 
250     return -ENOENT;
251 }
252 
str_parms_get_int(struct str_parms * str_parms,const char * key,int * val)253 int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
254 {
255     char *value;
256     char *end;
257 
258     value = hashmapGet(str_parms->map, (void *)key);
259     if (!value)
260         return -ENOENT;
261 
262     *val = (int)strtol(value, &end, 0);
263     if (*value != '\0' && *end == '\0')
264         return 0;
265 
266     return -EINVAL;
267 }
268 
str_parms_get_float(struct str_parms * str_parms,const char * key,float * val)269 int str_parms_get_float(struct str_parms *str_parms, const char *key,
270                         float *val)
271 {
272     float out;
273     char *value;
274     char *end;
275 
276     value = hashmapGet(str_parms->map, (void *)key);
277     if (!value)
278         return -ENOENT;
279 
280     out = strtof(value, &end);
281     if (*value != '\0' && *end == '\0')
282         return 0;
283 
284     return -EINVAL;
285 }
286 
combine_strings(void * key,void * value,void * context)287 static bool combine_strings(void *key, void *value, void *context)
288 {
289     char **old_str = context;
290     char *new_str;
291     int ret;
292 
293     ret = asprintf(&new_str, "%s%s%s=%s",
294                    *old_str ? *old_str : "",
295                    *old_str ? ";" : "",
296                    (char *)key,
297                    (char *)value);
298     if (*old_str)
299         free(*old_str);
300 
301     if (ret >= 0) {
302         *old_str = new_str;
303         return true;
304     }
305 
306     *old_str = NULL;
307     return false;
308 }
309 
str_parms_to_str(struct str_parms * str_parms)310 char *str_parms_to_str(struct str_parms *str_parms)
311 {
312     char *str = NULL;
313 
314     if (hashmapSize(str_parms->map) > 0)
315         hashmapForEach(str_parms->map, combine_strings, &str);
316     else
317         str = strdup("");
318     return str;
319 }
320 
dump_entry(void * key,void * value,void * context)321 static bool dump_entry(void *key, void *value, void *context)
322 {
323     ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
324     return true;
325 }
326 
str_parms_dump(struct str_parms * str_parms)327 void str_parms_dump(struct str_parms *str_parms)
328 {
329     hashmapForEach(str_parms->map, dump_entry, str_parms);
330 }
331 
332 #ifdef TEST_STR_PARMS
test_str_parms_str(const char * str)333 static void test_str_parms_str(const char *str)
334 {
335     struct str_parms *str_parms;
336     char *out_str;
337     int ret;
338 
339     str_parms = str_parms_create_str(str);
340     str_parms_add_str(str_parms, "dude", "woah");
341     str_parms_add_str(str_parms, "dude", "woah");
342     str_parms_del(str_parms, "dude");
343     str_parms_dump(str_parms);
344     out_str = str_parms_to_str(str_parms);
345     str_parms_destroy(str_parms);
346     ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
347     free(out_str);
348 }
349 
main(void)350 int main(void)
351 {
352     struct str_parms *str_parms;
353 
354     test_str_parms_str("");
355     test_str_parms_str(";");
356     test_str_parms_str("=");
357     test_str_parms_str("=;");
358     test_str_parms_str("=bar");
359     test_str_parms_str("=bar;");
360     test_str_parms_str("foo=");
361     test_str_parms_str("foo=;");
362     test_str_parms_str("foo=bar");
363     test_str_parms_str("foo=bar;");
364     test_str_parms_str("foo=bar;baz");
365     test_str_parms_str("foo=bar;baz=");
366     test_str_parms_str("foo=bar;baz=bat");
367     test_str_parms_str("foo=bar;baz=bat;");
368     test_str_parms_str("foo=bar;baz=bat;foo=bar");
369 
370     return 0;
371 }
372 #endif
373