Merge "libcutils: add a simple string parameter parsing ("a=b;c=d;") helper utils"
diff --git a/include/cutils/str_parms.h b/include/cutils/str_parms.h
new file mode 100644
index 0000000..247c996
--- /dev/null
+++ b/include/cutils/str_parms.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef __CUTILS_STR_PARMS_H
+#define __CUTILS_STR_PARMS_H
+
+#include <stdint.h>
+
+struct str_parms;
+
+struct str_parms *str_parms_create(void);
+struct str_parms *str_parms_create_str(const char *_string);
+void str_parms_destroy(struct str_parms *str_parms);
+
+void str_parms_del(struct str_parms *str_parms, const char *key);
+
+int str_parms_add_str(struct str_parms *str_parms, const char *key,
+                      const char *value);
+int str_parms_add_int(struct str_parms *str_parms, const char *key, int value);
+
+int str_parms_add_float(struct str_parms *str_parms, const char *key,
+                        float value);
+
+int str_parms_get_str(struct str_parms *str_parms, const char *key,
+                      char *out_val, int len);
+int str_parms_get_int(struct str_parms *str_parms, const char *key,
+                      int *out_val);
+int str_parms_get_float(struct str_parms *str_parms, const char *key,
+                        float *out_val);
+
+char *str_parms_to_str(struct str_parms *str_parms);
+
+/* debug */
+void str_parms_dump(struct str_parms *str_parms);
+
+#endif /* __CUTILS_STR_PARMS_H */
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 25d36da..4e8f9cf 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -46,7 +46,8 @@
 	properties.c \
 	threads.c \
 	sched_policy.c \
-	iosched_policy.c
+	iosched_policy.c \
+	str_parms.c
 
 commonHostSources := \
         ashmem-host.c
@@ -138,4 +139,12 @@
 LOCAL_CFLAGS += $(targetSmpFlag)
 include $(BUILD_SHARED_LIBRARY)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := tst_str_parms
+LOCAL_CFLAGS += -DTEST_STR_PARMS
+LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
+
 endif #!sim
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
new file mode 100644
index 0000000..dfa1f73
--- /dev/null
+++ b/libcutils/str_parms.c
@@ -0,0 +1,329 @@
+/*
+ * 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;
+}
+
+static bool remove_pair(void *key, void *value, void *context)
+{
+    struct str_parms *str_parms = context;
+
+    hashmapRemove(str_parms->map, key);
+    free(key);
+    free(value);
+    return true;
+}
+
+void str_parms_destroy(struct str_parms *str_parms)
+{
+    hashmapForEach(str_parms->map, remove_pair, str_parms);
+    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;
+
+    LOGV("%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);
+
+        items++;
+next_pair:
+        kvpair = strtok_r(NULL, ";", &tmpstr);
+    }
+
+    if (!items)
+        LOGV("%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;
+}
+
+void str_parms_del(struct str_parms *str_parms, const char *key)
+{
+    hashmapRemove(str_parms->map, (void *)key);
+}
+
+int str_parms_add_str(struct str_parms *str_parms, const char *key,
+                      const char *value)
+{
+    void *old_val;
+    char *tmp;
+
+    tmp = strdup(value);
+    old_val = hashmapPut(str_parms->map, (void *)key, tmp);
+
+    if (old_val) {
+        free(old_val);
+    } else if (errno == ENOMEM) {
+        free(tmp);
+        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)
+{
+    LOGI("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_dump(str_parms);
+    out_str = str_parms_to_str(str_parms);
+    str_parms_destroy(str_parms);
+    LOGI("%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;");
+
+    return 0;
+}
+#endif