43#include "canfigger_version.h"
47static char *grab_str_segment(
char *a,
char **dest,
const int c);
60strclone(
const char *src,
size_t n)
65 dest = malloc(strlen(src) + 1);
80 perror(
"canfigger: malloc");
125canfigger_free_current_key_node_advance(
struct Canfigger **node)
129 if ((*node)->attributes)
131 if ((*node)->attributes->current)
133 free((*node)->attributes->current);
134 (*node)->attributes->current = NULL;
137 if ((*node)->attributes->str)
139 free((*node)->attributes->str);
140 (*node)->attributes->str = NULL;
143 free((*node)->attributes);
144 (*node)->attributes = NULL;
149 free((*node)->value);
150 (*node)->value = NULL;
156 struct Canfigger *temp_node = (*node)->next;
187erase_lead_char(
const int lc,
char *haystack)
189 char *ptr = haystack;
201truncate_whitespace(
char *str)
219 while (isspace((
unsigned char)*str))
233grab_str_segment(
char *a,
char **dest,
const int c)
235 a = erase_lead_char(
' ', a);
237 char *b = strchr(a, c);
240 *dest = strclone(a, 0);
245 *dest = strclone(a, len);
249 truncate_whitespace(*dest);
254malloc_wrap(
size_t size)
256 void *retval = malloc(size);
260 perror(
"canfigger: malloc");
273 (*cur_node)->next = tmp_node;
277 *cur_node = tmp_node;
284read_entire_file(
const char *filename)
290 FILE *fp = fopen(filename,
"rb");
293 fprintf(stderr,
"canfigger: Failed to open %s: %s\n", filename, strerror(errno));
297 if (fseek(fp, 0, SEEK_END) != 0 || (file_size = ftell(fp)) < 0 || fseek(fp, 0, SEEK_SET) != 0)
299 fprintf(stderr,
"canfigger: Failed to determine size of %s: %s\n", filename, strerror(errno));
303 buffer = malloc_wrap(file_size + 1);
307 n_bytes = fread(buffer, 1, file_size, fp);
308 if (n_bytes != (
size_t) file_size)
311 fprintf(stderr,
"canfigger: Error reading %s: %s\n", filename, strerror(errno));
313 fprintf(stderr,
"canfigger: Partial read of %s: expected %ld bytes, got %zu bytes\n",
314 filename, file_size, n_bytes);
319 buffer[file_size] =
'\0';
328free_incomplete_node(
struct Canfigger **node)
336 free((*node)->value);
338 if ((*node)->attributes)
340 free((*node)->attributes->str);
341 free((*node)->attributes);
353 struct Canfigger *root = NULL, *cur_node = NULL;
355 char *file_contents = read_entire_file(file);
356 if (file_contents == NULL)
366 char *parse_start = file_contents;
367 if ((
unsigned char)parse_start[0] == 0xEF &&
368 (
unsigned char)parse_start[1] == 0xBB &&
369 (
unsigned char)parse_start[2] == 0xBF)
373 line.start = parse_start;
379 line.end = strchr(line.start,
'\n');
380 line.len = line.end ? (size_t)(line.end - line.start) : strlen(line.start);
383 if (line.len == 0 && !line.end)
386 char *tmp_line = malloc_wrap(line.len + 1);
388 canfigger_free_list(&root);
393 memcpy(tmp_line, line.start, line.len);
394 tmp_line[line.len] =
'\0';
395 line.start = line.end ? line.end + 1 : line.start + line.len;
397 char *line_ptr = tmp_line;
398 truncate_whitespace(line_ptr);
400 while (isspace((
unsigned char)*line_ptr))
401 line_ptr = erase_lead_char(*line_ptr, line_ptr);
403 if (*line_ptr ==
'\0' || *line_ptr ==
'#' || *line_ptr ==
'[') {
410 node_complete =
false;
412 add_key_node(&root, &cur_node);
413 if (cur_node == prev_node) {
419 cur_node->key = NULL;
420 line_ptr = grab_str_segment(line_ptr, &cur_node->key,
'=');
424 free_incomplete_node(&cur_node);
429 cur_node->value = NULL;
433 line_ptr = grab_str_segment(line_ptr, &cur_node->value, delimiter);
434 if (!cur_node->value)
437 free_incomplete_node(&cur_node);
445 cur_node->attributes = malloc_wrap(
sizeof(
struct attributes));
446 if (!cur_node->attributes)
449 free_incomplete_node(&cur_node);
453 struct attributes *attr_ptr = cur_node->attributes;
454 attr_ptr->current = NULL;
456 attr_ptr->str = strclone(line_ptr, 0);
460 free_incomplete_node(&cur_node);
464 attr_ptr->iter_ptr = attr_ptr->str;
468 char *delimiter_ptr = strchr(attr_ptr->iter_ptr, delimiter);
469 while (delimiter_ptr)
471 *delimiter_ptr =
'\n';
472 delimiter_ptr = strchr(delimiter_ptr, delimiter);
476 cur_node->attributes = NULL;
478 cur_node->next = NULL;
479 node_complete =
true;
504dir_for_appname(
const char *appname,
int csidl)
506 if (!appname || *appname ==
'\0')
510 if (FAILED(SHGetFolderPathA(NULL, csidl, NULL, 0, base)))
513 size_t len = strlen(base) + 1 + strlen(appname) + 1;
514 char *result = malloc_wrap(len);
517 snprintf(result, len,
"%s\\%s", base, appname);
522dir_for_appname(
const char *appname,
const char *xdg_env,
const char *xdg_fallback)
524 if (!appname || *appname ==
'\0')
527 const char *base = getenv(xdg_env);
530 size_t len = strlen(base) + 1 + strlen(appname) + 1;
531 char *result = malloc_wrap(len);
534 snprintf(result, len,
"%s/%s", base, appname);
538 const char *home = getenv(
"HOME");
542 size_t len = strlen(home) + 1 + strlen(xdg_fallback) + 1 + strlen(appname) + 1;
543 char *result = malloc_wrap(len);
546 snprintf(result, len,
"%s/%s/%s", home, xdg_fallback, appname);
556 return dir_for_appname(appname, CSIDL_APPDATA);
558 return dir_for_appname(appname,
"XDG_CONFIG_HOME",
".config");
567 return dir_for_appname(appname, CSIDL_LOCAL_APPDATA);
569 return dir_for_appname(appname,
"XDG_DATA_HOME",
".local/share");
577 if (!dir || !*dir || !file || !*file)
580 size_t dirlen = strlen(dir);
581 size_t filelen = strlen(file);
582 bool needs_sep = dirlen > 0 && dir[dirlen - 1] !=
'/' && dir[dirlen - 1] !=
'\\';
583 size_t total = dirlen + (needs_sep ? 1 : 0) + filelen + 1;
585 char *result = malloc_wrap(total);
590 const char sep =
'\\';
592 const char sep =
'/';
596 snprintf(result, total,
"%s%c%s", dir, sep, file);
598 snprintf(result, total,
"%s%s", dir, file);
Public API for the Canfigger configuration file parser.
struct Canfigger * canfigger_parse_file(const char *file, const int delimiter)
Parse a configuration file into a linked list of key-value nodes.
char * canfigger_data_dir(const char *appname)
Return the platform data directory for an application.
void canfigger_free_list(struct Canfigger **node)
Free all remaining nodes in the list.
char * canfigger_config_dir(const char *appname)
Return the platform config directory for an application.
char * canfigger_path_join(const char *dir, const char *file)
Join a directory path and a filename with the platform separator.
void canfigger_free_current_key_node_advance(struct Canfigger **node)
Free the current node and advance the list pointer to the next node.
A single node in the parsed configuration linked list.
Internal iteration state for a node's attribute list.