str_parms.c revision 86bfbe3cae1f5e480ba9756ec7404fa4d5a7f2c9
1/*
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
73static bool remove_pair(void *key, void *value, void *context)
74{
75    struct str_parms *str_parms = context;
76
77    hashmapRemove(str_parms->map, key);
78    free(key);
79    free(value);
80    return true;
81}
82
83void str_parms_destroy(struct str_parms *str_parms)
84{
85    hashmapForEach(str_parms->map, remove_pair, str_parms);
86    hashmapFree(str_parms->map);
87    free(str_parms);
88}
89
90struct str_parms *str_parms_create_str(const char *_string)
91{
92    struct str_parms *str_parms;
93    char *str;
94    char *kvpair;
95    char *tmpstr;
96    int items = 0;
97
98    str_parms = str_parms_create();
99    if (!str_parms)
100        goto err_create_str_parms;
101
102    str = strdup(_string);
103    if (!str)
104        goto err_strdup;
105
106    ALOGV("%s: source string == '%s'\n", __func__, _string);
107
108    kvpair = strtok_r(str, ";", &tmpstr);
109    while (kvpair && *kvpair) {
110        char *eq = strchr(kvpair, '='); /* would love strchrnul */
111        char *value;
112        char *key;
113        void *old_val;
114
115        if (eq == kvpair)
116            goto next_pair;
117
118        if (eq) {
119            key = strndup(kvpair, eq - kvpair);
120            if (*(++eq))
121                value = strdup(eq);
122            else
123                value = strdup("");
124        } else {
125            key = strdup(kvpair);
126            value = strdup("");
127        }
128
129        /* if we replaced a value, free it */
130        old_val = hashmapPut(str_parms->map, key, value);
131        if (old_val) {
132            free(old_val);
133            free(key);
134        }
135
136        items++;
137next_pair:
138        kvpair = strtok_r(NULL, ";", &tmpstr);
139    }
140
141    if (!items)
142        ALOGV("%s: no items found in string\n", __func__);
143
144    free(str);
145
146    return str_parms;
147
148err_strdup:
149    str_parms_destroy(str_parms);
150err_create_str_parms:
151    return NULL;
152}
153
154void str_parms_del(struct str_parms *str_parms, const char *key)
155{
156    hashmapRemove(str_parms->map, (void *)key);
157}
158
159int str_parms_add_str(struct str_parms *str_parms, const char *key,
160                      const char *value)
161{
162    void *old_val;
163    void *tmp_key;
164    void *tmp_val;
165
166    tmp_key = strdup(key);
167    tmp_val = strdup(value);
168    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
169
170    if (old_val) {
171        free(old_val);
172        free(tmp_key);
173    } else if (errno == ENOMEM) {
174        free(tmp_key);
175        free(tmp_val);
176        return -ENOMEM;
177    }
178    return 0;
179}
180
181int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
182{
183    char val_str[12];
184    int ret;
185
186    ret = snprintf(val_str, sizeof(val_str), "%d", value);
187    if (ret < 0)
188        return -EINVAL;
189
190    ret = str_parms_add_str(str_parms, key, val_str);
191    return ret;
192}
193
194int str_parms_add_float(struct str_parms *str_parms, const char *key,
195                        float value)
196{
197    char val_str[23];
198    int ret;
199
200    ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
201    if (ret < 0)
202        return -EINVAL;
203
204    ret = str_parms_add_str(str_parms, key, val_str);
205    return ret;
206}
207
208int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
209                      int len)
210{
211    char *value;
212
213    value = hashmapGet(str_parms->map, (void *)key);
214    if (value)
215        return strlcpy(val, value, len);
216
217    return -ENOENT;
218}
219
220int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
221{
222    char *value;
223    char *end;
224
225    value = hashmapGet(str_parms->map, (void *)key);
226    if (!value)
227        return -ENOENT;
228
229    *val = (int)strtol(value, &end, 0);
230    if (*value != '\0' && *end == '\0')
231        return 0;
232
233    return -EINVAL;
234}
235
236int str_parms_get_float(struct str_parms *str_parms, const char *key,
237                        float *val)
238{
239    float out;
240    char *value;
241    char *end;
242
243    value = hashmapGet(str_parms->map, (void *)key);
244    if (!value)
245        return -ENOENT;
246
247    out = strtof(value, &end);
248    if (*value != '\0' && *end == '\0')
249        return 0;
250
251    return -EINVAL;
252}
253
254static bool combine_strings(void *key, void *value, void *context)
255{
256    char **old_str = context;
257    char *new_str;
258    int ret;
259
260    ret = asprintf(&new_str, "%s%s%s=%s",
261                   *old_str ? *old_str : "",
262                   *old_str ? ";" : "",
263                   (char *)key,
264                   (char *)value);
265    if (*old_str)
266        free(*old_str);
267
268    if (ret >= 0) {
269        *old_str = new_str;
270        return true;
271    }
272
273    *old_str = NULL;
274    return false;
275}
276
277char *str_parms_to_str(struct str_parms *str_parms)
278{
279    char *str = NULL;
280
281    if (hashmapSize(str_parms->map) > 0)
282        hashmapForEach(str_parms->map, combine_strings, &str);
283    else
284        str = strdup("");
285    return str;
286}
287
288static bool dump_entry(void *key, void *value, void *context)
289{
290    ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
291    return true;
292}
293
294void str_parms_dump(struct str_parms *str_parms)
295{
296    hashmapForEach(str_parms->map, dump_entry, str_parms);
297}
298
299#ifdef TEST_STR_PARMS
300static void test_str_parms_str(const char *str)
301{
302    struct str_parms *str_parms;
303    char *out_str;
304    int ret;
305
306    str_parms = str_parms_create_str(str);
307    str_parms_add_str(str_parms, "dude", "woah");
308    str_parms_dump(str_parms);
309    out_str = str_parms_to_str(str_parms);
310    str_parms_destroy(str_parms);
311    ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
312    free(out_str);
313}
314
315int main(void)
316{
317    struct str_parms *str_parms;
318
319    test_str_parms_str("");
320    test_str_parms_str(";");
321    test_str_parms_str("=");
322    test_str_parms_str("=;");
323    test_str_parms_str("=bar");
324    test_str_parms_str("=bar;");
325    test_str_parms_str("foo=");
326    test_str_parms_str("foo=;");
327    test_str_parms_str("foo=bar");
328    test_str_parms_str("foo=bar;");
329    test_str_parms_str("foo=bar;baz");
330    test_str_parms_str("foo=bar;baz=");
331    test_str_parms_str("foo=bar;baz=bat");
332    test_str_parms_str("foo=bar;baz=bat;");
333    test_str_parms_str("foo=bar;baz=bat;foo=bar");
334
335    return 0;
336}
337#endif
338