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