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
13typedef struct {
14  char *key;
15  char *value;
16} entry_t;
17
18typedef struct {
19  char *name;
20  list_t *entries;
21} section_t;
22
23struct config_t {
24  list_t *sections;
25};
26
27static void config_parse(FILE *fp, config_t *config);
28
29static section_t *section_new(const char *name);
30static void section_free(void *ptr);
31static section_t *section_find(const config_t *config, const char *section);
32
33static entry_t *entry_new(const char *key, const char *value);
34static void entry_free(void *ptr);
35static entry_t *entry_find(const config_t *config, const char *section, const char *key);
36
37config_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
61void config_free(config_t *config) {
62  if (!config)
63    return;
64
65  list_free(config->sections);
66  free(config);
67}
68
69bool 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
76bool 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
84int 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
98bool 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
115const 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
127void 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
137void 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
145void 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
165static 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
180static 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
218static 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
228static 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
237static 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
247static 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
257static 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
266static 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