blob: 9e1d2dcf90ce0c96ae986d3432d87977bd8016c5 [file] [log] [blame]
Dima Zavin0fad7d02011-03-24 11:11:06 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
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
33struct str_parms {
34 Hashmap *map;
35};
36
37
38static bool str_eq(void *key_a, void *key_b)
39{
40 return !strcmp((const char *)key_a, (const char *)key_b);
41}
42
43/* use djb hash unless we find it inadequate */
44static int str_hash_fn(void *str)
45{
46 uint32_t hash = 5381;
47 char *p;
48
49 for (p = str; p && *p; p++)
50 hash = ((hash << 5) + hash) + *p;
51 return (int)hash;
52}
53
54struct str_parms *str_parms_create(void)
55{
56 struct str_parms *str_parms;
57
58 str_parms = calloc(1, sizeof(struct str_parms));
59 if (!str_parms)
60 return NULL;
61
62 str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
63 if (!str_parms->map)
64 goto err;
65
66 return str_parms;
67
68err:
69 free(str_parms);
70 return NULL;
71}
72
Dima Zavinefd75012012-03-14 23:12:40 -070073struct remove_ctxt {
74 struct str_parms *str_parms;
75 const char *key;
76};
77
Dima Zavin0fad7d02011-03-24 11:11:06 -070078static bool remove_pair(void *key, void *value, void *context)
79{
Dima Zavinefd75012012-03-14 23:12:40 -070080 struct remove_ctxt *ctxt = context;
81 bool should_continue;
Dima Zavin0fad7d02011-03-24 11:11:06 -070082
Dima Zavinefd75012012-03-14 23:12:40 -070083 /*
84 * - if key is not supplied, then we are removing all entries,
85 * so remove key and continue (i.e. return true)
86 * - if key is supplied and matches, then remove it and don't
87 * continue (return false). Otherwise, return true and keep searching
88 * for key.
89 *
90 */
91 if (!ctxt->key) {
92 should_continue = true;
93 goto do_remove;
94 } else if (!strcmp(ctxt->key, key)) {
95 should_continue = false;
96 goto do_remove;
97 }
98
99 return true;
100
101do_remove:
102 hashmapRemove(ctxt->str_parms->map, key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700103 free(key);
104 free(value);
Dima Zavinefd75012012-03-14 23:12:40 -0700105 return should_continue;
106}
107
108void str_parms_del(struct str_parms *str_parms, const char *key)
109{
110 struct remove_ctxt ctxt = {
111 .str_parms = str_parms,
112 .key = key,
113 };
114 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700115}
116
117void str_parms_destroy(struct str_parms *str_parms)
118{
Dima Zavinefd75012012-03-14 23:12:40 -0700119 struct remove_ctxt ctxt = {
120 .str_parms = str_parms,
121 };
122
123 hashmapForEach(str_parms->map, remove_pair, &ctxt);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700124 hashmapFree(str_parms->map);
125 free(str_parms);
126}
127
128struct str_parms *str_parms_create_str(const char *_string)
129{
130 struct str_parms *str_parms;
131 char *str;
132 char *kvpair;
133 char *tmpstr;
134 int items = 0;
135
136 str_parms = str_parms_create();
137 if (!str_parms)
138 goto err_create_str_parms;
139
140 str = strdup(_string);
141 if (!str)
142 goto err_strdup;
143
Steve Block69f4cd72011-10-20 11:54:09 +0100144 ALOGV("%s: source string == '%s'\n", __func__, _string);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700145
146 kvpair = strtok_r(str, ";", &tmpstr);
147 while (kvpair && *kvpair) {
148 char *eq = strchr(kvpair, '='); /* would love strchrnul */
149 char *value;
150 char *key;
151 void *old_val;
152
153 if (eq == kvpair)
154 goto next_pair;
155
156 if (eq) {
157 key = strndup(kvpair, eq - kvpair);
158 if (*(++eq))
159 value = strdup(eq);
160 else
161 value = strdup("");
162 } else {
163 key = strdup(kvpair);
164 value = strdup("");
165 }
166
167 /* if we replaced a value, free it */
168 old_val = hashmapPut(str_parms->map, key, value);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700169 if (old_val) {
Dima Zavin0fad7d02011-03-24 11:11:06 -0700170 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700171 free(key);
172 }
Dima Zavin0fad7d02011-03-24 11:11:06 -0700173
174 items++;
175next_pair:
176 kvpair = strtok_r(NULL, ";", &tmpstr);
177 }
178
179 if (!items)
Steve Block69f4cd72011-10-20 11:54:09 +0100180 ALOGV("%s: no items found in string\n", __func__);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700181
182 free(str);
183
184 return str_parms;
185
186err_strdup:
187 str_parms_destroy(str_parms);
188err_create_str_parms:
189 return NULL;
190}
191
Dima Zavin0fad7d02011-03-24 11:11:06 -0700192int str_parms_add_str(struct str_parms *str_parms, const char *key,
193 const char *value)
194{
195 void *old_val;
Dima Zavin70b93032012-03-12 11:01:16 -0700196 void *tmp_key;
197 void *tmp_val;
Dima Zavin0fad7d02011-03-24 11:11:06 -0700198
Dima Zavin70b93032012-03-12 11:01:16 -0700199 tmp_key = strdup(key);
200 tmp_val = strdup(value);
201 old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700202
203 if (old_val) {
204 free(old_val);
Dima Zavin86bfbe32012-03-14 23:10:06 -0700205 free(tmp_key);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700206 } else if (errno == ENOMEM) {
Dima Zavin70b93032012-03-12 11:01:16 -0700207 free(tmp_key);
208 free(tmp_val);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700209 return -ENOMEM;
210 }
211 return 0;
212}
213
214int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
215{
216 char val_str[12];
217 int ret;
218
219 ret = snprintf(val_str, sizeof(val_str), "%d", value);
220 if (ret < 0)
221 return -EINVAL;
222
223 ret = str_parms_add_str(str_parms, key, val_str);
224 return ret;
225}
226
227int str_parms_add_float(struct str_parms *str_parms, const char *key,
228 float value)
229{
230 char val_str[23];
231 int ret;
232
233 ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
234 if (ret < 0)
235 return -EINVAL;
236
237 ret = str_parms_add_str(str_parms, key, val_str);
238 return ret;
239}
240
241int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
242 int len)
243{
244 char *value;
245
246 value = hashmapGet(str_parms->map, (void *)key);
247 if (value)
248 return strlcpy(val, value, len);
249
250 return -ENOENT;
251}
252
253int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
254{
255 char *value;
256 char *end;
257
258 value = hashmapGet(str_parms->map, (void *)key);
259 if (!value)
260 return -ENOENT;
261
262 *val = (int)strtol(value, &end, 0);
263 if (*value != '\0' && *end == '\0')
264 return 0;
265
266 return -EINVAL;
267}
268
269int str_parms_get_float(struct str_parms *str_parms, const char *key,
270 float *val)
271{
272 float out;
273 char *value;
274 char *end;
275
276 value = hashmapGet(str_parms->map, (void *)key);
277 if (!value)
278 return -ENOENT;
279
280 out = strtof(value, &end);
281 if (*value != '\0' && *end == '\0')
282 return 0;
283
284 return -EINVAL;
285}
286
287static bool combine_strings(void *key, void *value, void *context)
288{
289 char **old_str = context;
290 char *new_str;
291 int ret;
292
293 ret = asprintf(&new_str, "%s%s%s=%s",
294 *old_str ? *old_str : "",
295 *old_str ? ";" : "",
296 (char *)key,
297 (char *)value);
298 if (*old_str)
299 free(*old_str);
300
301 if (ret >= 0) {
302 *old_str = new_str;
303 return true;
304 }
305
306 *old_str = NULL;
307 return false;
308}
309
310char *str_parms_to_str(struct str_parms *str_parms)
311{
312 char *str = NULL;
313
314 if (hashmapSize(str_parms->map) > 0)
315 hashmapForEach(str_parms->map, combine_strings, &str);
316 else
317 str = strdup("");
318 return str;
319}
320
321static bool dump_entry(void *key, void *value, void *context)
322{
Steve Blockfe71a612012-01-04 19:19:03 +0000323 ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700324 return true;
325}
326
327void str_parms_dump(struct str_parms *str_parms)
328{
329 hashmapForEach(str_parms->map, dump_entry, str_parms);
330}
331
332#ifdef TEST_STR_PARMS
333static void test_str_parms_str(const char *str)
334{
335 struct str_parms *str_parms;
336 char *out_str;
337 int ret;
338
339 str_parms = str_parms_create_str(str);
Dima Zavina603e402012-03-12 11:02:00 -0700340 str_parms_add_str(str_parms, "dude", "woah");
Dima Zavinefd75012012-03-14 23:12:40 -0700341 str_parms_add_str(str_parms, "dude", "woah");
342 str_parms_del(str_parms, "dude");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700343 str_parms_dump(str_parms);
344 out_str = str_parms_to_str(str_parms);
345 str_parms_destroy(str_parms);
Steve Blockfe71a612012-01-04 19:19:03 +0000346 ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
Dima Zavin0fad7d02011-03-24 11:11:06 -0700347 free(out_str);
348}
349
350int main(void)
351{
352 struct str_parms *str_parms;
353
354 test_str_parms_str("");
355 test_str_parms_str(";");
356 test_str_parms_str("=");
357 test_str_parms_str("=;");
358 test_str_parms_str("=bar");
359 test_str_parms_str("=bar;");
360 test_str_parms_str("foo=");
361 test_str_parms_str("foo=;");
362 test_str_parms_str("foo=bar");
363 test_str_parms_str("foo=bar;");
364 test_str_parms_str("foo=bar;baz");
365 test_str_parms_str("foo=bar;baz=");
366 test_str_parms_str("foo=bar;baz=bat");
367 test_str_parms_str("foo=bar;baz=bat;");
Dima Zavin86bfbe32012-03-14 23:10:06 -0700368 test_str_parms_str("foo=bar;baz=bat;foo=bar");
Dima Zavin0fad7d02011-03-24 11:11:06 -0700369
370 return 0;
371}
372#endif