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