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