blob: d2b97e8836d8e67fc37245f737af32a1d8b4e463 [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>
28#include <cutils/log.h>
29#include <cutils/memory.h>
30
31#include <cutils/str_parms.h>
32
Mark Salyzynba02cd12013-11-22 07:36:45 -080033#define UNUSED __attribute__((unused))
34
Dima Zavin0fad7d02011-03-24 11:11:06 -070035struct str_parms {
36 Hashmap *map;
37};
38
39
40static bool str_eq(void *key_a, void *key_b)
41{
42 return !strcmp((const char *)key_a, (const char *)key_b);
43}
44
45/* use djb hash unless we find it inadequate */
46static int str_hash_fn(void *str)
47{
48 uint32_t hash = 5381;
49 char *p;
50
51 for (p = str; p && *p; p++)
52 hash = ((hash << 5) + hash) + *p;
53 return (int)hash;
54}
55
56struct str_parms *str_parms_create(void)
57{
58 struct str_parms *str_parms;
59
60 str_parms = calloc(1, sizeof(struct str_parms));
61 if (!str_parms)
62 return NULL;
63
64 str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
65 if (!str_parms->map)
66 goto err;
67
68 return str_parms;
69
70err:
71 free(str_parms);
72 return NULL;
73}
74
Dima Zavinefd75012012-03-14 23:12:40 -070075struct remove_ctxt {
76 struct str_parms *str_parms;
77 const char *key;
78};
79
Dima Zavin0fad7d02011-03-24 11:11:06 -070080static bool remove_pair(void *key, void *value, void *context)
81{
Dima Zavinefd75012012-03-14 23:12:40 -070082 struct remove_ctxt *ctxt = context;
83 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -070084
Dima Zavinefd75012012-03-14 23:12:40 -070085 /*
86 * - if key is not supplied, then we are removing all entries,
87 * so remove key and continue (i.e. return true)
88 * - if key is supplied and matches, then remove it and don't
89 * continue (return false). Otherwise, return true and keep searching
90 * for key.
91 *
92 */
93 if (!ctxt->key) {
94 should_continue = true;
95 goto do_remove;
96 } else if (!strcmp(ctxt->key, key)) {
97 should_continue = false;
98 goto do_remove;
99 }
100
101 return true;
102
103do_remove:
104 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700105 free(key);
106 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700107 return should_continue;
108}
109
110void str_parms_del(struct str_parms *str_parms, const char *key)
111{
112 struct remove_ctxt ctxt = {
113 .str_parms = str_parms,
114 .key = key,
115 };
116 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700117}
118
119void str_parms_destroy(struct str_parms *str_parms)
120{
Dima Zavinefd75012012-03-14 23:12:40 -0700121 struct remove_ctxt ctxt = {
122 .str_parms = str_parms,
123 };
124
125 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700126 hashmapFree(str_parms->map);
127 free(str_parms);
128}
129
130struct str_parms *str_parms_create_str(const char *_string)
131{
132 struct str_parms *str_parms;
133 char *str;
134 char *kvpair;
135 char *tmpstr;
136 int items = 0;
137
138 str_parms = str_parms_create();
139 if (!str_parms)
140 goto err_create_str_parms;
141
142 str = strdup(_string);
143 if (!str)
144 goto err_strdup;
145
Steve Block69f4cd72011-10-20 11:54:09 +0100146 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700147
148 kvpair = strtok_r(str, ";", &tmpstr);
149 while (kvpair && *kvpair) {
150 char *eq = strchr(kvpair, '='); /* would love strchrnul */
151 char *value;
152 char *key;
153 void *old_val;
154
155 if (eq == kvpair)
156 goto next_pair;
157
158 if (eq) {
159 key = strndup(kvpair, eq - kvpair);
160 if (*(++eq))
161 value = strdup(eq);
162 else
163 value = strdup("");
164 } else {
165 key = strdup(kvpair);
166 value = strdup("");
167 }
168
169 /* if we replaced a value, free it */
170 old_val = hashmapPut(str_parms->map, key, value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700171 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700172 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700173 free(key);
174 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700175
176 items++;
177next_pair:
178 kvpair = strtok_r(NULL, ";", &tmpstr);
179 }
180
181 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100182 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700183
184 free(str);
185
186 return str_parms;
187
188err_strdup:
189 str_parms_destroy(str_parms);
190err_create_str_parms:
191 return NULL;
192}
193
Dima Zavin0fad7d02011-03-24 11:11:06 -0700194int str_parms_add_str(struct str_parms *str_parms, const char *key,
195 const char *value)
196{
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100197 void *tmp_key = NULL;
198 void *tmp_val = NULL;
199 void *old_val = NULL;
200
201 // strdup and hashmapPut both set errno on failure.
202 // Set errno to 0 so we can recognize whether anything went wrong.
203 int saved_errno = errno;
204 errno = 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700205
Dima Zavin70b93032012-03-12 11:01:16 -0700206 tmp_key = strdup(key);
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100207 if (tmp_key == NULL) {
208 goto clean_up;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700209 }
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100210
211 tmp_val = strdup(value);
212 if (tmp_val == NULL) {
213 goto clean_up;
214 }
215
216 old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
217 if (old_val == NULL) {
218 // Did hashmapPut fail?
219 if (errno == ENOMEM) {
220 goto clean_up;
221 }
222 // For new keys, hashmap takes ownership of tmp_key and tmp_val.
223 tmp_key = tmp_val = NULL;
224 } else {
225 // For existing keys, hashmap takes ownership of tmp_val.
226 // (It also gives up ownership of old_val.)
227 tmp_val = NULL;
228 }
229
230clean_up:
231 free(tmp_key);
232 free(tmp_val);
233 free(old_val);
234 int result = -errno;
235 errno = saved_errno;
236 return result;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700237}
238
239int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
240{
241 char val_str[12];
242 int ret;
243
244 ret = snprintf(val_str, sizeof(val_str), "%d", value);
245 if (ret < 0)
246 return -EINVAL;
247
248 ret = str_parms_add_str(str_parms, key, val_str);
249 return ret;
250}
251
252int str_parms_add_float(struct str_parms *str_parms, const char *key,
253 float value)
254{
255 char val_str[23];
256 int ret;
257
258 ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
259 if (ret < 0)
260 return -EINVAL;
261
262 ret = str_parms_add_str(str_parms, key, val_str);
263 return ret;
264}
265
Paul McLean55c64072013-12-19 15:47:29 -0800266int str_parms_has_key(struct str_parms *str_parms, const char *key) {
267 return hashmapGet(str_parms->map, (void *)key) != NULL;
268}
269
Dima Zavin0fad7d02011-03-24 11:11:06 -0700270int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
271 int len)
272{
273 char *value;
274
275 value = hashmapGet(str_parms->map, (void *)key);
276 if (value)
277 return strlcpy(val, value, len);
278
279 return -ENOENT;
280}
281
282int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
283{
284 char *value;
285 char *end;
286
287 value = hashmapGet(str_parms->map, (void *)key);
288 if (!value)
289 return -ENOENT;
290
291 *val = (int)strtol(value, &end, 0);
292 if (*value != '\0' && *end == '\0')
293 return 0;
294
295 return -EINVAL;
296}
297
298int str_parms_get_float(struct str_parms *str_parms, const char *key,
299 float *val)
300{
301 float out;
302 char *value;
303 char *end;
304
305 value = hashmapGet(str_parms->map, (void *)key);
306 if (!value)
307 return -ENOENT;
308
309 out = strtof(value, &end);
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800310 if (*value == '\0' || *end != '\0')
311 return -EINVAL;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700312
Mark Salyzyn8e71dde2013-11-22 07:38:46 -0800313 *val = out;
314 return 0;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700315}
316
317static bool combine_strings(void *key, void *value, void *context)
318{
319 char **old_str = context;
320 char *new_str;
321 int ret;
322
323 ret = asprintf(&new_str, "%s%s%s=%s",
324 *old_str ? *old_str : "",
325 *old_str ? ";" : "",
326 (char *)key,
327 (char *)value);
328 if (*old_str)
329 free(*old_str);
330
331 if (ret >= 0) {
332 *old_str = new_str;
333 return true;
334 }
335
336 *old_str = NULL;
337 return false;
338}
339
340char *str_parms_to_str(struct str_parms *str_parms)
341{
342 char *str = NULL;
343
344 if (hashmapSize(str_parms->map) > 0)
345 hashmapForEach(str_parms->map, combine_strings, &str);
346 else
347 str = strdup("");
348 return str;
349}
350
Mark Salyzynba02cd12013-11-22 07:36:45 -0800351static bool dump_entry(void *key, void *value, void *context UNUSED)
Dima Zavin0fad7d02011-03-24 11:11:06 -0700352{
Steve Blockfe71a612012-01-04 19:19:03 +0000353 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700354 return true;
355}
356
357void str_parms_dump(struct str_parms *str_parms)
358{
359 hashmapForEach(str_parms->map, dump_entry, str_parms);
360}
361
362#ifdef TEST_STR_PARMS
363static void test_str_parms_str(const char *str)
364{
365 struct str_parms *str_parms;
366 char *out_str;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700367
368 str_parms = str_parms_create_str(str);
Dima Zavina603e402012-03-12 11:02:00 -0700369 str_parms_add_str(str_parms, "dude", "woah");
Dima Zavinefd75012012-03-14 23:12:40 -0700370 str_parms_add_str(str_parms, "dude", "woah");
371 str_parms_del(str_parms, "dude");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700372 str_parms_dump(str_parms);
373 out_str = str_parms_to_str(str_parms);
374 str_parms_destroy(str_parms);
Steve Blockfe71a612012-01-04 19:19:03 +0000375 ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700376 free(out_str);
377}
378
379int main(void)
380{
381 struct str_parms *str_parms;
382
383 test_str_parms_str("");
384 test_str_parms_str(";");
385 test_str_parms_str("=");
386 test_str_parms_str("=;");
387 test_str_parms_str("=bar");
388 test_str_parms_str("=bar;");
389 test_str_parms_str("foo=");
390 test_str_parms_str("foo=;");
391 test_str_parms_str("foo=bar");
392 test_str_parms_str("foo=bar;");
393 test_str_parms_str("foo=bar;baz");
394 test_str_parms_str("foo=bar;baz=");
395 test_str_parms_str("foo=bar;baz=bat");
396 test_str_parms_str("foo=bar;baz=bat;");
Dima Zavin86bfbe32012-03-14 23:10:06 -0700397 test_str_parms_str("foo=bar;baz=bat;foo=bar");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700398
Jens Gulind3c8d5b2014-03-06 18:15:43 +0100399 // hashmapPut reports errors by setting errno to ENOMEM.
400 // Test that we're not confused by running in an environment where this is already true.
401 errno = ENOMEM;
402 test_str_parms_str("foo=bar;baz=");
403 if (errno != ENOMEM) {
404 abort();
405 }
406 test_str_parms_str("foo=bar;baz=");
407
Dima Zavin0fad7d02011-03-24 11:11:06 -0700408 return 0;
409}
410#endif