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