blob: 139d62f6d244cd02cb8edea30650395ec788bdd4 [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
Mark Salyzynba02cd12013-11-22 07:36:45 -080033#define UNUSED __attribute__((unused))
34
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -080035/* When an object is allocated but not freed in a function,
36 * because its ownership is released to other object like a hashmap,
37 * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
38 * false warnings about potential memory leak.
39 * For now, a "temporary" assignment to global variables
40 * is enough to confuse the clang static analyzer.
41 */
42#ifdef __clang_analyzer__
43static void *released_pointer;
44#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
45#else
46#define RELEASE_OWNERSHIP(x)
47#endif
48
Dima Zavin0fad7d02011-03-24 11:11:06 -070049struct str_parms {
50 Hashmap *map;
51};
52
53
54static bool str_eq(void *key_a, void *key_b)
55{
56 return !strcmp((const char *)key_a, (const char *)key_b);
57}
58
59/* use djb hash unless we find it inadequate */
Nick Kralevich73904782015-08-26 10:40:00 -070060#ifdef __clang__
61__attribute__((no_sanitize("integer")))
62#endif
Dima Zavin0fad7d02011-03-24 11:11:06 -070063static int str_hash_fn(void *str)
64{
65 uint32_t hash = 5381;
Dima Zavin0fad7d02011-03-24 11:11:06 -070066
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080067 for (char* p = static_cast<char*>(str); p && *p; p++)
Dima Zavin0fad7d02011-03-24 11:11:06 -070068 hash = ((hash << 5) + hash) + *p;
69 return (int)hash;
70}
71
72struct str_parms *str_parms_create(void)
73{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080074 str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
75 if (!s) return NULL;
Dima Zavin0fad7d02011-03-24 11:11:06 -070076
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080077 s->map = hashmapCreate(5, str_hash_fn, str_eq);
78 if (!s->map) {
79 free(s);
Dima Zavin0fad7d02011-03-24 11:11:06 -070080 return NULL;
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080081 }
Dima Zavin0fad7d02011-03-24 11:11:06 -070082
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080083 return s;
Dima Zavin0fad7d02011-03-24 11:11:06 -070084}
85
Dima Zavinefd75012012-03-14 23:12:40 -070086struct remove_ctxt {
87 struct str_parms *str_parms;
88 const char *key;
89};
90
Dima Zavin0fad7d02011-03-24 11:11:06 -070091static bool remove_pair(void *key, void *value, void *context)
92{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080093 remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
Dima Zavinefd75012012-03-14 23:12:40 -070094 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -070095
Dima Zavinefd75012012-03-14 23:12:40 -070096 /*
97 * - if key is not supplied, then we are removing all entries,
98 * so remove key and continue (i.e. return true)
99 * - if key is supplied and matches, then remove it and don't
100 * continue (return false). Otherwise, return true and keep searching
101 * for key.
102 *
103 */
104 if (!ctxt->key) {
105 should_continue = true;
106 goto do_remove;
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800107 } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
Dima Zavinefd75012012-03-14 23:12:40 -0700108 should_continue = false;
109 goto do_remove;
110 }
111
112 return true;
113
114do_remove:
115 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700116 free(key);
117 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700118 return should_continue;
119}
120
121void str_parms_del(struct str_parms *str_parms, const char *key)
122{
123 struct remove_ctxt ctxt = {
124 .str_parms = str_parms,
125 .key = key,
126 };
127 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700128}
129
130void str_parms_destroy(struct str_parms *str_parms)
131{
Dima Zavinefd75012012-03-14 23:12:40 -0700132 struct remove_ctxt ctxt = {
133 .str_parms = str_parms,
134 };
135
136 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700137 hashmapFree(str_parms->map);
138 free(str_parms);
139}
140
141struct str_parms *str_parms_create_str(const char *_string)
142{
143 struct str_parms *str_parms;
144 char *str;
145 char *kvpair;
146 char *tmpstr;
147 int items = 0;
148
149 str_parms = str_parms_create();
150 if (!str_parms)
151 goto err_create_str_parms;
152
153 str = strdup(_string);
154 if (!str)
155 goto err_strdup;
156
Steve Block69f4cd72011-10-20 11:54:09 +0100157 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700158
159 kvpair = strtok_r(str, ";", &tmpstr);
160 while (kvpair && *kvpair) {
161 char *eq = strchr(kvpair, '='); /* would love strchrnul */
162 char *value;
163 char *key;
164 void *old_val;
165
166 if (eq == kvpair)
167 goto next_pair;
168
169 if (eq) {
170 key = strndup(kvpair, eq - kvpair);
171 if (*(++eq))
172 value = strdup(eq);
173 else
174 value = strdup("");
175 } else {
176 key = strdup(kvpair);
177 value = strdup("");
178 }
179
180 /* if we replaced a value, free it */
181 old_val = hashmapPut(str_parms->map, key, value);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800182 RELEASE_OWNERSHIP(value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700183 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700184 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700185 free(key);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800186 } else {
187 RELEASE_OWNERSHIP(key);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700188 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700189
190 items++;
191next_pair:
192 kvpair = strtok_r(NULL, ";", &tmpstr);
193 }
194
195 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100196 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700197
198 free(str);
199
200 return str_parms;
201
202err_strdup:
203 str_parms_destroy(str_parms);
204err_create_str_parms:
205 return NULL;
206}
207
Dima Zavin0fad7d02011-03-24 11:11:06 -0700208int str_parms_add_str(struct str_parms *str_parms, const char *key,
209 const char *value)
210{
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100211 void *tmp_key = NULL;
212 void *tmp_val = NULL;
213 void *old_val = NULL;
214
215 // strdup and hashmapPut both set errno on failure.
216 // Set errno to 0 so we can recognize whether anything went wrong.
217 int saved_errno = errno;
218 errno = 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700219
Dima Zavin70b93032012-03-12 11:01:16 -0700220 tmp_key = strdup(key);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100221 if (tmp_key == NULL) {
222 goto clean_up;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700223 }
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100224
225 tmp_val = strdup(value);
226 if (tmp_val == NULL) {
227 goto clean_up;
228 }
229
230 old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
231 if (old_val == NULL) {
232 // Did hashmapPut fail?
233 if (errno == ENOMEM) {
234 goto clean_up;
235 }
236 // For new keys, hashmap takes ownership of tmp_key and tmp_val.
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800237 RELEASE_OWNERSHIP(tmp_key);
238 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100239 tmp_key = tmp_val = NULL;
240 } else {
241 // For existing keys, hashmap takes ownership of tmp_val.
242 // (It also gives up ownership of old_val.)
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800243 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100244 tmp_val = NULL;
245 }
246
247clean_up:
248 free(tmp_key);
249 free(tmp_val);
250 free(old_val);
251 int result = -errno;
252 errno = saved_errno;
253 return result;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700254}
255
256int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
257{
258 char val_str[12];
259 int ret;
260
261 ret = snprintf(val_str, sizeof(val_str), "%d", value);
262 if (ret < 0)
263 return -EINVAL;
264
265 ret = str_parms_add_str(str_parms, key, val_str);
266 return ret;
267}
268
269int str_parms_add_float(struct str_parms *str_parms, const char *key,
270 float value)
271{
272 char val_str[23];
273 int ret;
274
275 ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
276 if (ret < 0)
277 return -EINVAL;
278
279 ret = str_parms_add_str(str_parms, key, val_str);
280 return ret;
281}
282
Paul McLean55c64072013-12-19 15:47:29 -0800283int str_parms_has_key(struct str_parms *str_parms, const char *key) {
284 return hashmapGet(str_parms->map, (void *)key) != NULL;
285}
286
Dima Zavin0fad7d02011-03-24 11:11:06 -0700287int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
288 int len)
289{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800290 // TODO: hashmapGet should take a const* key.
291 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700292 if (value)
293 return strlcpy(val, value, len);
294
295 return -ENOENT;
296}
297
298int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
299{
Dima Zavin0fad7d02011-03-24 11:11:06 -0700300 char *end;
301
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800302 // TODO: hashmapGet should take a const* key.
303 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700304 if (!value)
305 return -ENOENT;
306
307 *val = (int)strtol(value, &end, 0);
308 if (*value != '\0' && *end == '\0')
309 return 0;
310
311 return -EINVAL;
312}
313
314int str_parms_get_float(struct str_parms *str_parms, const char *key,
315 float *val)
316{
317 float out;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700318 char *end;
319
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800320 // TODO: hashmapGet should take a const* key.
321 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
Dima Zavin0fad7d02011-03-24 11:11:06 -0700322 if (!value)
323 return -ENOENT;
324
325 out = strtof(value, &end);
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800326 if (*value == '\0' || *end != '\0')
327 return -EINVAL;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700328
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800329 *val = out;
330 return 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700331}
332
333static bool combine_strings(void *key, void *value, void *context)
334{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800335 char** old_str = static_cast<char**>(context);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700336 char *new_str;
337 int ret;
338
339 ret = asprintf(&new_str, "%s%s%s=%s",
340 *old_str ? *old_str : "",
341 *old_str ? ";" : "",
342 (char *)key,
343 (char *)value);
344 if (*old_str)
345 free(*old_str);
346
347 if (ret >= 0) {
348 *old_str = new_str;
349 return true;
350 }
351
352 *old_str = NULL;
353 return false;
354}
355
356char *str_parms_to_str(struct str_parms *str_parms)
357{
358 char *str = NULL;
359
360 if (hashmapSize(str_parms->map) > 0)
361 hashmapForEach(str_parms->map, combine_strings, &str);
362 else
363 str = strdup("");
364 return str;
365}
366
Mark Salyzynba02cd12013-11-22 07:36:45 -0800367static bool dump_entry(void *key, void *value, void *context UNUSED)
Dima Zavin0fad7d02011-03-24 11:11:06 -0700368{
Steve Blockfe71a612012-01-04 19:19:03 +0000369 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700370 return true;
371}
372
373void str_parms_dump(struct str_parms *str_parms)
374{
375 hashmapForEach(str_parms->map, dump_entry, str_parms);
376}