str_parms.c revision efd7501d535fb165792013763027cf50a5b37e08
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
73struct remove_ctxt {
74    struct str_parms *str_parms;
75    const char *key;
76};
77
78static bool remove_pair(void *key, void *value, void *context)
79{
80    struct remove_ctxt *ctxt = context;
81    bool should_continue;
82
83    /*
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);
103    free(key);
104    free(value);
105    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);
115}
116
117void str_parms_destroy(struct str_parms *str_parms)
118{
119    struct remove_ctxt ctxt = {
120        .str_parms = str_parms,
121    };
122
123    hashmapForEach(str_parms->map, remove_pair, &ctxt);
124    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
144    ALOGV("%s: source string == '%s'\n", __func__, _string);
145
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);
169        if (old_val) {
170            free(old_val);
171            free(key);
172        }
173
174        items++;
175next_pair:
176        kvpair = strtok_r(NULL, ";", &tmpstr);
177    }
178
179    if (!items)
180        ALOGV("%s: no items found in string\n", __func__);
181
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
192int str_parms_add_str(struct str_parms *str_parms, const char *key,
193                      const char *value)
194{
195    void *old_val;
196    void *tmp_key;
197    void *tmp_val;
198
199    tmp_key = strdup(key);
200    tmp_val = strdup(value);
201    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
202
203    if (old_val) {
204        free(old_val);
205        free(tmp_key);
206    } else if (errno == ENOMEM) {
207        free(tmp_key);
208        free(tmp_val);
209        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{
323    ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
324    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);
340    str_parms_add_str(str_parms, "dude", "woah");
341    str_parms_add_str(str_parms, "dude", "woah");
342    str_parms_del(str_parms, "dude");
343    str_parms_dump(str_parms);
344    out_str = str_parms_to_str(str_parms);
345    str_parms_destroy(str_parms);
346    ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
347    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;");
368    test_str_parms_str("foo=bar;baz=bat;foo=bar");
369
370    return 0;
371}
372#endif
373