123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <cutils/str_parms.h>
- #define LOG_TAG "str_params"
- //#define LOG_NDEBUG 0
- #define _GNU_SOURCE 1
- #include <errno.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <cutils/hashmap.h>
- #include <cutils/memory.h>
- #include <log/log.h>
- /* When an object is allocated but not freed in a function,
- * because its ownership is released to other object like a hashmap,
- * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
- * false warnings about potential memory leak.
- * For now, a "temporary" assignment to global variables
- * is enough to confuse the clang static analyzer.
- */
- #ifdef __clang_analyzer__
- static void *released_pointer;
- #define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
- #else
- #define RELEASE_OWNERSHIP(x)
- #endif
- struct str_parms {
- Hashmap *map;
- };
- static bool str_eq(void *key_a, void *key_b)
- {
- return !strcmp((const char *)key_a, (const char *)key_b);
- }
- /* use djb hash unless we find it inadequate */
- #ifdef __clang__
- __attribute__((no_sanitize("integer")))
- #endif
- static int str_hash_fn(void *str)
- {
- uint32_t hash = 5381;
- for (char* p = static_cast<char*>(str); p && *p; p++)
- hash = ((hash << 5) + hash) + *p;
- return (int)hash;
- }
- struct str_parms *str_parms_create(void)
- {
- str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
- if (!s) return NULL;
- s->map = hashmapCreate(5, str_hash_fn, str_eq);
- if (!s->map) {
- free(s);
- return NULL;
- }
- return s;
- }
- struct remove_ctxt {
- struct str_parms *str_parms;
- const char *key;
- };
- static bool remove_pair(void *key, void *value, void *context)
- {
- remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
- bool should_continue;
- /*
- * - if key is not supplied, then we are removing all entries,
- * so remove key and continue (i.e. return true)
- * - if key is supplied and matches, then remove it and don't
- * continue (return false). Otherwise, return true and keep searching
- * for key.
- *
- */
- if (!ctxt->key) {
- should_continue = true;
- goto do_remove;
- } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
- should_continue = false;
- goto do_remove;
- }
- return true;
- do_remove:
- hashmapRemove(ctxt->str_parms->map, key);
- free(key);
- free(value);
- return should_continue;
- }
- void str_parms_del(struct str_parms *str_parms, const char *key)
- {
- struct remove_ctxt ctxt = {
- .str_parms = str_parms,
- .key = key,
- };
- hashmapForEach(str_parms->map, remove_pair, &ctxt);
- }
- void str_parms_destroy(struct str_parms *str_parms)
- {
- struct remove_ctxt ctxt = {
- .str_parms = str_parms,
- };
- hashmapForEach(str_parms->map, remove_pair, &ctxt);
- hashmapFree(str_parms->map);
- free(str_parms);
- }
- struct str_parms *str_parms_create_str(const char *_string)
- {
- struct str_parms *str_parms;
- char *str;
- char *kvpair;
- char *tmpstr;
- int items = 0;
- str_parms = str_parms_create();
- if (!str_parms)
- goto err_create_str_parms;
- str = strdup(_string);
- if (!str)
- goto err_strdup;
- ALOGV("%s: source string == '%s'\n", __func__, _string);
- kvpair = strtok_r(str, ";", &tmpstr);
- while (kvpair && *kvpair) {
- char *eq = strchr(kvpair, '='); /* would love strchrnul */
- char *value;
- char *key;
- void *old_val;
- if (eq == kvpair)
- goto next_pair;
- if (eq) {
- key = strndup(kvpair, eq - kvpair);
- if (*(++eq))
- value = strdup(eq);
- else
- value = strdup("");
- } else {
- key = strdup(kvpair);
- value = strdup("");
- }
- /* if we replaced a value, free it */
- old_val = hashmapPut(str_parms->map, key, value);
- RELEASE_OWNERSHIP(value);
- if (old_val) {
- free(old_val);
- free(key);
- } else {
- RELEASE_OWNERSHIP(key);
- }
- items++;
- next_pair:
- kvpair = strtok_r(NULL, ";", &tmpstr);
- }
- if (!items)
- ALOGV("%s: no items found in string\n", __func__);
- free(str);
- return str_parms;
- err_strdup:
- str_parms_destroy(str_parms);
- err_create_str_parms:
- return NULL;
- }
- int str_parms_add_str(struct str_parms *str_parms, const char *key,
- const char *value)
- {
- void *tmp_key = NULL;
- void *tmp_val = NULL;
- void *old_val = NULL;
- // strdup and hashmapPut both set errno on failure.
- // Set errno to 0 so we can recognize whether anything went wrong.
- int saved_errno = errno;
- errno = 0;
- tmp_key = strdup(key);
- if (tmp_key == NULL) {
- goto clean_up;
- }
- tmp_val = strdup(value);
- if (tmp_val == NULL) {
- goto clean_up;
- }
- old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
- if (old_val == NULL) {
- // Did hashmapPut fail?
- if (errno == ENOMEM) {
- goto clean_up;
- }
- // For new keys, hashmap takes ownership of tmp_key and tmp_val.
- RELEASE_OWNERSHIP(tmp_key);
- RELEASE_OWNERSHIP(tmp_val);
- tmp_key = tmp_val = NULL;
- } else {
- // For existing keys, hashmap takes ownership of tmp_val.
- // (It also gives up ownership of old_val.)
- RELEASE_OWNERSHIP(tmp_val);
- tmp_val = NULL;
- }
- clean_up:
- free(tmp_key);
- free(tmp_val);
- free(old_val);
- int result = -errno;
- errno = saved_errno;
- return result;
- }
- int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
- {
- char val_str[12];
- int ret;
- ret = snprintf(val_str, sizeof(val_str), "%d", value);
- if (ret < 0)
- return -EINVAL;
- ret = str_parms_add_str(str_parms, key, val_str);
- return ret;
- }
- int str_parms_add_float(struct str_parms *str_parms, const char *key,
- float value)
- {
- char val_str[23];
- int ret;
- ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
- if (ret < 0)
- return -EINVAL;
- ret = str_parms_add_str(str_parms, key, val_str);
- return ret;
- }
- int str_parms_has_key(struct str_parms *str_parms, const char *key) {
- return hashmapGet(str_parms->map, (void *)key) != NULL;
- }
- int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
- int len)
- {
- // TODO: hashmapGet should take a const* key.
- char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
- if (value)
- return strlcpy(val, value, len);
- return -ENOENT;
- }
- int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
- {
- char *end;
- // TODO: hashmapGet should take a const* key.
- char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
- if (!value)
- return -ENOENT;
- *val = (int)strtol(value, &end, 0);
- if (*value != '\0' && *end == '\0')
- return 0;
- return -EINVAL;
- }
- int str_parms_get_float(struct str_parms *str_parms, const char *key,
- float *val)
- {
- float out;
- char *end;
- // TODO: hashmapGet should take a const* key.
- char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
- if (!value)
- return -ENOENT;
- out = strtof(value, &end);
- if (*value == '\0' || *end != '\0')
- return -EINVAL;
- *val = out;
- return 0;
- }
- static bool combine_strings(void *key, void *value, void *context)
- {
- char** old_str = static_cast<char**>(context);
- char *new_str;
- int ret;
- ret = asprintf(&new_str, "%s%s%s=%s",
- *old_str ? *old_str : "",
- *old_str ? ";" : "",
- (char *)key,
- (char *)value);
- if (*old_str)
- free(*old_str);
- if (ret >= 0) {
- *old_str = new_str;
- return true;
- }
- *old_str = NULL;
- return false;
- }
- char *str_parms_to_str(struct str_parms *str_parms)
- {
- char *str = NULL;
- hashmapForEach(str_parms->map, combine_strings, &str);
- return (str != NULL) ? str : strdup("");
- }
- static bool dump_entry(void* key, void* value, void* /*context*/) {
- ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
- return true;
- }
- void str_parms_dump(struct str_parms *str_parms)
- {
- hashmapForEach(str_parms->map, dump_entry, str_parms);
- }
|