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 33 struct str_parms { 34 Hashmap *map; 35 }; 36 37 38 static 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 */ 44 static 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 54 struct 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 68 err: 69 free(str_parms); 70 return NULL; 71 } 72 73 struct remove_ctxt { 74 struct str_parms *str_parms; 75 const char *key; 76 }; 77 78 static 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 101 do_remove: 102 hashmapRemove(ctxt->str_parms->map, key); 103 free(key); 104 free(value); 105 return should_continue; 106 } 107 108 void 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 117 void 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 128 struct 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++; 175 next_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 186 err_strdup: 187 str_parms_destroy(str_parms); 188 err_create_str_parms: 189 return NULL; 190 } 191 192 int 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 214 int 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 227 int 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 241 int 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 253 int 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 269 int 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 287 static 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 310 char *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 321 static 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 327 void 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 333 static 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 350 int 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