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 hashmapForEach(str_parms->map, combine_strings, &str);
358 return (str != NULL) ? str : strdup("");
359 }
360
dump_entry(void * key,void * value,void *)361 static bool dump_entry(void* key, void* value, void* /*context*/) {
362 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
363 return true;
364 }
365
str_parms_dump(struct str_parms * str_parms)366 void str_parms_dump(struct str_parms *str_parms)
367 {
368 hashmapForEach(str_parms->map, dump_entry, str_parms);
369 }
370