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