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