blob: 8dafdedac1a89b2ad8e25420b21e73bfa19a113a [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
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>
Dima Zavin0fad7d02011-03-24 11:11:06 -070028#include <cutils/memory.h>
Dima Zavin0fad7d02011-03-24 11:11:06 -070029#include <cutils/str_parms.h>
Mark Salyzyn30f991f2017-01-10 13:19:54 -080030#include <log/log.h>
Dima Zavin0fad7d02011-03-24 11:11:06 -070031
Mark Salyzynba02cd12013-11-22 07:36:45 -080032#define UNUSED __attribute__((unused))
33
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -080034/* 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__
42static void *released_pointer;
43#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
44#else
45#define RELEASE_OWNERSHIP(x)
46#endif
47
Dima Zavin0fad7d02011-03-24 11:11:06 -070048struct str_parms {
49 Hashmap *map;
50};
51
52
53static 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 */
Nick Kralevich73904782015-08-26 10:40:00 -070059#ifdef __clang__
60__attribute__((no_sanitize("integer")))
61#endif
Dima Zavin0fad7d02011-03-24 11:11:06 -070062static 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
72struct 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
86err:
87 free(str_parms);
88 return NULL;
89}
90
Dima Zavinefd75012012-03-14 23:12:40 -070091struct remove_ctxt {
92 struct str_parms *str_parms;
93 const char *key;
94};
95
Dima Zavin0fad7d02011-03-24 11:11:06 -070096static bool remove_pair(void *key, void *value, void *context)
97{
Dima Zavinefd75012012-03-14 23:12:40 -070098 struct remove_ctxt *ctxt = context;
99 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700100
Dima Zavinefd75012012-03-14 23:12:40 -0700101 /*
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
119do_remove:
120 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700121 free(key);
122 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700123 return should_continue;
124}
125
126void 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);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700133}
134
135void str_parms_destroy(struct str_parms *str_parms)
136{
Dima Zavinefd75012012-03-14 23:12:40 -0700137 struct remove_ctxt ctxt = {
138 .str_parms = str_parms,
139 };
140
141 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700142 hashmapFree(str_parms->map);
143 free(str_parms);
144}
145
146struct 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
Steve Block69f4cd72011-10-20 11:54:09 +0100162 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700163
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);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800187 RELEASE_OWNERSHIP(value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700188 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700189 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700190 free(key);
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800191 } else {
192 RELEASE_OWNERSHIP(key);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700193 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700194
195 items++;
196next_pair:
197 kvpair = strtok_r(NULL, ";", &tmpstr);
198 }
199
200 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100201 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700202
203 free(str);
204
205 return str_parms;
206
207err_strdup:
208 str_parms_destroy(str_parms);
209err_create_str_parms:
210 return NULL;
211}
212
Dima Zavin0fad7d02011-03-24 11:11:06 -0700213int str_parms_add_str(struct str_parms *str_parms, const char *key,
214 const char *value)
215{
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100216 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;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700224
Dima Zavin70b93032012-03-12 11:01:16 -0700225 tmp_key = strdup(key);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100226 if (tmp_key == NULL) {
227 goto clean_up;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700228 }
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100229
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.
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800242 RELEASE_OWNERSHIP(tmp_key);
243 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100244 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.)
Chih-Hung Hsiehf7e7bd42016-02-03 10:40:45 -0800248 RELEASE_OWNERSHIP(tmp_val);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100249 tmp_val = NULL;
250 }
251
252clean_up:
253 free(tmp_key);
254 free(tmp_val);
255 free(old_val);
256 int result = -errno;
257 errno = saved_errno;
258 return result;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700259}
260
261int 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
274int 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
Paul McLean55c64072013-12-19 15:47:29 -0800288int str_parms_has_key(struct str_parms *str_parms, const char *key) {
289 return hashmapGet(str_parms->map, (void *)key) != NULL;
290}
291
Dima Zavin0fad7d02011-03-24 11:11:06 -0700292int 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
304int 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
320int 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);
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800332 if (*value == '\0' || *end != '\0')
333 return -EINVAL;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700334
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800335 *val = out;
336 return 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700337}
338
339static 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
362char *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
Mark Salyzynba02cd12013-11-22 07:36:45 -0800373static bool dump_entry(void *key, void *value, void *context UNUSED)
Dima Zavin0fad7d02011-03-24 11:11:06 -0700374{
Steve Blockfe71a612012-01-04 19:19:03 +0000375 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700376 return true;
377}
378
379void str_parms_dump(struct str_parms *str_parms)
380{
381 hashmapForEach(str_parms->map, dump_entry, str_parms);
382}