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