Home | History | Annotate | Download | only in libcutils
      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 
     33 struct str_parms {
     34     Hashmap *map;
     35 };
     36 
     37 
     38 static 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 */
     44 static 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 
     54 struct 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 
     68 err:
     69     free(str_parms);
     70     return NULL;
     71 }
     72 
     73 struct remove_ctxt {
     74     struct str_parms *str_parms;
     75     const char *key;
     76 };
     77 
     78 static 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 
    101 do_remove:
    102     hashmapRemove(ctxt->str_parms->map, key);
    103     free(key);
    104     free(value);
    105     return should_continue;
    106 }
    107 
    108 void 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 
    117 void 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 
    128 struct 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++;
    175 next_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 
    186 err_strdup:
    187     str_parms_destroy(str_parms);
    188 err_create_str_parms:
    189     return NULL;
    190 }
    191 
    192 int 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 
    214 int 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 
    227 int 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 
    241 int 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 
    253 int 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 
    269 int 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 
    287 static 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 
    310 char *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 
    321 static 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 
    327 void 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
    333 static 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 
    350 int 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