blob: dfe8c4ba717ae6ae97889058eddac49bae312446 [file] [log] [blame]
Dima Zavin0fad7d02011-03-24 11:11:06 -07001/*
Mark Salyzyn8e71dde2013-11-22 07:38:46 -08002 * Copyright (C) 2011-2013 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 Salyzyn12717162014-04-29 15:49:14 -070030#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
Dima Zavin0fad7d02011-03-24 11:11:06 -070034struct str_parms {
35 Hashmap *map;
36};
37
38
39static bool str_eq(void *key_a, void *key_b)
40{
41 return !strcmp((const char *)key_a, (const char *)key_b);
42}
43
44/* use djb hash unless we find it inadequate */
45static int str_hash_fn(void *str)
46{
47 uint32_t hash = 5381;
48 char *p;
49
50 for (p = str; p && *p; p++)
51 hash = ((hash << 5) + hash) + *p;
52 return (int)hash;
53}
54
55struct str_parms *str_parms_create(void)
56{
57 struct str_parms *str_parms;
58
59 str_parms = calloc(1, sizeof(struct str_parms));
60 if (!str_parms)
61 return NULL;
62
63 str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
64 if (!str_parms->map)
65 goto err;
66
67 return str_parms;
68
69err:
70 free(str_parms);
71 return NULL;
72}
73
Dima Zavinefd75012012-03-14 23:12:40 -070074struct remove_ctxt {
75 struct str_parms *str_parms;
76 const char *key;
77};
78
Dima Zavin0fad7d02011-03-24 11:11:06 -070079static bool remove_pair(void *key, void *value, void *context)
80{
Dima Zavinefd75012012-03-14 23:12:40 -070081 struct remove_ctxt *ctxt = context;
82 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -070083
Dima Zavinefd75012012-03-14 23:12:40 -070084 /*
85 * - if key is not supplied, then we are removing all entries,
86 * so remove key and continue (i.e. return true)
87 * - if key is supplied and matches, then remove it and don't
88 * continue (return false). Otherwise, return true and keep searching
89 * for key.
90 *
91 */
92 if (!ctxt->key) {
93 should_continue = true;
94 goto do_remove;
95 } else if (!strcmp(ctxt->key, key)) {
96 should_continue = false;
97 goto do_remove;
98 }
99
100 return true;
101
102do_remove:
103 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700104 free(key);
105 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700106 return should_continue;
107}
108
109void str_parms_del(struct str_parms *str_parms, const char *key)
110{
111 struct remove_ctxt ctxt = {
112 .str_parms = str_parms,
113 .key = key,
114 };
115 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700116}
117
118void str_parms_destroy(struct str_parms *str_parms)
119{
Dima Zavinefd75012012-03-14 23:12:40 -0700120 struct remove_ctxt ctxt = {
121 .str_parms = str_parms,
122 };
123
124 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700125 hashmapFree(str_parms->map);
126 free(str_parms);
127}
128
129struct str_parms *str_parms_create_str(const char *_string)
130{
131 struct str_parms *str_parms;
132 char *str;
133 char *kvpair;
134 char *tmpstr;
135 int items = 0;
136
137 str_parms = str_parms_create();
138 if (!str_parms)
139 goto err_create_str_parms;
140
141 str = strdup(_string);
142 if (!str)
143 goto err_strdup;
144
Steve Block69f4cd72011-10-20 11:54:09 +0100145 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700146
147 kvpair = strtok_r(str, ";", &tmpstr);
148 while (kvpair && *kvpair) {
149 char *eq = strchr(kvpair, '='); /* would love strchrnul */
150 char *value;
151 char *key;
152 void *old_val;
153
154 if (eq == kvpair)
155 goto next_pair;
156
157 if (eq) {
158 key = strndup(kvpair, eq - kvpair);
159 if (*(++eq))
160 value = strdup(eq);
161 else
162 value = strdup("");
163 } else {
164 key = strdup(kvpair);
165 value = strdup("");
166 }
167
168 /* if we replaced a value, free it */
169 old_val = hashmapPut(str_parms->map, key, value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700170 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700171 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700172 free(key);
173 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700174
175 items++;
176next_pair:
177 kvpair = strtok_r(NULL, ";", &tmpstr);
178 }
179
180 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100181 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700182
183 free(str);
184
185 return str_parms;
186
187err_strdup:
188 str_parms_destroy(str_parms);
189err_create_str_parms:
190 return NULL;
191}
192
Dima Zavin0fad7d02011-03-24 11:11:06 -0700193int str_parms_add_str(struct str_parms *str_parms, const char *key,
194 const char *value)
195{
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100196 void *tmp_key = NULL;
197 void *tmp_val = NULL;
198 void *old_val = NULL;
199
200 // strdup and hashmapPut both set errno on failure.
201 // Set errno to 0 so we can recognize whether anything went wrong.
202 int saved_errno = errno;
203 errno = 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700204
Dima Zavin70b93032012-03-12 11:01:16 -0700205 tmp_key = strdup(key);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100206 if (tmp_key == NULL) {
207 goto clean_up;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700208 }
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100209
210 tmp_val = strdup(value);
211 if (tmp_val == NULL) {
212 goto clean_up;
213 }
214
215 old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
216 if (old_val == NULL) {
217 // Did hashmapPut fail?
218 if (errno == ENOMEM) {
219 goto clean_up;
220 }
221 // For new keys, hashmap takes ownership of tmp_key and tmp_val.
222 tmp_key = tmp_val = NULL;
223 } else {
224 // For existing keys, hashmap takes ownership of tmp_val.
225 // (It also gives up ownership of old_val.)
226 tmp_val = NULL;
227 }
228
229clean_up:
230 free(tmp_key);
231 free(tmp_val);
232 free(old_val);
233 int result = -errno;
234 errno = saved_errno;
235 return result;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700236}
237
238int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
239{
240 char val_str[12];
241 int ret;
242
243 ret = snprintf(val_str, sizeof(val_str), "%d", value);
244 if (ret < 0)
245 return -EINVAL;
246
247 ret = str_parms_add_str(str_parms, key, val_str);
248 return ret;
249}
250
251int str_parms_add_float(struct str_parms *str_parms, const char *key,
252 float value)
253{
254 char val_str[23];
255 int ret;
256
257 ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
258 if (ret < 0)
259 return -EINVAL;
260
261 ret = str_parms_add_str(str_parms, key, val_str);
262 return ret;
263}
264
Paul McLean55c64072013-12-19 15:47:29 -0800265int str_parms_has_key(struct str_parms *str_parms, const char *key) {
266 return hashmapGet(str_parms->map, (void *)key) != NULL;
267}
268
Dima Zavin0fad7d02011-03-24 11:11:06 -0700269int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
270 int len)
271{
272 char *value;
273
274 value = hashmapGet(str_parms->map, (void *)key);
275 if (value)
276 return strlcpy(val, value, len);
277
278 return -ENOENT;
279}
280
281int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
282{
283 char *value;
284 char *end;
285
286 value = hashmapGet(str_parms->map, (void *)key);
287 if (!value)
288 return -ENOENT;
289
290 *val = (int)strtol(value, &end, 0);
291 if (*value != '\0' && *end == '\0')
292 return 0;
293
294 return -EINVAL;
295}
296
297int str_parms_get_float(struct str_parms *str_parms, const char *key,
298 float *val)
299{
300 float out;
301 char *value;
302 char *end;
303
304 value = hashmapGet(str_parms->map, (void *)key);
305 if (!value)
306 return -ENOENT;
307
308 out = strtof(value, &end);
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800309 if (*value == '\0' || *end != '\0')
310 return -EINVAL;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700311
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800312 *val = out;
313 return 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700314}
315
316static bool combine_strings(void *key, void *value, void *context)
317{
318 char **old_str = context;
319 char *new_str;
320 int ret;
321
322 ret = asprintf(&new_str, "%s%s%s=%s",
323 *old_str ? *old_str : "",
324 *old_str ? ";" : "",
325 (char *)key,
326 (char *)value);
327 if (*old_str)
328 free(*old_str);
329
330 if (ret >= 0) {
331 *old_str = new_str;
332 return true;
333 }
334
335 *old_str = NULL;
336 return false;
337}
338
339char *str_parms_to_str(struct str_parms *str_parms)
340{
341 char *str = NULL;
342
343 if (hashmapSize(str_parms->map) > 0)
344 hashmapForEach(str_parms->map, combine_strings, &str);
345 else
346 str = strdup("");
347 return str;
348}
349
Mark Salyzynba02cd12013-11-22 07:36:45 -0800350static bool dump_entry(void *key, void *value, void *context UNUSED)
Dima Zavin0fad7d02011-03-24 11:11:06 -0700351{
Steve Blockfe71a612012-01-04 19:19:03 +0000352 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700353 return true;
354}
355
356void str_parms_dump(struct str_parms *str_parms)
357{
358 hashmapForEach(str_parms->map, dump_entry, str_parms);
359}
360
361#ifdef TEST_STR_PARMS
362static void test_str_parms_str(const char *str)
363{
364 struct str_parms *str_parms;
365 char *out_str;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700366
367 str_parms = str_parms_create_str(str);
Dima Zavina603e402012-03-12 11:02:00 -0700368 str_parms_add_str(str_parms, "dude", "woah");
Dima Zavinefd75012012-03-14 23:12:40 -0700369 str_parms_add_str(str_parms, "dude", "woah");
370 str_parms_del(str_parms, "dude");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700371 str_parms_dump(str_parms);
372 out_str = str_parms_to_str(str_parms);
373 str_parms_destroy(str_parms);
Steve Blockfe71a612012-01-04 19:19:03 +0000374 ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700375 free(out_str);
376}
377
378int main(void)
379{
Dima Zavin0fad7d02011-03-24 11:11:06 -0700380 test_str_parms_str("");
381 test_str_parms_str(";");
382 test_str_parms_str("=");
383 test_str_parms_str("=;");
384 test_str_parms_str("=bar");
385 test_str_parms_str("=bar;");
386 test_str_parms_str("foo=");
387 test_str_parms_str("foo=;");
388 test_str_parms_str("foo=bar");
389 test_str_parms_str("foo=bar;");
390 test_str_parms_str("foo=bar;baz");
391 test_str_parms_str("foo=bar;baz=");
392 test_str_parms_str("foo=bar;baz=bat");
393 test_str_parms_str("foo=bar;baz=bat;");
Dima Zavin86bfbe32012-03-14 23:10:06 -0700394 test_str_parms_str("foo=bar;baz=bat;foo=bar");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700395
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100396 // hashmapPut reports errors by setting errno to ENOMEM.
397 // Test that we're not confused by running in an environment where this is already true.
398 errno = ENOMEM;
399 test_str_parms_str("foo=bar;baz=");
400 if (errno != ENOMEM) {
401 abort();
402 }
403 test_str_parms_str("foo=bar;baz=");
404
Dima Zavin0fad7d02011-03-24 11:11:06 -0700405 return 0;
406}
407#endif