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/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__
     42 static void *released_pointer;
     43 #define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
     44 #else
     45 #define RELEASE_OWNERSHIP(x)
     46 #endif
     47 
     48 struct str_parms {
     49     Hashmap *map;
     50 };
     51 
     52 
     53 static 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
     62 static 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 
     72 struct 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 
     86 err:
     87     free(str_parms);
     88     return NULL;
     89 }
     90 
     91 struct remove_ctxt {
     92     struct str_parms *str_parms;
     93     const char *key;
     94 };
     95 
     96 static 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 
    119 do_remove:
    120     hashmapRemove(ctxt->str_parms->map, key);
    121     free(key);
    122     free(value);
    123     return should_continue;
    124 }
    125 
    126 void 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 
    135 void 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 
    146 struct 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++;
    196 next_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 
    207 err_strdup:
    208     str_parms_destroy(str_parms);
    209 err_create_str_parms:
    210     return NULL;
    211 }
    212 
    213 int 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 
    252 clean_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 
    261 int 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 
    274 int 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 
    288 int str_parms_has_key(struct str_parms *str_parms, const char *key) {
    289     return hashmapGet(str_parms->map, (void *)key) != NULL;
    290 }
    291 
    292 int 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 
    304 int 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 
    320 int 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 
    339 static 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 
    362 char *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 
    373 static 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 
    379 void str_parms_dump(struct str_parms *str_parms)
    380 {
    381     hashmapForEach(str_parms->map, dump_entry, str_parms);
    382 }
    383