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
config_new(const char * filename)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
config_free(config_t * config)61 void config_free(config_t *config) {
62 if (!config)
63 return;
64
65 list_free(config->sections);
66 free(config);
67 }
68
config_has_section(const config_t * config,const char * section)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
config_has_key(const config_t * config,const char * section,const char * key)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
config_get_int(const config_t * config,const char * section,const char * key,int def_value)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
config_get_bool(const config_t * config,const char * section,const char * key,bool def_value)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
config_get_string(const config_t * config,const char * section,const char * key,const char * def_value)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
config_set_int(config_t * config,const char * section,const char * key,int value)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
config_set_bool(config_t * config,const char * section,const char * key,bool value)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
config_set_string(config_t * config,const char * section,const char * key,const char * value)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
trim(char * str)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
config_parse(FILE * fp,config_t * config)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
section_new(const char * name)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
section_free(void * ptr)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
section_find(const config_t * config,const char * section)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
entry_new(const char * key,const char * value)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
entry_free(void * ptr)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
entry_find(const config_t * config,const char * section,const char * key)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