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