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 static bool remove_pair(void *key, void *value, void *context) 74 { 75 struct str_parms *str_parms = context; 76 77 hashmapRemove(str_parms->map, key); 78 free(key); 79 free(value); 80 return true; 81 } 82 83 void str_parms_destroy(struct str_parms *str_parms) 84 { 85 hashmapForEach(str_parms->map, remove_pair, str_parms); 86 hashmapFree(str_parms->map); 87 free(str_parms); 88 } 89 90 struct str_parms *str_parms_create_str(const char *_string) 91 { 92 struct str_parms *str_parms; 93 char *str; 94 char *kvpair; 95 char *tmpstr; 96 int items = 0; 97 98 str_parms = str_parms_create(); 99 if (!str_parms) 100 goto err_create_str_parms; 101 102 str = strdup(_string); 103 if (!str) 104 goto err_strdup; 105 106 LOGV("%s: source string == '%s'\n", __func__, _string); 107 108 kvpair = strtok_r(str, ";", &tmpstr); 109 while (kvpair && *kvpair) { 110 char *eq = strchr(kvpair, '='); /* would love strchrnul */ 111 char *value; 112 char *key; 113 void *old_val; 114 115 if (eq == kvpair) 116 goto next_pair; 117 118 if (eq) { 119 key = strndup(kvpair, eq - kvpair); 120 if (*(++eq)) 121 value = strdup(eq); 122 else 123 value = strdup(""); 124 } else { 125 key = strdup(kvpair); 126 value = strdup(""); 127 } 128 129 /* if we replaced a value, free it */ 130 old_val = hashmapPut(str_parms->map, key, value); 131 if (old_val) 132 free(old_val); 133 134 items++; 135 next_pair: 136 kvpair = strtok_r(NULL, ";", &tmpstr); 137 } 138 139 if (!items) 140 LOGV("%s: no items found in string\n", __func__); 141 142 free(str); 143 144 return str_parms; 145 146 err_strdup: 147 str_parms_destroy(str_parms); 148 err_create_str_parms: 149 return NULL; 150 } 151 152 void str_parms_del(struct str_parms *str_parms, const char *key) 153 { 154 hashmapRemove(str_parms->map, (void *)key); 155 } 156 157 int str_parms_add_str(struct str_parms *str_parms, const char *key, 158 const char *value) 159 { 160 void *old_val; 161 char *tmp; 162 163 tmp = strdup(value); 164 old_val = hashmapPut(str_parms->map, (void *)key, tmp); 165 166 if (old_val) { 167 free(old_val); 168 } else if (errno == ENOMEM) { 169 free(tmp); 170 return -ENOMEM; 171 } 172 return 0; 173 } 174 175 int str_parms_add_int(struct str_parms *str_parms, const char *key, int value) 176 { 177 char val_str[12]; 178 int ret; 179 180 ret = snprintf(val_str, sizeof(val_str), "%d", value); 181 if (ret < 0) 182 return -EINVAL; 183 184 ret = str_parms_add_str(str_parms, key, val_str); 185 return ret; 186 } 187 188 int str_parms_add_float(struct str_parms *str_parms, const char *key, 189 float value) 190 { 191 char val_str[23]; 192 int ret; 193 194 ret = snprintf(val_str, sizeof(val_str), "%.10f", value); 195 if (ret < 0) 196 return -EINVAL; 197 198 ret = str_parms_add_str(str_parms, key, val_str); 199 return ret; 200 } 201 202 int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val, 203 int len) 204 { 205 char *value; 206 207 value = hashmapGet(str_parms->map, (void *)key); 208 if (value) 209 return strlcpy(val, value, len); 210 211 return -ENOENT; 212 } 213 214 int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val) 215 { 216 char *value; 217 char *end; 218 219 value = hashmapGet(str_parms->map, (void *)key); 220 if (!value) 221 return -ENOENT; 222 223 *val = (int)strtol(value, &end, 0); 224 if (*value != '\0' && *end == '\0') 225 return 0; 226 227 return -EINVAL; 228 } 229 230 int str_parms_get_float(struct str_parms *str_parms, const char *key, 231 float *val) 232 { 233 float out; 234 char *value; 235 char *end; 236 237 value = hashmapGet(str_parms->map, (void *)key); 238 if (!value) 239 return -ENOENT; 240 241 out = strtof(value, &end); 242 if (*value != '\0' && *end == '\0') 243 return 0; 244 245 return -EINVAL; 246 } 247 248 static bool combine_strings(void *key, void *value, void *context) 249 { 250 char **old_str = context; 251 char *new_str; 252 int ret; 253 254 ret = asprintf(&new_str, "%s%s%s=%s", 255 *old_str ? *old_str : "", 256 *old_str ? ";" : "", 257 (char *)key, 258 (char *)value); 259 if (*old_str) 260 free(*old_str); 261 262 if (ret >= 0) { 263 *old_str = new_str; 264 return true; 265 } 266 267 *old_str = NULL; 268 return false; 269 } 270 271 char *str_parms_to_str(struct str_parms *str_parms) 272 { 273 char *str = NULL; 274 275 if (hashmapSize(str_parms->map) > 0) 276 hashmapForEach(str_parms->map, combine_strings, &str); 277 else 278 str = strdup(""); 279 return str; 280 } 281 282 static bool dump_entry(void *key, void *value, void *context) 283 { 284 LOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); 285 return true; 286 } 287 288 void str_parms_dump(struct str_parms *str_parms) 289 { 290 hashmapForEach(str_parms->map, dump_entry, str_parms); 291 } 292 293 #ifdef TEST_STR_PARMS 294 static void test_str_parms_str(const char *str) 295 { 296 struct str_parms *str_parms; 297 char *out_str; 298 int ret; 299 300 str_parms = str_parms_create_str(str); 301 str_parms_dump(str_parms); 302 out_str = str_parms_to_str(str_parms); 303 str_parms_destroy(str_parms); 304 LOGI("%s: '%s' stringified is '%s'", __func__, str, out_str); 305 free(out_str); 306 } 307 308 int main(void) 309 { 310 struct str_parms *str_parms; 311 312 test_str_parms_str(""); 313 test_str_parms_str(";"); 314 test_str_parms_str("="); 315 test_str_parms_str("=;"); 316 test_str_parms_str("=bar"); 317 test_str_parms_str("=bar;"); 318 test_str_parms_str("foo="); 319 test_str_parms_str("foo=;"); 320 test_str_parms_str("foo=bar"); 321 test_str_parms_str("foo=bar;"); 322 test_str_parms_str("foo=bar;baz"); 323 test_str_parms_str("foo=bar;baz="); 324 test_str_parms_str("foo=bar;baz=bat"); 325 test_str_parms_str("foo=bar;baz=bat;"); 326 327 return 0; 328 } 329 #endif 330