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,
298 if (fseek(fp, 0, SEEK_END) != 0 || (file_size = ftell(fp)) < 0
299 || fseek(fp, 0, SEEK_SET) != 0)
301 fprintf(stderr,
"canfigger: Failed to determine size of %s: %s\n",
302 filename, strerror(errno));
306 buffer = malloc_wrap(file_size + 1);
310 n_bytes = fread(buffer, 1, file_size, fp);
311 if (n_bytes != (
size_t) file_size)
314 fprintf(stderr,
"canfigger: Error reading %s: %s\n", filename,
318 "canfigger: Partial read of %s: expected %ld bytes, got %zu bytes\n",
319 filename, file_size, n_bytes);
324 buffer[file_size] =
'\0';
333free_incomplete_node(
struct Canfigger **node)
341 free((*node)->value);
343 if ((*node)->attributes)
345 free((*node)->attributes->str);
346 free((*node)->attributes);
358 struct Canfigger *root = NULL, *cur_node = NULL;
360 char *file_contents = read_entire_file(file);
361 if (file_contents == NULL)
371 char *parse_start = file_contents;
372 if ((
unsigned char) parse_start[0] == 0xEF &&
373 (
unsigned char) parse_start[1] == 0xBB &&
374 (
unsigned char) parse_start[2] == 0xBF)
378 line.start = parse_start;
384 line.end = strchr(line.start,
'\n');
386 line.end ? (size_t) (line.end - line.start) : strlen(line.start);
389 if (line.len == 0 && !line.end)
392 char *tmp_line = malloc_wrap(line.len + 1);
395 canfigger_free_list(&root);
400 memcpy(tmp_line, line.start, line.len);
401 tmp_line[line.len] =
'\0';
402 line.start = line.end ? line.end + 1 : line.start + line.len;
404 char *line_ptr = tmp_line;
405 truncate_whitespace(line_ptr);
407 while (isspace((
unsigned char) *line_ptr))
408 line_ptr = erase_lead_char(*line_ptr, line_ptr);
410 if (*line_ptr ==
'\0' || *line_ptr ==
'#' || *line_ptr ==
'[')
418 node_complete =
false;
420 add_key_node(&root, &cur_node);
421 if (cur_node == prev_node)
428 cur_node->key = NULL;
429 line_ptr = grab_str_segment(line_ptr, &cur_node->key,
'=');
433 free_incomplete_node(&cur_node);
438 cur_node->value = NULL;
442 line_ptr = grab_str_segment(line_ptr, &cur_node->value, delimiter);
443 if (!cur_node->value)
446 free_incomplete_node(&cur_node);
454 cur_node->attributes = malloc_wrap(
sizeof(
struct attributes));
455 if (!cur_node->attributes)
458 free_incomplete_node(&cur_node);
462 struct attributes *attr_ptr = cur_node->attributes;
463 attr_ptr->current = NULL;
465 attr_ptr->str = strclone(line_ptr, 0);
469 free_incomplete_node(&cur_node);
473 attr_ptr->iter_ptr = attr_ptr->str;
477 char *delimiter_ptr = strchr(attr_ptr->iter_ptr, delimiter);
478 while (delimiter_ptr)
480 *delimiter_ptr =
'\n';
481 delimiter_ptr = strchr(delimiter_ptr, delimiter);
485 cur_node->attributes = NULL;
487 cur_node->next = NULL;
488 node_complete =
true;
514xdg_base_dir(
const char *xdg_env,
const char *fallback)
516 const char *base = getenv(xdg_env);
518 return strclone(base, 0);
520 const char *home = getenv(
"HOME");
524 size_t len = strlen(home) + 1 + strlen(fallback) + 1;
525 char *result = malloc_wrap(len);
528 snprintf(result, len,
"%s/%s", home, fallback);
536dir_for_appname(
const char *appname,
int csidl)
538 if (!appname || *appname ==
'\0')
542 if (FAILED(SHGetFolderPathA(NULL, csidl, NULL, 0, base)))
545 size_t len = strlen(base) + 1 + strlen(appname) + 1;
546 char *result = malloc_wrap(len);
549 snprintf(result, len,
"%s\\%s", base, appname);
554dir_for_appname(
const char *appname,
const char *xdg_env,
555 const char *xdg_fallback)
557 if (!appname || *appname ==
'\0')
560 char *base = xdg_base_dir(xdg_env, xdg_fallback);
574 return dir_for_appname(appname, CSIDL_APPDATA);
576 return dir_for_appname(appname,
"XDG_CONFIG_HOME",
".config");
584 if (!filename || !*filename)
589 if (FAILED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, base)))
592 size_t len = strlen(base) + 1 + strlen(filename) + 1;
593 char *result = malloc_wrap(len);
596 snprintf(result, len,
"%s\\%s", base, filename);
599 char *base = xdg_base_dir(
"XDG_CONFIG_HOME",
".config");
613 return dir_for_appname(appname, CSIDL_LOCAL_APPDATA);
615 return dir_for_appname(appname,
"XDG_DATA_HOME",
".local/share");
624 return dir_for_appname(appname, CSIDL_LOCAL_APPDATA);
626 return dir_for_appname(appname,
"XDG_CACHE_HOME",
".cache");
634 if (!dir || !*dir || !file || !*file)
637 size_t dirlen = strlen(dir);
638 size_t filelen = strlen(file);
639 bool needs_sep = dirlen > 0 && dir[dirlen - 1] !=
'/'
640 && dir[dirlen - 1] !=
'\\';
641 size_t total = dirlen + (needs_sep ? 1 : 0) + filelen + 1;
643 char *result = malloc_wrap(total);
648 const char sep =
'\\';
650 const char sep =
'/';
654 snprintf(result, total,
"%s%c%s", dir, sep, file);
656 snprintf(result, total,
"%s%s", dir, file);
Public API for the Canfigger configuration file parser.
char * canfigger_config_file(const char *filename)
Return the path to a config file in the platform base config directory.
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_cache_dir(const char *appname)
Return the platform cache directory for an application.
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.