Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_config_file.c
Go to the documentation of this file.
1
7#include <stdio.h>
8#include <errno.h>
9
10#include "nilorea/n_common.h"
12#include "nilorea/n_pcre.h"
13
18void free_no_null(void* ptr) {
19 char* strptr = ptr;
20 if (strptr) {
21 free(strptr);
22 }
23 return;
24}
25
32 if (!section)
33 return;
34 Free(section->section_name);
35 destroy_ht(&section->entries);
36 Free(section);
37} /* destroy_config_file_section(...) */
38
45CONFIG_FILE* load_config_file(char* filename, int* errors) {
46 CONFIG_FILE* cfg_file = NULL;
47 CONFIG_FILE_SECTION* section = NULL;
48 char buffer[MAX_CONFIG_LINE_LEN] = "";
49 char** split_result = NULL;
50 N_PCRE* npcre = npcre_new("(.*?=)(.*)", 99, 0);
51 int error = 0;
52 FILE* in = NULL;
53
54 __n_assert(filename, return NULL);
55 int fd = open(filename, O_RDONLY);
56 error = errno;
57
58 if (fd == -1) {
59 n_log(LOG_ERR, "Unable to open %s: %s", _str(filename), strerror(error));
60 (*errors)++;
61 npcre_delete(&npcre);
62 return NULL;
63 }
64
65 in = fdopen(fd, "r");
66 error = errno;
67 if (!in) {
68 n_log(LOG_ERR, "fdopen failed for %s (fd=%d): %s", _str(filename), fd, strerror(errno));
69 close(fd);
70 (*errors)++;
71 npcre_delete(&npcre);
72 return NULL;
73 }
74
75 Malloc(cfg_file, CONFIG_FILE, 1);
76 if (!cfg_file) {
77 fclose(in);
78 npcre_delete(&npcre);
79 return NULL;
80 }
81 cfg_file->sections = new_generic_list(MAX_LIST_ITEMS);
82 if (!cfg_file->sections) {
83 Free(cfg_file);
84 fclose(in);
85 npcre_delete(&npcre);
86 return NULL;
87 }
88 cfg_file->filename = strdup(filename);
89 if (!cfg_file->filename) {
90 n_log(LOG_ERR, "couldn't strdup( %s )", _str(filename));
91 (*errors)++;
92 Free(cfg_file);
93 fclose(in);
94 npcre_delete(&npcre);
95 return NULL;
96 }
97 /* adding default section */
98 CONFIG_FILE_SECTION* default_section = NULL;
99 Malloc(default_section, CONFIG_FILE_SECTION, 1);
100 if (!default_section) {
101 n_log(LOG_ERR, "couldn't allocate default_section");
102 (*errors)++;
103 Free(cfg_file);
104 fclose(in);
105 npcre_delete(&npcre);
106 return NULL;
107 }
108
109 default_section->section_name = strdup("__DEFAULT__");
110 if (!default_section->section_name) {
111 n_log(LOG_ERR, "couldn't allocate default_section name");
112 (*errors)++;
113 Free(cfg_file);
114 Free(default_section);
115 fclose(in);
116 npcre_delete(&npcre);
117 return NULL;
118 }
119
120 default_section->entries = new_ht(CONFIG_SECTION_HASH_TABLE_LEN);
121 if (!default_section->entries) {
122 n_log(LOG_ERR, "error creating hash table of size %d for default_section", CONFIG_SECTION_HASH_TABLE_LEN);
123 (*errors)++;
124 Free(default_section->section_name);
125 Free(default_section);
126 Free(cfg_file);
127 fclose(in);
128 npcre_delete(&npcre);
129 return NULL;
130 }
131 list_push(cfg_file->sections, default_section, &destroy_config_file_section);
132
133 size_t line_number = 0;
134 while (fgets(buffer, MAX_CONFIG_LINE_LEN, in)) {
135 NSTRBYTE start = 0, end = 0;
136
137 char* bufptr = (char*)&buffer;
138 line_number++;
139
140 bufptr = trim_nocopy(buffer);
141 if (strlen(bufptr) == 0) {
142 continue;
143 }
144
145 switch (bufptr[0]) {
146 /* comment */
147 case '#':
148 case ';':
149 continue;
150 /* new section */
151 case '[':
152 end = 0;
153 /* go to begin of section name */
154 if (skipu(bufptr, ']', &end, 1) != TRUE) {
155 n_log(LOG_ERR, "coulnd't find end of section at line %zu of %s (string:%s)", line_number, filename, buffer);
156 (*errors)++;
157 continue;
158 }
159 /* keep only the name */
160 bufptr++;
161 bufptr[end - 1] = '\0';
162 bufptr = trim_nocopy(bufptr);
163 if (strlen(bufptr) == 0) {
164 n_log(LOG_ERR, "section without name at line %zu", line_number);
165 (*errors)++;
166 continue;
167 }
168
169 Malloc(section, CONFIG_FILE_SECTION, 1);
170 __n_assert(section, break);
171
172 section->section_name = strdup(bufptr);
173 if (!section->section_name) {
174 n_log(LOG_ERR, "couldn't duplicate %s", bufptr);
175 (*errors)++;
176 continue;
177 }
178
179 section->entries = new_ht(CONFIG_SECTION_HASH_TABLE_LEN);
180 if (!section->entries) {
181 n_log(LOG_ERR, "error creating [%s] hash table of size %d", bufptr, CONFIG_SECTION_HASH_TABLE_LEN);
182 (*errors)++;
183 continue;
184 }
185 list_push(cfg_file->sections, section, &destroy_config_file_section);
186 continue;
187
188 /* if it's not a comment, not a section, not empty, then it's an entry ! */
189 default:
190 if (!section) {
191 section = default_section;
192 n_log(LOG_DEBUG, "No section, setting __DEFAULT__ starting line %zu of %s (string:%s)", line_number, filename, bufptr);
193 }
194
195 if (npcre_match(bufptr, npcre) == TRUE) {
196 Malloc(split_result, char*, 3);
197
198 split_result[2] = NULL;
199 split_result[0] = str_replace(npcre->match_list[1], "=", " ");
200 split_result[1] = strdup(npcre->match_list[2]);
201 } else {
202 split_result = NULL;
203 }
204
205 if (!split_result) {
206 n_log(LOG_ERR, "couldn't find entry separator '=' at line %zu of %s (string:%s)", line_number, filename, bufptr);
207 (*errors)++;
208 continue;
209 }
210 if (split_count(split_result) < 2) {
211 n_log(LOG_ERR, "Invalid split count at line %zu of %s (string:%s, count:%d)", line_number, filename, bufptr, split_count(split_result));
212 (*errors)++;
213 free_split_result(&split_result);
214 continue;
215 }
216 if (strlen(trim_nocopy(split_result[0])) == 0) {
217 free_split_result(&split_result);
218 n_log(LOG_ERR, "couldn't find entry name at line %zu of %s (string:%s)", line_number, filename, buffer[start]);
219 (*errors)++;
220 continue;
221 }
222
223 /* value pointer */
224 char* valptr = NULL;
225 /* flags */
226 int closing_delimiter_found = 0;
227 int opening_delimiter_found = 0;
228 /* initialize with empty delimiter */
229 char delimiter = '\0';
230
231 /* If the trimmed value have a zero length it means we only had spaces or tabs
232 In that particular case we set the val to NULL */
233 if (strlen(trim_nocopy(split_result[1])) == 0) {
234 Free(split_result[1]);
235 valptr = NULL;
236 delimiter = '\0';
237 } else /* we have a value, or something between delimiters */
238 {
239 valptr = trim_nocopy(split_result[1]);
240 size_t it = strlen(valptr);
241
242 /* get value right boundary */
243 while (1) {
244 /* if comments tags replace them with end of string */
245 if (split_result[1][it] == ';' || split_result[1][it] == '#') {
246 split_result[1][it] = '\0';
247 } else {
248 /* we found a delimiter on the right */
249 if (split_result[1][it] == '"' || split_result[1][it] == '\'') {
250 delimiter = split_result[1][it];
251 split_result[1][it] = '\0';
252 closing_delimiter_found = 1;
253 break;
254 }
255 }
256 if (it == 0)
257 break;
258 else
259 it--;
260 }
261
262 if (strlen(trim_nocopy(split_result[1])) == 0) {
263 Free(split_result[1]);
264 valptr = NULL;
265 delimiter = '\0';
266 }
267
268 /* get value left boundary */
269 it = 0;
270 while (split_result[1][it] != '\0') {
271 if (split_result[1][it] == ';' || split_result[1][it] == '#') {
272 split_result[1][it] = '\0';
273 } else {
274 if (delimiter != '\0' && split_result[1][it] == delimiter) {
275 opening_delimiter_found = 1;
276 it++;
277 valptr = split_result[1] + it;
278 break;
279 }
280 it++;
281 }
282 }
283 if (strlen(trim_nocopy(split_result[1])) == 0) {
284 Free(split_result[1]);
285 valptr = NULL;
286 delimiter = '\0';
287 }
288 }
289 if (delimiter != '\0' && ((opening_delimiter_found && !closing_delimiter_found) || (!opening_delimiter_found && closing_delimiter_found))) {
290 free_split_result(&split_result);
291 n_log(LOG_ERR, "Only one delimiter found at line %zu of %s (string:%s)", line_number, filename, buffer);
292 (*errors)++;
293 continue;
294 }
295
296 char* result = NULL;
297 N_STR* entry_key = NULL;
298 size_t nb_entry_in_sections = 0;
299 while (TRUE) {
300 nstrprintf(entry_key, "%s%zu", trim_nocopy(split_result[0]), nb_entry_in_sections);
301 if (ht_get_ptr(section->entries, _nstr(entry_key), (void**)&result) == FALSE) {
302 break;
303 }
304 nb_entry_in_sections++;
305 free_nstr(&entry_key);
306 }
307 char* strptr = NULL;
308 if (valptr) {
309 strptr = strdup(valptr);
310 }
311 ht_put_ptr(section->entries, _nstr(entry_key), strptr, &free_no_null);
312 free_nstr(&entry_key);
313 free_split_result(&split_result);
314 continue;
315 }
316 }
317 npcre_delete(&npcre);
318 if (!feof(in)) {
319 n_log(LOG_ERR, "couldn't read EOF for %s", filename);
320 (*errors)++;
321 }
322 fclose(in);
323
324 return cfg_file;
325
326} /*load_config_file */
327
334int write_config_file(CONFIG_FILE* cfg_file, char* filename) {
335 __n_assert(cfg_file, return FALSE);
336 __n_assert(filename, return FALSE);
337
338 int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600); // Secure permissions
339 if (fd == -1) {
340 int error = errno;
341 n_log(LOG_ERR, "Unable to open '%s' for writing (0600): %s", _str(filename), strerror(error));
342 return FALSE;
343 }
344
345 FILE* out = NULL;
346 out = fdopen(fd, "w");
347 if (!out) {
348 int error = errno;
349 n_log(LOG_ERR, "fdopen failed for '%s' (fd=%d): %s", _str(filename), fd, strerror(error));
350 close(fd);
351 return FALSE;
352 }
353
354 char* last_section = NULL;
355 char *section = NULL, *key = NULL, *val = NULL;
356
357 config_foreach(cfg_file, section, key, val) {
358 /* write section name */
359 if (!last_section || strcmp(last_section, section) != 0) {
360 fprintf(out, "[%s]\n", section);
361 FreeNoLog(last_section);
362 last_section = strdup(section);
363 }
364 fprintf(out, "%s=%s\n", key, val);
365 }
367 fclose(out);
368 FreeNoLog(last_section);
369
370 return TRUE;
371} /* write_config_file */
372
379size_t get_nb_config_file_sections(CONFIG_FILE* cfg_file, char* section_name) {
380 __n_assert(cfg_file, return 0);
381 __n_assert(cfg_file->sections, return 0);
382
383 size_t nb_sections = 0;
384 list_foreach(listnode, cfg_file->sections) {
385 CONFIG_FILE_SECTION* section = (CONFIG_FILE_SECTION*)listnode->ptr;
386 if (section_name) {
387 if (!strcmp(section->section_name, section_name)) {
388 nb_sections++;
389 }
390 } else {
391 nb_sections++;
392 }
393 }
394 return nb_sections;
395} /* get_nb_config_file_sections(...) */
396
405size_t get_nb_config_file_sections_entries(CONFIG_FILE* cfg_file, char* section_name, size_t section_position, char* entry) {
406 __n_assert(cfg_file, return 0);
407 __n_assert(cfg_file->sections, return 0);
408 __n_assert(section_name, return 0);
409 __n_assert(entry, return 0);
410
411 size_t nb_sections = 0;
412 size_t nb_entry_in_sections = 0;
413 list_foreach(listnode, cfg_file->sections) {
414 CONFIG_FILE_SECTION* section = (CONFIG_FILE_SECTION*)listnode->ptr;
415 if (!strcmp(section->section_name, section_name)) {
416 if (nb_sections == section_position) {
417 char* result = NULL;
418 N_STR* entry_key = NULL;
419 while (TRUE) {
420 nstrprintf(entry_key, "%s%zu", entry, nb_entry_in_sections);
421 if (ht_get_ptr(section->entries, _nstr(entry_key), (void**)&result) == FALSE) {
422 free_nstr(&entry_key);
423 break;
424 }
425 nb_entry_in_sections++;
426 free_nstr(&entry_key);
427 }
428 break;
429 }
430 nb_sections++;
431 }
432 }
433 return nb_entry_in_sections;
434} /* get_nb_config_file_sections_entries(...) */
435
445char* get_config_section_value(CONFIG_FILE* cfg_file, char* section_name, size_t section_position, char* entry, size_t entry_position) {
446 __n_assert(cfg_file, return NULL);
447 __n_assert(cfg_file->sections, return NULL);
448 __n_assert(section_name, return NULL);
449 __n_assert(entry, return NULL);
450
451 if (section_position >= cfg_file->sections->nb_items) {
452 n_log(LOG_DEBUG, "section_position (%d) is higher than the number of item in list (%d)", section_position, cfg_file->sections->nb_items);
453 return NULL;
454 }
455
456 size_t section_position_it = 0;
457 HASH_TABLE* entries = NULL;
458 list_foreach(listnode, cfg_file->sections) {
459 CONFIG_FILE_SECTION* section = (CONFIG_FILE_SECTION*)listnode->ptr;
460 if (!strcmp(section->section_name, section_name)) {
461 if (section_position_it == section_position) {
462 entries = section->entries;
463 break;
464 }
465 section_position_it++;
466 }
467 }
468
469 char* result = NULL;
470 N_STR* entry_key = NULL;
471 nstrprintf(entry_key, "%s%zu", entry, entry_position);
472 if (ht_get_ptr(entries, _nstr(entry_key), (void**)&result) == FALSE) {
473 /* n_log( LOG_DEBUG , "ht_get_ptr( %p , trim_nocopy( %s ) , (void **)&result ) returned FALSE !" , entries , _nstr( entry_key ) ); */
474 free_nstr(&entry_key);
475 return NULL;
476 }
477 free_nstr(&entry_key);
478
479 return result;
480} /* get_config_section_value */
481
488 __n_assert(cfg_file && (*cfg_file), return FALSE);
489
490 Free((*cfg_file)->filename);
491
492 if ((*cfg_file)->sections) {
493 list_destroy(&(*cfg_file)->sections);
494 }
495
496 Free((*cfg_file));
497
498 return TRUE;
499} /* destroy_config_file */
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:251
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:187
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:258
#define _str(__PTR)
define true
Definition n_common.h:176
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:242
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition n_common.h:182
CONFIG_FILE * load_config_file(char *filename, int *errors)
load a config file
int write_config_file(CONFIG_FILE *cfg_file, char *filename)
write a config file
size_t get_nb_config_file_sections_entries(CONFIG_FILE *cfg_file, char *section_name, size_t section_position, char *entry)
Get the number of config file with section_name.
#define config_endfor
Foreach elements of CONFIG_FILE macro END.
#define CONFIG_SECTION_HASH_TABLE_LEN
size of the hash table of config sections entries
int destroy_config_file(CONFIG_FILE **cfg_file)
Destroy a loaded config file.
#define MAX_CONFIG_LINE_LEN
maximum length of a single config line
char * get_config_section_value(CONFIG_FILE *cfg_file, char *section_name, size_t section_position, char *entry, size_t entry_position)
Function to parse sections and get entries values.
#define config_foreach(__config, __section_name, __key, __val)
Foreach elements of CONFIG_FILE macro, i.e config_foreach( config , section , key ,...
size_t get_nb_config_file_sections(CONFIG_FILE *cfg_file, char *section_name)
Get the number of config file with section_name.
Structure of a config file.
Structure of a config section.
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition n_hash.c:2047
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2180
HASH_TABLE * new_ht(size_t size)
Create a hash table with the given size.
Definition n_hash.c:1948
int ht_put_ptr(HASH_TABLE *table, const char *key, void *ptr, void(*destructor)(void *ptr))
put a pointer to the string value with given key in the targeted hash table
Definition n_hash.c:2100
structure of a hash table
Definition n_hash.h:114
size_t nb_items
number of item currently in the list
Definition n_list.h:41
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:199
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper.
Definition n_list.h:65
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:518
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Definition n_list.h:55
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:69
#define LOG_DEBUG
debug-level messages
Definition n_log.h:64
#define LOG_ERR
error conditions
Definition n_log.h:56
char * trim_nocopy(char *s)
trim and zero end the string, WARNING: keep and original pointer to delete the string correctly
Definition n_str.c:102
size_t NSTRBYTE
N_STR base unit.
Definition n_str.h:36
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:176
int split_count(char **split_result)
Count split elements.
Definition n_str.c:954
char * str_replace(const char *string, const char *substr, const char *replacement)
Replace "substr" by "replacement" inside string taken from http://coding.debuntu.org/c-implementing-s...
Definition n_str.c:1341
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
Definition n_str.h:94
int free_split_result(char ***tab)
Free a split result allocated array.
Definition n_str.c:970
int skipu(char *string, char toskip, NSTRBYTE *iterator, int inc)
skip until 'toskip' occurence is found from 'iterator' to the next 'toskip' value.
Definition n_str.c:750
A box including a string and his lenght.
Definition n_str.h:39
const char ** match_list
populated match list if nPcreCapMatch is called
Definition n_pcre.h:40
N_PCRE * npcre_new(char *str, int max_cap, int flags)
From pcre doc, the flag bits are: PCRE_ANCHORED Force pattern anchoring PCRE_AUTO_CALLOUT Compile aut...
Definition n_pcre.c:58
int npcre_match(char *str, N_PCRE *pcre)
Return TRUE if str matches regexp, and make captures up to max_cap.
Definition n_pcre.c:149
int npcre_delete(N_PCRE **pcre)
Free a N_PCRE pointer.
Definition n_pcre.c:101
N_PCRE structure.
Definition n_pcre.h:27
Common headers and low-level functions & define.
void free_no_null(void *ptr)
local free for config entries
void destroy_config_file_section(void *ptr)
Destroy a config file section.
Config file reading and writing.
PCRE helpers for regex matching.