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/memory.h> 29#include <cutils/str_parms.h> 30#include <log/log.h> 31 32#define UNUSED __attribute__((unused)) 33 34struct str_parms { 35 Hashmap *map; 36}; 37 38 39static 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 */ 45static 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 55struct 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 69err: 70 free(str_parms); 71 return NULL; 72} 73 74struct remove_ctxt { 75 struct str_parms *str_parms; 76 const char *key; 77}; 78 79static 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 102do_remove: 103 hashmapRemove(ctxt->str_parms->map, key); 104 free(key); 105 free(value); 106 return should_continue; 107} 108 109void 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 118void 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 129struct 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++; 176next_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 187err_strdup: 188 str_parms_destroy(str_parms); 189err_create_str_parms: 190 return NULL; 191} 192 193int 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 229clean_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 238int 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 251int 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 265int str_parms_has_key(struct str_parms *str_parms, const char *key) { 266 return hashmapGet(str_parms->map, (void *)key) != NULL; 267} 268 269int 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 281int 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 297int 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 316static 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 339char *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 350static 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 356void str_parms_dump(struct str_parms *str_parms) 357{ 358 hashmapForEach(str_parms->map, dump_entry, str_parms); 359} 360