blob: f38c7f0c6b4900ee029e38b43d2fd75a93d2c9c2 [file] [log] [blame]
#define LOG_TAG "bt_osi_config"
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Log.h>
#include "config.h"
#include "list.h"
typedef struct {
char *key;
char *value;
} entry_t;
typedef struct {
char *name;
list_t *entries;
} section_t;
struct config_t {
list_t *sections;
};
static void config_parse(FILE *fp, config_t *config);
static section_t *section_new(const char *name);
static void section_free(void *ptr);
static section_t *section_find(const config_t *config, const char *section);
static entry_t *entry_new(const char *key, const char *value);
static void entry_free(void *ptr);
static entry_t *entry_find(const config_t *config, const char *section, const char *key);
config_t *config_new(const char *filename) {
assert(filename != NULL);
FILE *fp = fopen(filename, "rt");
if (!fp) {
ALOGE("%s unable to open file '%s': %s", __func__, filename, strerror(errno));
return NULL;
}
config_t *config = calloc(1, sizeof(config_t));
if (!config) {
ALOGE("%s unable to allocate memory for config_t.", __func__);
fclose(fp);
return NULL;
}
config->sections = list_new(section_free);
config_parse(fp, config);
fclose(fp);
return config;
}
void config_free(config_t *config) {
if (!config)
return;
list_free(config->sections);
free(config);
}
bool config_has_section(const config_t *config, const char *section) {
assert(config != NULL);
assert(section != NULL);
return (section_find(config, section) != NULL);
}
bool config_has_key(const config_t *config, const char *section, const char *key) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
return (entry_find(config, section, key) != NULL);
}
int config_get_int(const config_t *config, const char *section, const char *key, int def_value) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
entry_t *entry = entry_find(config, section, key);
if (!entry)
return def_value;
char *endptr;
int ret = strtol(entry->value, &endptr, 0);
return (*endptr == '\0') ? ret : def_value;
}
bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
entry_t *entry = entry_find(config, section, key);
if (!entry)
return def_value;
if (!strcmp(entry->value, "true"))
return true;
if (!strcmp(entry->value, "false"))
return false;
return def_value;
}
const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
entry_t *entry = entry_find(config, section, key);
if (!entry)
return def_value;
return entry->value;
}
void config_set_int(config_t *config, const char *section, const char *key, int value) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
char value_str[32] = { 0 };
sprintf(value_str, "%d", value);
config_set_string(config, section, key, value_str);
}
void config_set_bool(config_t *config, const char *section, const char *key, bool value) {
assert(config != NULL);
assert(section != NULL);
assert(key != NULL);
config_set_string(config, section, key, value ? "true" : "false");
}
void config_set_string(config_t *config, const char *section, const char *key, const char *value) {
section_t *sec = section_find(config, section);
if (!sec) {
sec = section_new(section);
list_append(config->sections, sec);
}
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
entry_t *entry = list_node(node);
if (!strcmp(entry->key, key)) {
free(entry->value);
entry->value = strdup(value);
return;
}
}
entry_t *entry = entry_new(key, value);
list_append(sec->entries, entry);
}
static char *trim(char *str) {
while (isspace(*str))
++str;
if (!*str)
return str;
char *end_str = str + strlen(str) - 1;
while (end_str > str && isspace(*end_str))
--end_str;
end_str[1] = '\0';
return str;
}
static void config_parse(FILE *fp, config_t *config) {
assert(fp != NULL);
assert(config != NULL);
int line_num = 0;
char line[1024];
char section[1024];
strcpy(section, CONFIG_DEFAULT_SECTION);
while (fgets(line, sizeof(line), fp)) {
char *line_ptr = trim(line);
++line_num;
// Skip blank and comment lines.
if (*line_ptr == '\0' || *line_ptr == '#')
continue;
if (*line_ptr == '[') {
size_t len = strlen(line_ptr);
if (line_ptr[len - 1] != ']') {
ALOGD("%s unterminated section name on line %d.", __func__, line_num);
continue;
}
strncpy(section, line_ptr + 1, len - 2);
section[len - 2] = '\0';
} else {
char *split = strchr(line_ptr, '=');
if (!split) {
ALOGD("%s no key/value separator found on line %d.", __func__, line_num);
continue;
}
*split = '\0';
config_set_string(config, section, trim(line_ptr), trim(split + 1));
}
}
}
static section_t *section_new(const char *name) {
section_t *section = calloc(1, sizeof(section_t));
if (!section)
return NULL;
section->name = strdup(name);
section->entries = list_new(entry_free);
return section;
}
static void section_free(void *ptr) {
if (!ptr)
return;
section_t *section = ptr;
free(section->name);
list_free(section->entries);
}
static section_t *section_find(const config_t *config, const char *section) {
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
section_t *sec = list_node(node);
if (!strcmp(sec->name, section))
return sec;
}
return NULL;
}
static entry_t *entry_new(const char *key, const char *value) {
entry_t *entry = calloc(1, sizeof(entry_t));
if (!entry)
return NULL;
entry->key = strdup(key);
entry->value = strdup(value);
return entry;
}
static void entry_free(void *ptr) {
if (!ptr)
return;
entry_t *entry = ptr;
free(entry->key);
free(entry->value);
}
static entry_t *entry_find(const config_t *config, const char *section, const char *key) {
section_t *sec = section_find(config, section);
if (!sec)
return NULL;
for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
entry_t *entry = list_node(node);
if (!strcmp(entry->key, key))
return entry;
}
return NULL;
}