platform_system_core/libcutils/str_parms.c
Dima Zavin efd7501d53 cutils: str_parms: when deleting entry, need to also remove key
When removing one entry, we can't just remove it from the map since
key will not be freed. So, instead, search through the map for the key
and free it once it is found.

Change-Id: Id745c166a4acc5ea25f6d6cdbfe4f8fbc21cbbb3
Reported-by: Xin Qian <xqian@marvell.com>
Signed-off-by: Dima Zavin <dima@android.com>
2012-03-14 23:17:11 -07:00

372 lines
8.3 KiB
C

/*
* 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.
*/
#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/log.h>
#include <cutils/memory.h>
#include <cutils/str_parms.h>
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 */
static int str_hash_fn(void *str)
{
uint32_t hash = 5381;
char *p;
for (p = str; p && *p; p++)
hash = ((hash << 5) + hash) + *p;
return (int)hash;
}
struct str_parms *str_parms_create(void)
{
struct str_parms *str_parms;
str_parms = calloc(1, sizeof(struct str_parms));
if (!str_parms)
return NULL;
str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
if (!str_parms->map)
goto err;
return str_parms;
err:
free(str_parms);
return NULL;
}
struct remove_ctxt {
struct str_parms *str_parms;
const char *key;
};
static bool remove_pair(void *key, void *value, void *context)
{
struct remove_ctxt *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, 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);
if (old_val) {
free(old_val);
free(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 *old_val;
void *tmp_key;
void *tmp_val;
tmp_key = strdup(key);
tmp_val = strdup(value);
old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
if (old_val) {
free(old_val);
free(tmp_key);
} else if (errno == ENOMEM) {
free(tmp_key);
free(tmp_val);
return -ENOMEM;
}
return 0;
}
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_get_str(struct str_parms *str_parms, const char *key, char *val,
int len)
{
char *value;
value = 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 *value;
char *end;
value = 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 *value;
char *end;
value = hashmapGet(str_parms->map, (void *)key);
if (!value)
return -ENOENT;
out = strtof(value, &end);
if (*value != '\0' && *end == '\0')
return 0;
return -EINVAL;
}
static bool combine_strings(void *key, void *value, void *context)
{
char **old_str = 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;
if (hashmapSize(str_parms->map) > 0)
hashmapForEach(str_parms->map, combine_strings, &str);
else
str = strdup("");
return str;
}
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);
}
#ifdef TEST_STR_PARMS
static void test_str_parms_str(const char *str)
{
struct str_parms *str_parms;
char *out_str;
int ret;
str_parms = str_parms_create_str(str);
str_parms_add_str(str_parms, "dude", "woah");
str_parms_add_str(str_parms, "dude", "woah");
str_parms_del(str_parms, "dude");
str_parms_dump(str_parms);
out_str = str_parms_to_str(str_parms);
str_parms_destroy(str_parms);
ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str);
free(out_str);
}
int main(void)
{
struct str_parms *str_parms;
test_str_parms_str("");
test_str_parms_str(";");
test_str_parms_str("=");
test_str_parms_str("=;");
test_str_parms_str("=bar");
test_str_parms_str("=bar;");
test_str_parms_str("foo=");
test_str_parms_str("foo=;");
test_str_parms_str("foo=bar");
test_str_parms_str("foo=bar;");
test_str_parms_str("foo=bar;baz");
test_str_parms_str("foo=bar;baz=");
test_str_parms_str("foo=bar;baz=bat");
test_str_parms_str("foo=bar;baz=bat;");
test_str_parms_str("foo=bar;baz=bat;foo=bar");
return 0;
}
#endif