PatchworkOS  966e257
A non-POSIX operating system.
Loading...
Searching...
No Matches
config.c
Go to the documentation of this file.
2
3#include <ctype.h>
4#include <stdarg.h>
5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <strings.h>
10#include <sys/io.h>
11#include <sys/list.h>
12
13typedef struct config_pair
14{
15 char* key;
16 char* value;
19
20typedef struct config_section
21{
22 char* name;
26
27typedef struct config
28{
30} config_t;
31
32static char* config_trim_whitespace(char* str)
33{
34 char* end;
35 while (isspace((unsigned char)*str))
36 {
37 str++;
38 }
39
40 if (*str == 0)
41 {
42 return str;
43 }
44
45 end = str + strlen(str) - 1;
46 while (end > str && isspace((unsigned char)*end))
47 {
48 end--;
49 }
50
51 *(end + 1) = '\0';
52 return str;
53}
54
55static config_section_t* config_find_section(config_t* cfg, const char* section)
56{
57 if (cfg == NULL || section == NULL)
58 {
59 return NULL;
60 }
61
63 LIST_FOR_EACH(sec, &cfg->sections, entry)
64 {
65 if (strcasecmp(sec->name, section) == 0)
66 {
67 return sec;
68 }
69 }
70
71 return NULL;
72}
73
74static config_pair_t* config_find_pair(config_section_t* sec, const char* key)
75{
76 if (sec == NULL || key == NULL)
77 {
78 return NULL;
79 }
80
81 config_pair_t* pair;
82 LIST_FOR_EACH(pair, &sec->pairs, entry)
83 {
84 if (strcasecmp(pair->key, key) == 0)
85 {
86 return pair;
87 }
88 }
89
90 return NULL;
91}
92
93config_t* config_open(const char* prefix, const char* name)
94{
95 if (prefix == NULL || name == NULL)
96 {
97 return NULL;
98 }
99
100 char path[MAX_PATH] = {0};
101 snprintf(path, MAX_PATH - 1, "/cfg/%s-%s.cfg", prefix, name);
102
103 FILE* file = fopen(path, "r");
104 if (file == NULL)
105 {
106 return NULL;
107 }
108
109 config_t* config = malloc(sizeof(config_t));
110 if (config == NULL)
111 {
112 fclose(file);
113 return NULL;
114 }
115 list_init(&config->sections);
116
117 char lineBuffer[1024];
118 config_section_t* currentSection = NULL;
119 while (fgets(lineBuffer, sizeof(lineBuffer), file) != NULL)
120 {
122 if (line[0] == '\0' || line[0] == '#' || line[0] == ';')
123 {
124 continue;
125 }
126
127 if (line[0] == '[')
128 {
129 char* end = strchr(line, ']');
130 if (end == NULL)
131 {
132 continue;
133 }
134 *end = '\0';
135 char* name = config_trim_whitespace(line + 1);
136 if (name[0] == '\0')
137 {
138 continue;
139 }
140
141 config_section_t* section = malloc(sizeof(config_section_t));
142 if (section == NULL)
143 {
144 goto error;
145 }
146 section->name = strdup(name);
147 if (section->name == NULL)
148 {
149 free(section);
150 goto error;
151 }
152 list_entry_init(&section->entry);
153 list_init(&section->pairs);
154
155 list_push_back(&config->sections, &section->entry);
156 currentSection = section;
157 }
158 else
159 {
160 char* equals = strchr(line, '=');
161 if (equals == NULL || currentSection == NULL)
162 {
163 continue;
164 }
165 *equals = '\0';
166 char* key = config_trim_whitespace(line);
167 char* value = config_trim_whitespace(equals + 1);
168 if (key[0] == '\0')
169 {
170 continue;
171 }
172
173 config_pair_t* pair = malloc(sizeof(config_pair_t));
174 if (pair == NULL)
175 {
176 goto error;
177 }
178 pair->key = strdup(key);
179 pair->value = strdup(value);
180 if (pair->key == NULL || pair->value == NULL)
181 {
182 free(pair->key);
183 free(pair->value);
184 free(pair);
185 goto error;
186 }
187 list_entry_init(&pair->entry);
188
189 list_push_back(&currentSection->pairs, &pair->entry);
190 }
191 }
192
193 fclose(file);
194 return config;
195
196error:
197 fclose(file);
198 config_close(config);
199 return NULL;
200}
201
203{
204 if (config == NULL)
205 {
206 return;
207 }
208
209 while (!list_is_empty(&config->sections))
210 {
212
213 while (!list_is_empty(&sec->pairs))
214 {
216 free(pair->key);
217 free(pair->value);
218 free(pair);
219 }
220
221 free(sec->name);
222 free(sec);
223 }
224
225 free(config);
226}
227
228void config_get(config_t* config, const char* section, uint64_t index, const char* fallback, const char** outValue,
229 const char** outKey)
230{
231 if (config == NULL || section == NULL)
232 {
233 *outValue = fallback;
234 *outKey = fallback;
235 return;
236 }
237
238 config_section_t* sec = config_find_section(config, section);
239 if (sec == NULL)
240 {
241 *outValue = fallback;
242 *outKey = fallback;
243 return;
244 }
245
246 uint64_t currentIndex = 0;
247 config_pair_t* pair;
248 LIST_FOR_EACH(pair, &sec->pairs, entry)
249 {
250 if (currentIndex == index)
251 {
252 *outValue = pair->value;
253 *outKey = pair->key;
254 return;
255 }
256 currentIndex++;
257 }
258
259 *outValue = fallback;
260 *outKey = fallback;
261}
262
263const char* config_get_string(config_t* config, const char* section, const char* key, const char* fallback)
264{
265 if (config == NULL || section == NULL || key == NULL)
266 {
267 return fallback;
268 }
269
270 config_section_t* sec = config_find_section(config, section);
271 if (sec == NULL)
272 {
273 return fallback;
274 }
275
276 config_pair_t* pair = config_find_pair(sec, key);
277 if (pair == NULL)
278 {
279 return fallback;
280 }
281
282 return pair->value;
283}
284
285int64_t config_get_int(config_t* config, const char* section, const char* key, int64_t fallback)
286{
287 if (config == NULL || section == NULL || key == NULL)
288 {
289 return fallback;
290 }
291
292 const char* str = config_get_string(config, section, key, NULL);
293 if (str == NULL)
294 {
295 return fallback;
296 }
297
298 char* end;
299 int64_t value = strtoll(str, &end, 0);
300 if (end == str || *end != '\0')
301 {
302 return fallback;
303 }
304
305 return value;
306}
307
308bool config_get_bool(config_t* config, const char* section, const char* key, bool fallback)
309{
310 if (config == NULL || section == NULL || key == NULL)
311 {
312 return fallback;
313 }
314
315 const char* str = config_get_string(config, section, key, NULL);
316 if (str == NULL)
317 {
318 return fallback;
319 }
320
321 if (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
322 strcmp(str, "1") == 0)
323 {
324 return true;
325 }
326 else if (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0 || strcasecmp(str, "off") == 0 ||
327 strcmp(str, "0") == 0)
328 {
329 return false;
330 }
331
332 return fallback;
333}
334
335config_array_t* config_get_array(config_t* config, const char* section, const char* key)
336{
337 static config_array_t emptyArray = {.items = NULL, .length = 0};
338 if (config == NULL || section == NULL || key == NULL)
339 {
340 goto return_empty;
341 }
342
343 const char* str = config_get_string(config, section, key, NULL);
344 if (str == NULL || str[0] == '\0')
345 {
346 goto return_empty;
347 }
348
349 uint64_t length = strlen(str);
350 uint64_t maxSize = sizeof(config_array_t) + (length * sizeof(char*)) + length + 1;
351
352 config_array_t* array = malloc(maxSize);
353 if (array == NULL)
354 {
355 return NULL;
356 }
357 array->items = (char**)(array + 1);
358 array->length = 0;
359
360 char* data = (char*)(array->items + length);
361
362 uint64_t index = 0;
363 while (*str != '\0')
364 {
365 while (isspace((unsigned char)*str))
366 {
367 str++;
368 }
369
370 if (*str == '\0')
371 {
372 break;
373 }
374
375 const char* start = str;
376
377 while (*str != '\0' && *str != ',')
378 {
379 str++;
380 }
381
382 const char* end = str;
383
384 while (end > start && isspace((unsigned char)*(end - 1)))
385 {
386 end--;
387 }
388
389 uint64_t len = (end > start) ? (end - start) : 0;
390 if (len > 0)
391 {
392 memcpy(data, start, len);
393 data[len] = '\0';
394
395 array->items[index] = data;
396
397 data += len + 1;
398 index++;
399 }
400
401 if (*str == ',')
402 {
403 str++;
404 }
405 }
406
407 array->length = index;
408 return array;
409
410return_empty:
411 if (false) // to satisfy compiler
412 {
413 }
414 config_array_t* empty = malloc(sizeof(config_array_t));
415 if (empty != NULL)
416 {
417 empty->items = NULL;
418 empty->length = 0;
419 }
420 return empty;
421}
422
424{
425 if (array != NULL)
426 {
427 free(array);
428 }
429}
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
_PUBLIC int isspace(int c)
Definition isspace.c:5
static fd_t data
Definition dwm.c:21
config_array_t * config_get_array(config_t *config, const char *section, const char *key)
Get an array of strings from a configuration file.
Definition config.c:335
config_t * config_open(const char *prefix, const char *name)
Open a configuration file.
Definition config.c:93
int64_t config_get_int(config_t *config, const char *section, const char *key, int64_t fallback)
Get an integer value from a configuration file.
Definition config.c:285
const char * config_get_string(config_t *config, const char *section, const char *key, const char *fallback)
Get a string value from a configuration file.
Definition config.c:263
void config_array_free(config_array_t *array)
Free a configuration array.
Definition config.c:423
bool config_get_bool(config_t *config, const char *section, const char *key, bool fallback)
Get a boolean value from a configuration file.
Definition config.c:308
void config_close(config_t *config)
Close a configuration file.
Definition config.c:202
void config_get(config_t *config, const char *section, uint64_t index, const char *fallback, const char **outValue, const char **outKey)
Get a value as a string by index from a configuration file.
Definition config.c:228
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:63
static list_entry_t * list_pop_first(list_t *list)
Pops the first entry from the list.
Definition list.h:375
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:343
static bool list_is_empty(list_t *list)
Checks if a list is empty.
Definition list.h:227
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:182
static void list_init(list_t *list)
Initializes a list.
Definition list.h:196
#define NULL
Pointer error value.
Definition NULL.h:23
#define CONTAINER_OF(ptr, type, member)
Container of macro.
static config_section_t * config_find_section(config_t *cfg, const char *section)
Definition config.c:55
static config_pair_t * config_find_pair(config_section_t *sec, const char *key)
Definition config.c:74
static char * config_trim_whitespace(char *str)
Definition config.c:32
static char lineBuffer[LOG_MAX_BUFFER]
Definition log.c:23
static dentry_t * file
Definition log_file.c:22
static void start()
Definition main.c:542
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__INT64_TYPE__ int64_t
Definition stdint.h:16
_PUBLIC char * fgets(char *_RESTRICT s, int n, FILE *_RESTRICT stream)
Definition fgets.c:5
_PUBLIC FILE * fopen(const char *_RESTRICT filename, const char *_RESTRICT mode)
Definition fopen.c:27
_PUBLIC int fclose(FILE *stream)
Definition fclose.c:7
_PUBLIC int snprintf(char *_RESTRICT s, size_t n, const char *_RESTRICT format,...)
Definition snprintf.c:3
_PUBLIC long long int strtoll(const char *_RESTRICT nptr, char **_RESTRICT endptr, int base)
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
char * strdup(const char *src)
Definition strdup.c:5
_PUBLIC void * memcpy(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
Definition memcpy.c:61
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
_PUBLIC int strcmp(const char *s1, const char *s2)
Definition strcmp.c:3
_PUBLIC char * strchr(const char *s, int c)
Definition strchr.c:3
int strcasecmp(const char *s1, const char *s2)
Definition strcasecmp.c:4
Definition file.h:34
Configuration array structure.
Definition config.h:38
char ** items
Definition config.h:39
uint64_t length
Definition config.h:40
char * value
Definition config.c:16
list_entry_t entry
Definition config.c:17
char * key
Definition config.c:15
char * name
Definition config.c:22
list_t pairs
Definition config.c:24
list_entry_t entry
Definition config.c:23
Opaque configuration structure.
Definition config.c:28
list_t sections
Definition config.c:29
A entry in a doubly linked list.
Definition list.h:36
A doubly linked list.
Definition list.h:49