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/memory.h>
29 #include <cutils/str_parms.h>
30 #include <log/log.h>
31 
32 #define UNUSED __attribute__((unused))
33 
34 struct str_parms {
35     Hashmap *map;
36 };
37 
38 
str_eq(void * key_a,void * key_b)39 static bool str_eq(void *key_a, void *key_b)
40 {
41     return !strcmp((const char *)key_a, (const char *)key_b);
42 }
43 
44 /* use djb hash unless we find it inadequate */
str_hash_fn(void * str)45 static int str_hash_fn(void *str)
46 {
47     uint32_t hash = 5381;
48     char *p;
49 
50     for (p = str; p && *p; p++)
51         hash = ((hash << 5) + hash) + *p;
52     return (int)hash;
53 }
54 
str_parms_create(void)55 struct str_parms *str_parms_create(void)
56 {
57     struct str_parms *str_parms;
58 
59     str_parms = calloc(1, sizeof(struct str_parms));
60     if (!str_parms)
61         return NULL;
62 
63     str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
64     if (!str_parms->map)
65         goto err;
66 
67     return str_parms;
68 
69 err:
70     free(str_parms);
71     return NULL;
72 }
73 
74 struct remove_ctxt {
75     struct str_parms *str_parms;
76     const char *key;
77 };
78 
remove_pair(void * key,void * value,void * context)79 static bool remove_pair(void *key, void *value, void *context)
80 {
81     struct remove_ctxt *ctxt = context;
82     bool should_continue;
83 
84     /*
85      * - if key is not supplied, then we are removing all entries,
86      *   so remove key and continue (i.e. return true)
87      * - if key is supplied and matches, then remove it and don't
88      *   continue (return false). Otherwise, return true and keep searching
89      *   for key.
90      *
91      */
92     if (!ctxt->key) {
93         should_continue = true;
94         goto do_remove;
95     } else if (!strcmp(ctxt->key, key)) {
96         should_continue = false;
97         goto do_remove;
98     }
99 
100     return true;
101 
102 do_remove:
103     hashmapRemove(ctxt->str_parms->map, key);
104     free(key);
105     free(value);
106     return should_continue;
107 }
108 
str_parms_del(struct str_parms * str_parms,const char * key)109 void str_parms_del(struct str_parms *str_parms, const char *key)
110 {
111     struct remove_ctxt ctxt = {
112         .str_parms = str_parms,
113         .key = key,
114     };
115     hashmapForEach(str_parms->map, remove_pair, &ctxt);
116 }
117 
str_parms_destroy(struct str_parms * str_parms)118 void str_parms_destroy(struct str_parms *str_parms)
119 {
120     struct remove_ctxt ctxt = {
121         .str_parms = str_parms,
122     };
123 
124     hashmapForEach(str_parms->map, remove_pair, &ctxt);
125     hashmapFree(str_parms->map);
126     free(str_parms);
127 }
128 
str_parms_create_str(const char * _string)129 struct str_parms *str_parms_create_str(const char *_string)
130 {
131     struct str_parms *str_parms;
132     char *str;
133     char *kvpair;
134     char *tmpstr;
135     int items = 0;
136 
137     str_parms = str_parms_create();
138     if (!str_parms)
139         goto err_create_str_parms;
140 
141     str = strdup(_string);
142     if (!str)
143         goto err_strdup;
144 
145     ALOGV("%s: source string == '%s'\n", __func__, _string);
146 
147     kvpair = strtok_r(str, ";", &tmpstr);
148     while (kvpair && *kvpair) {
149         char *eq = strchr(kvpair, '='); /* would love strchrnul */
150         char *value;
151         char *key;
152         void *old_val;
153 
154         if (eq == kvpair)
155             goto next_pair;
156 
157         if (eq) {
158             key = strndup(kvpair, eq - kvpair);
159             if (*(++eq))
160                 value = strdup(eq);
161             else
162                 value = strdup("");
163         } else {
164             key = strdup(kvpair);
165             value = strdup("");
166         }
167 
168         /* if we replaced a value, free it */
169         old_val = hashmapPut(str_parms->map, key, value);
170         if (old_val) {
171             free(old_val);
172             free(key);
173         }
174 
175         items++;
176 next_pair:
177         kvpair = strtok_r(NULL, ";", &tmpstr);
178     }
179 
180     if (!items)
181         ALOGV("%s: no items found in string\n", __func__);
182 
183     free(str);
184 
185     return str_parms;
186 
187 err_strdup:
188     str_parms_destroy(str_parms);
189 err_create_str_parms:
190     return NULL;
191 }
192 
str_parms_add_str(struct str_parms * str_parms,const char * key,const char * value)193 int str_parms_add_str(struct str_parms *str_parms, const char *key,
194                       const char *value)
195 {
196     void *tmp_key = NULL;
197     void *tmp_val = NULL;
198     void *old_val = NULL;
199 
200     // strdup and hashmapPut both set errno on failure.
201     // Set errno to 0 so we can recognize whether anything went wrong.
202     int saved_errno = errno;
203     errno = 0;
204 
205     tmp_key = strdup(key);
206     if (tmp_key == NULL) {
207         goto clean_up;
208     }
209 
210     tmp_val = strdup(value);
211     if (tmp_val == NULL) {
212         goto clean_up;
213     }
214 
215     old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
216     if (old_val == NULL) {
217         // Did hashmapPut fail?
218         if (errno == ENOMEM) {
219             goto clean_up;
220         }
221         // For new keys, hashmap takes ownership of tmp_key and tmp_val.
222         tmp_key = tmp_val = NULL;
223     } else {
224         // For existing keys, hashmap takes ownership of tmp_val.
225         // (It also gives up ownership of old_val.)
226         tmp_val = NULL;
227     }
228 
229 clean_up:
230     free(tmp_key);
231     free(tmp_val);
232     free(old_val);
233     int result = -errno;
234     errno = saved_errno;
235     return result;
236 }
237 
str_parms_add_int(struct str_parms * str_parms,const char * key,int value)238 int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
239 {
240     char val_str[12];
241     int ret;
242 
243     ret = snprintf(val_str, sizeof(val_str), "%d", value);
244     if (ret < 0)
245         return -EINVAL;
246 
247     ret = str_parms_add_str(str_parms, key, val_str);
248     return ret;
249 }
250 
str_parms_add_float(struct str_parms * str_parms,const char * key,float value)251 int str_parms_add_float(struct str_parms *str_parms, const char *key,
252                         float value)
253 {
254     char val_str[23];
255     int ret;
256 
257     ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
258     if (ret < 0)
259         return -EINVAL;
260 
261     ret = str_parms_add_str(str_parms, key, val_str);
262     return ret;
263 }
264 
str_parms_has_key(struct str_parms * str_parms,const char * key)265 int str_parms_has_key(struct str_parms *str_parms, const char *key) {
266     return hashmapGet(str_parms->map, (void *)key) != NULL;
267 }
268 
str_parms_get_str(struct str_parms * str_parms,const char * key,char * val,int len)269 int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
270                       int len)
271 {
272     char *value;
273 
274     value = hashmapGet(str_parms->map, (void *)key);
275     if (value)
276         return strlcpy(val, value, len);
277 
278     return -ENOENT;
279 }
280 
str_parms_get_int(struct str_parms * str_parms,const char * key,int * val)281 int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
282 {
283     char *value;
284     char *end;
285 
286     value = hashmapGet(str_parms->map, (void *)key);
287     if (!value)
288         return -ENOENT;
289 
290     *val = (int)strtol(value, &end, 0);
291     if (*value != '\0' && *end == '\0')
292         return 0;
293 
294     return -EINVAL;
295 }
296 
str_parms_get_float(struct str_parms * str_parms,const char * key,float * val)297 int str_parms_get_float(struct str_parms *str_parms, const char *key,
298                         float *val)
299 {
300     float out;
301     char *value;
302     char *end;
303 
304     value = hashmapGet(str_parms->map, (void *)key);
305     if (!value)
306         return -ENOENT;
307 
308     out = strtof(value, &end);
309     if (*value == '\0' || *end != '\0')
310         return -EINVAL;
311 
312     *val = out;
313     return 0;
314 }
315 
combine_strings(void * key,void * value,void * context)316 static bool combine_strings(void *key, void *value, void *context)
317 {
318     char **old_str = context;
319     char *new_str;
320     int ret;
321 
322     ret = asprintf(&new_str, "%s%s%s=%s",
323                    *old_str ? *old_str : "",
324                    *old_str ? ";" : "",
325                    (char *)key,
326                    (char *)value);
327     if (*old_str)
328         free(*old_str);
329 
330     if (ret >= 0) {
331         *old_str = new_str;
332         return true;
333     }
334 
335     *old_str = NULL;
336     return false;
337 }
338 
str_parms_to_str(struct str_parms * str_parms)339 char *str_parms_to_str(struct str_parms *str_parms)
340 {
341     char *str = NULL;
342 
343     if (hashmapSize(str_parms->map) > 0)
344         hashmapForEach(str_parms->map, combine_strings, &str);
345     else
346         str = strdup("");
347     return str;
348 }
349 
dump_entry(void * key,void * value,void * context UNUSED)350 static bool dump_entry(void *key, void *value, void *context UNUSED)
351 {
352     ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
353     return true;
354 }
355 
str_parms_dump(struct str_parms * str_parms)356 void str_parms_dump(struct str_parms *str_parms)
357 {
358     hashmapForEach(str_parms->map, dump_entry, str_parms);
359 }
360