blob: f5a52a7670f2f588e51e5924b8c94e4ccfad33c4 [file] [log] [blame]
Dima Zavin0fad7d02011-03-24 11:11:06 -07001/*
Elliott Hughesaf98efb2015-04-02 13:36:54 -07002 * Copyright (C) 2011 The Android Open Source Project
Dima Zavin0fad7d02011-03-24 11:11:06 -07003 *
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
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080017#include <cutils/str_parms.h>
18
Dima Zavin0fad7d02011-03-24 11:11:06 -070019#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>
Dima Zavin0fad7d02011-03-24 11:11:06 -070030#include <cutils/memory.h>
Mark Salyzyn30f991f2017-01-10 13:19:54 -080031#include <log/log.h>
Dima Zavin0fad7d02011-03-24 11:11:06 -070032
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -080033/* 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__
41static void *released_pointer;
42#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
43#else
44#define RELEASE_OWNERSHIP(x)
45#endif
46
Dima Zavin0fad7d02011-03-24 11:11:06 -070047struct str_parms {
48 Hashmap *map;
49};
50
51
52static 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 */
Nick Kralevich73904782015-08-26 10:40:00 -070058#ifdef __clang__
59__attribute__((no_sanitize("integer")))
60#endif
Dima Zavin0fad7d02011-03-24 11:11:06 -070061static int str_hash_fn(void *str)
62{
63 uint32_t hash = 5381;
Dima Zavin0fad7d02011-03-24 11:11:06 -070064
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080065 for (char* p = static_cast<char*>(str); p && *p; p++)
Dima Zavin0fad7d02011-03-24 11:11:06 -070066 hash = ((hash << 5) + hash) + *p;
67 return (int)hash;
68}
69
70struct str_parms *str_parms_create(void)
71{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080072 str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
73 if (!s) return NULL;
Dima Zavin0fad7d02011-03-24 11:11:06 -070074
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080075 s->map = hashmapCreate(5, str_hash_fn, str_eq);
76 if (!s->map) {
77 free(s);
Dima Zavin0fad7d02011-03-24 11:11:06 -070078 return NULL;
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080079 }
Dima Zavin0fad7d02011-03-24 11:11:06 -070080
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080081 return s;
Dima Zavin0fad7d02011-03-24 11:11:06 -070082}
83
Dima Zavinefd75012012-03-14 23:12:40 -070084struct remove_ctxt {
85 struct str_parms *str_parms;
86 const char *key;
87};
88
Dima Zavin0fad7d02011-03-24 11:11:06 -070089static bool remove_pair(void *key, void *value, void *context)
90{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080091 remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
Dima Zavinefd75012012-03-14 23:12:40 -070092 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -070093
Dima Zavinefd75012012-03-14 23:12:40 -070094 /*
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;
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800105 } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
Dima Zavinefd75012012-03-14 23:12:40 -0700106 should_continue = false;
107 goto do_remove;
108 }
109
110 return true;
111
112do_remove:
113 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700114 free(key);
115 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700116 return should_continue;
117}
118
119void 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);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700126}
127
128void str_parms_destroy(struct str_parms *str_parms)
129{
Dima Zavinefd75012012-03-14 23:12:40 -0700130 struct remove_ctxt ctxt = {
131 .str_parms = str_parms,
132 };
133
134 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700135 hashmapFree(str_parms->map);
136 free(str_parms);
137}
138
139struct 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
Steve Block69f4cd72011-10-20 11:54:09 +0100155 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700156
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);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800180 RELEASE_OWNERSHIP(value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700181 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700182 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700183 free(key);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800184 } else {
185 RELEASE_OWNERSHIP(key);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700186 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700187
188 items++;
189next_pair:
190 kvpair = strtok_r(NULL, ";", &tmpstr);
191 }
192
193 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100194 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700195
196 free(str);
197
198 return str_parms;
199
200err_strdup:
201 str_parms_destroy(str_parms);
202err_create_str_parms:
203 return NULL;
204}
205
Dima Zavin0fad7d02011-03-24 11:11:06 -0700206int str_parms_add_str(struct str_parms *str_parms, const char *key,
207 const char *value)
208{
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100209 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;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700217
Dima Zavin70b93032012-03-12 11:01:16 -0700218 tmp_key = strdup(key);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100219 if (tmp_key == NULL) {
220 goto clean_up;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700221 }
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100222
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.
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800235 RELEASE_OWNERSHIP(tmp_key);
236 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100237 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.)
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800241 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100242 tmp_val = NULL;
243 }
244
245clean_up:
246 free(tmp_key);
247 free(tmp_val);
248 free(old_val);
249 int result = -errno;
250 errno = saved_errno;
251 return result;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700252}
253
254int 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
267int 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
Paul McLean55c64072013-12-19 15:47:29 -0800281int str_parms_has_key(struct str_parms *str_parms, const char *key) {
282 return hashmapGet(str_parms->map, (void *)key) != NULL;
283}
284
Dima Zavin0fad7d02011-03-24 11:11:06 -0700285int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
286 int len)
287{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800288 // TODO: hashmapGet should take a const* key.
289 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700290 if (value)
291 return strlcpy(val, value, len);
292
293 return -ENOENT;
294}
295
296int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
297{
Dima Zavin0fad7d02011-03-24 11:11:06 -0700298 char *end;
299
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800300 // TODO: hashmapGet should take a const* key.
301 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700302 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
312int str_parms_get_float(struct str_parms *str_parms, const char *key,
313 float *val)
314{
315 float out;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700316 char *end;
317
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800318 // TODO: hashmapGet should take a const* key.
319 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700320 if (!value)
321 return -ENOENT;
322
323 out = strtof(value, &end);
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800324 if (*value == '\0' || *end != '\0')
325 return -EINVAL;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700326
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800327 *val = out;
328 return 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700329}
330
331static bool combine_strings(void *key, void *value, void *context)
332{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800333 char** old_str = static_cast<char**>(context);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700334 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
354char *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
Elliott Hughes38d25672017-11-30 16:23:51 -0800365static bool dump_entry(void* key, void* value, void* /*context*/) {
Steve Blockfe71a612012-01-04 19:19:03 +0000366 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700367 return true;
368}
369
370void str_parms_dump(struct str_parms *str_parms)
371{
372 hashmapForEach(str_parms->map, dump_entry, str_parms);
373}