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