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/memory.h>
29#include <cutils/str_parms.h>
30#include <log/log.h>
31
32#define UNUSED __attribute__((unused))
33
34/* When an object is allocated but not freed in a function,
35 * because its ownership is released to other object like a hashmap,
36 * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
37 * false warnings about potential memory leak.
38 * For now, a "temporary" assignment to global variables
39 * is enough to confuse the clang static analyzer.
40 */
41#ifdef __clang_analyzer__
42static void *released_pointer;
43#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
44#else
45#define RELEASE_OWNERSHIP(x)
46#endif
47
48struct str_parms {
49    Hashmap *map;
50};
51
52
53static bool str_eq(void *key_a, void *key_b)
54{
55    return !strcmp((const char *)key_a, (const char *)key_b);
56}
57
58/* use djb hash unless we find it inadequate */
59#ifdef __clang__
60__attribute__((no_sanitize("integer")))
61#endif
62static int str_hash_fn(void *str)
63{
64    uint32_t hash = 5381;
65    char *p;
66
67    for (p = str; p && *p; p++)
68        hash = ((hash << 5) + hash) + *p;
69    return (int)hash;
70}
71
72struct str_parms *str_parms_create(void)
73{
74    struct str_parms *str_parms;
75
76    str_parms = calloc(1, sizeof(struct str_parms));
77    if (!str_parms)
78        return NULL;
79
80    str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
81    if (!str_parms->map)
82        goto err;
83
84    return str_parms;
85
86err:
87    free(str_parms);
88    return NULL;
89}
90
91struct remove_ctxt {
92    struct str_parms *str_parms;
93    const char *key;
94};
95
96static bool remove_pair(void *key, void *value, void *context)
97{
98    struct remove_ctxt *ctxt = context;
99    bool should_continue;
100
101    /*
102     * - if key is not supplied, then we are removing all entries,
103     *   so remove key and continue (i.e. return true)
104     * - if key is supplied and matches, then remove it and don't
105     *   continue (return false). Otherwise, return true and keep searching
106     *   for key.
107     *
108     */
109    if (!ctxt->key) {
110        should_continue = true;
111        goto do_remove;
112    } else if (!strcmp(ctxt->key, key)) {
113        should_continue = false;
114        goto do_remove;
115    }
116
117    return true;
118
119do_remove:
120    hashmapRemove(ctxt->str_parms->map, key);
121    free(key);
122    free(value);
123    return should_continue;
124}
125
126void str_parms_del(struct str_parms *str_parms, const char *key)
127{
128    struct remove_ctxt ctxt = {
129        .str_parms = str_parms,
130        .key = key,
131    };
132    hashmapForEach(str_parms->map, remove_pair, &ctxt);
133}
134
135void str_parms_destroy(struct str_parms *str_parms)
136{
137    struct remove_ctxt ctxt = {
138        .str_parms = str_parms,
139    };
140
141    hashmapForEach(str_parms->map, remove_pair, &ctxt);
142    hashmapFree(str_parms->map);
143    free(str_parms);
144}
145
146struct str_parms *str_parms_create_str(const char *_string)
147{
148    struct str_parms *str_parms;
149    char *str;
150    char *kvpair;
151    char *tmpstr;
152    int items = 0;
153
154    str_parms = str_parms_create();
155    if (!str_parms)
156        goto err_create_str_parms;
157
158    str = strdup(_string);
159    if (!str)
160        goto err_strdup;
161
162    ALOGV("%s: source string == '%s'\n", __func__, _string);
163
164    kvpair = strtok_r(str, ";", &tmpstr);
165    while (kvpair && *kvpair) {
166        char *eq = strchr(kvpair, '='); /* would love strchrnul */
167        char *value;
168        char *key;
169        void *old_val;
170
171        if (eq == kvpair)
172            goto next_pair;
173
174        if (eq) {
175            key = strndup(kvpair, eq - kvpair);
176            if (*(++eq))
177                value = strdup(eq);
178            else
179                value = strdup("");
180        } else {
181            key = strdup(kvpair);
182            value = strdup("");
183        }
184
185        /* if we replaced a value, free it */
186        old_val = hashmapPut(str_parms->map, key, value);
187        RELEASE_OWNERSHIP(value);
188        if (old_val) {
189            free(old_val);
190            free(key);
191        } else {
192            RELEASE_OWNERSHIP(key);
193        }
194
195        items++;
196next_pair:
197        kvpair = strtok_r(NULL, ";", &tmpstr);
198    }
199
200    if (!items)
201        ALOGV("%s: no items found in string\n", __func__);
202
203    free(str);
204
205    return str_parms;
206
207err_strdup:
208    str_parms_destroy(str_parms);
209err_create_str_parms:
210    return NULL;
211}
212
213int str_parms_add_str(struct str_parms *str_parms, const char *key,
214                      const char *value)
215{
216    void *tmp_key = NULL;
217    void *tmp_val = NULL;
218    void *old_val = NULL;
219
220    // strdup and hashmapPut both set errno on failure.
221    // Set errno to 0 so we can recognize whether anything went wrong.
222    int saved_errno = errno;
223    errno = 0;
224
225    tmp_key = strdup(key);
226    if (tmp_key == NULL) {
227        goto clean_up;
228    }
229
230    tmp_val = strdup(value);
231    if (tmp_val == NULL) {
232        goto clean_up;
233    }
234
235    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
236    if (old_val == NULL) {
237        // Did hashmapPut fail?
238        if (errno == ENOMEM) {
239            goto clean_up;
240        }
241        // For new keys, hashmap takes ownership of tmp_key and tmp_val.
242        RELEASE_OWNERSHIP(tmp_key);
243        RELEASE_OWNERSHIP(tmp_val);
244        tmp_key = tmp_val = NULL;
245    } else {
246        // For existing keys, hashmap takes ownership of tmp_val.
247        // (It also gives up ownership of old_val.)
248        RELEASE_OWNERSHIP(tmp_val);
249        tmp_val = NULL;
250    }
251
252clean_up:
253    free(tmp_key);
254    free(tmp_val);
255    free(old_val);
256    int result = -errno;
257    errno = saved_errno;
258    return result;
259}
260
261int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
262{
263    char val_str[12];
264    int ret;
265
266    ret = snprintf(val_str, sizeof(val_str), "%d", value);
267    if (ret < 0)
268        return -EINVAL;
269
270    ret = str_parms_add_str(str_parms, key, val_str);
271    return ret;
272}
273
274int str_parms_add_float(struct str_parms *str_parms, const char *key,
275                        float value)
276{
277    char val_str[23];
278    int ret;
279
280    ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
281    if (ret < 0)
282        return -EINVAL;
283
284    ret = str_parms_add_str(str_parms, key, val_str);
285    return ret;
286}
287
288int str_parms_has_key(struct str_parms *str_parms, const char *key) {
289    return hashmapGet(str_parms->map, (void *)key) != NULL;
290}
291
292int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
293                      int len)
294{
295    char *value;
296
297    value = hashmapGet(str_parms->map, (void *)key);
298    if (value)
299        return strlcpy(val, value, len);
300
301    return -ENOENT;
302}
303
304int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
305{
306    char *value;
307    char *end;
308
309    value = hashmapGet(str_parms->map, (void *)key);
310    if (!value)
311        return -ENOENT;
312
313    *val = (int)strtol(value, &end, 0);
314    if (*value != '\0' && *end == '\0')
315        return 0;
316
317    return -EINVAL;
318}
319
320int str_parms_get_float(struct str_parms *str_parms, const char *key,
321                        float *val)
322{
323    float out;
324    char *value;
325    char *end;
326
327    value = hashmapGet(str_parms->map, (void *)key);
328    if (!value)
329        return -ENOENT;
330
331    out = strtof(value, &end);
332    if (*value == '\0' || *end != '\0')
333        return -EINVAL;
334
335    *val = out;
336    return 0;
337}
338
339static bool combine_strings(void *key, void *value, void *context)
340{
341    char **old_str = context;
342    char *new_str;
343    int ret;
344
345    ret = asprintf(&new_str, "%s%s%s=%s",
346                   *old_str ? *old_str : "",
347                   *old_str ? ";" : "",
348                   (char *)key,
349                   (char *)value);
350    if (*old_str)
351        free(*old_str);
352
353    if (ret >= 0) {
354        *old_str = new_str;
355        return true;
356    }
357
358    *old_str = NULL;
359    return false;
360}
361
362char *str_parms_to_str(struct str_parms *str_parms)
363{
364    char *str = NULL;
365
366    if (hashmapSize(str_parms->map) > 0)
367        hashmapForEach(str_parms->map, combine_strings, &str);
368    else
369        str = strdup("");
370    return str;
371}
372
373static bool dump_entry(void *key, void *value, void *context UNUSED)
374{
375    ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
376    return true;
377}
378
379void str_parms_dump(struct str_parms *str_parms)
380{
381    hashmapForEach(str_parms->map, dump_entry, str_parms);
382}
383