1 #define LOG_TAG "bt_osi_config" 2 3 #include <assert.h> 4 #include <ctype.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <utils/Log.h> 9 10 #include "config.h" 11 #include "list.h" 12 13 typedef struct { 14 char *key; 15 char *value; 16 } entry_t; 17 18 typedef struct { 19 char *name; 20 list_t *entries; 21 } section_t; 22 23 struct config_t { 24 list_t *sections; 25 }; 26 27 static void config_parse(FILE *fp, config_t *config); 28 29 static section_t *section_new(const char *name); 30 static void section_free(void *ptr); 31 static section_t *section_find(const config_t *config, const char *section); 32 33 static entry_t *entry_new(const char *key, const char *value); 34 static void entry_free(void *ptr); 35 static entry_t *entry_find(const config_t *config, const char *section, const char *key); 36 37 config_t *config_new(const char *filename) { 38 assert(filename != NULL); 39 40 FILE *fp = fopen(filename, "rt"); 41 if (!fp) { 42 ALOGE("%s unable to open file '%s': %s", __func__, filename, strerror(errno)); 43 return NULL; 44 } 45 46 config_t *config = calloc(1, sizeof(config_t)); 47 if (!config) { 48 ALOGE("%s unable to allocate memory for config_t.", __func__); 49 fclose(fp); 50 return NULL; 51 } 52 53 config->sections = list_new(section_free); 54 config_parse(fp, config); 55 56 fclose(fp); 57 58 return config; 59 } 60 61 void config_free(config_t *config) { 62 if (!config) 63 return; 64 65 list_free(config->sections); 66 free(config); 67 } 68 69 bool config_has_section(const config_t *config, const char *section) { 70 assert(config != NULL); 71 assert(section != NULL); 72 73 return (section_find(config, section) != NULL); 74 } 75 76 bool config_has_key(const config_t *config, const char *section, const char *key) { 77 assert(config != NULL); 78 assert(section != NULL); 79 assert(key != NULL); 80 81 return (entry_find(config, section, key) != NULL); 82 } 83 84 int config_get_int(const config_t *config, const char *section, const char *key, int def_value) { 85 assert(config != NULL); 86 assert(section != NULL); 87 assert(key != NULL); 88 89 entry_t *entry = entry_find(config, section, key); 90 if (!entry) 91 return def_value; 92 93 char *endptr; 94 int ret = strtol(entry->value, &endptr, 0); 95 return (*endptr == '\0') ? ret : def_value; 96 } 97 98 bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value) { 99 assert(config != NULL); 100 assert(section != NULL); 101 assert(key != NULL); 102 103 entry_t *entry = entry_find(config, section, key); 104 if (!entry) 105 return def_value; 106 107 if (!strcmp(entry->value, "true")) 108 return true; 109 if (!strcmp(entry->value, "false")) 110 return false; 111 112 return def_value; 113 } 114 115 const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value) { 116 assert(config != NULL); 117 assert(section != NULL); 118 assert(key != NULL); 119 120 entry_t *entry = entry_find(config, section, key); 121 if (!entry) 122 return def_value; 123 124 return entry->value; 125 } 126 127 void config_set_int(config_t *config, const char *section, const char *key, int value) { 128 assert(config != NULL); 129 assert(section != NULL); 130 assert(key != NULL); 131 132 char value_str[32] = { 0 }; 133 sprintf(value_str, "%d", value); 134 config_set_string(config, section, key, value_str); 135 } 136 137 void config_set_bool(config_t *config, const char *section, const char *key, bool value) { 138 assert(config != NULL); 139 assert(section != NULL); 140 assert(key != NULL); 141 142 config_set_string(config, section, key, value ? "true" : "false"); 143 } 144 145 void config_set_string(config_t *config, const char *section, const char *key, const char *value) { 146 section_t *sec = section_find(config, section); 147 if (!sec) { 148 sec = section_new(section); 149 list_append(config->sections, sec); 150 } 151 152 for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) { 153 entry_t *entry = list_node(node); 154 if (!strcmp(entry->key, key)) { 155 free(entry->value); 156 entry->value = strdup(value); 157 return; 158 } 159 } 160 161 entry_t *entry = entry_new(key, value); 162 list_append(sec->entries, entry); 163 } 164 165 static char *trim(char *str) { 166 while (isspace(*str)) 167 ++str; 168 169 if (!*str) 170 return str; 171 172 char *end_str = str + strlen(str) - 1; 173 while (end_str > str && isspace(*end_str)) 174 --end_str; 175 176 end_str[1] = '\0'; 177 return str; 178 } 179 180 static void config_parse(FILE *fp, config_t *config) { 181 assert(fp != NULL); 182 assert(config != NULL); 183 184 int line_num = 0; 185 char line[1024]; 186 char section[1024]; 187 strcpy(section, CONFIG_DEFAULT_SECTION); 188 189 while (fgets(line, sizeof(line), fp)) { 190 char *line_ptr = trim(line); 191 ++line_num; 192 193 // Skip blank and comment lines. 194 if (*line_ptr == '\0' || *line_ptr == '#') 195 continue; 196 197 if (*line_ptr == '[') { 198 size_t len = strlen(line_ptr); 199 if (line_ptr[len - 1] != ']') { 200 ALOGD("%s unterminated section name on line %d.", __func__, line_num); 201 continue; 202 } 203 strncpy(section, line_ptr + 1, len - 2); 204 section[len - 2] = '\0'; 205 } else { 206 char *split = strchr(line_ptr, '='); 207 if (!split) { 208 ALOGD("%s no key/value separator found on line %d.", __func__, line_num); 209 continue; 210 } 211 212 *split = '\0'; 213 config_set_string(config, section, trim(line_ptr), trim(split + 1)); 214 } 215 } 216 } 217 218 static section_t *section_new(const char *name) { 219 section_t *section = calloc(1, sizeof(section_t)); 220 if (!section) 221 return NULL; 222 223 section->name = strdup(name); 224 section->entries = list_new(entry_free); 225 return section; 226 } 227 228 static void section_free(void *ptr) { 229 if (!ptr) 230 return; 231 232 section_t *section = ptr; 233 free(section->name); 234 list_free(section->entries); 235 } 236 237 static section_t *section_find(const config_t *config, const char *section) { 238 for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) { 239 section_t *sec = list_node(node); 240 if (!strcmp(sec->name, section)) 241 return sec; 242 } 243 244 return NULL; 245 } 246 247 static entry_t *entry_new(const char *key, const char *value) { 248 entry_t *entry = calloc(1, sizeof(entry_t)); 249 if (!entry) 250 return NULL; 251 252 entry->key = strdup(key); 253 entry->value = strdup(value); 254 return entry; 255 } 256 257 static void entry_free(void *ptr) { 258 if (!ptr) 259 return; 260 261 entry_t *entry = ptr; 262 free(entry->key); 263 free(entry->value); 264 } 265 266 static entry_t *entry_find(const config_t *config, const char *section, const char *key) { 267 section_t *sec = section_find(config, section); 268 if (!sec) 269 return NULL; 270 271 for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) { 272 entry_t *entry = list_node(node); 273 if (!strcmp(entry->key, key)) 274 return entry; 275 } 276 277 return NULL; 278 } 279