You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
ugui/fm/libconf.c

193 lines
3.9 KiB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <linux/limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PATHS_NO 3
static char temp[PATH_MAX] = {0};
static int valid_paths = 0;
static char paths[PATHS_NO][PATH_MAX] = {0}; // 0: xdg, 1: fallback, 2: global
static const mode_t CONFDIR_MODE = S_IRWXU | S_IRWXU | S_IRWXU | S_IRWXU;
static void err(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "[libconf]: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
// implements "mkdir -p"
static int mkdir_p(const char *path)
{
char tmp_path[PATH_MAX] = {0};
strncpy(tmp_path, path, PATH_MAX - 1);
struct stat st;
for (char *tk = strtok(tmp_path, "/"); tk != NULL; tk = strtok(NULL, "/")) {
if (tk != tmp_path) {
tk[-1] = '/';
}
if (stat(tmp_path, &st)) {
if (errno != ENOENT) {
err("could not stat() %s: %s",
tmp_path,
strerror(errno));
return 1;
}
if (mkdir(tmp_path, CONFDIR_MODE)) {
err("could not mkdir() %s: %s",
tmp_path,
strerror(errno));
return 1;
}
} else if (!S_ISDIR(st.st_mode)) {
err("could not create directory %s: file exists but it is "
"not a directory",
tmp_path);
return 1;
}
}
return 0;
}
// TODO: add a way to add a custom directory to the paths, for example via an
// environment variable
// TODO: maybe add a LIBCONF_PATH that overrides the defaults
/* default paths are:
* 1. ${XDG_CONFIG_HOME}/libconf/appid.d/
* 2. ${HOME}/.config/libconf/appid.d/
* 3. etc/libconf/appid.d/
*/
static int fill_paths(const char *id)
{
// TODO: verify id
if (id == NULL) {
err("must provide a valid app id");
return 1;
}
const char *xdg = getenv("XDG_CONFIG_HOME");
if (xdg != NULL) {
snprintf(paths[0], PATH_MAX, "%s/libconf/%s.d", xdg, id);
}
const char *home = getenv("HOME");
if (home) {
snprintf(temp, PATH_MAX, "%s/.config/libconf/%s.d", home, id);
if (mkdir_p(temp) == 0) {
strcpy(paths[1], temp);
}
}
// do not create global config path since most likely we don't have
// the necessary permissions to do so
snprintf(paths[2], PATH_MAX, "/etc/libconf/%s.d", id);
for (size_t i = 0; i < PATHS_NO; i++) {
printf("paths[%ld] = %s\n", i, paths[i]);
}
valid_paths = 1;
return 0;
}
// get config file path
const char *lcnf_get_conf_file(const char *id, const char *name, int global)
{
static char str[PATH_MAX] = {0};
if (id == NULL) {
return NULL;
}
if (!valid_paths) {
if (fill_paths(id)) {
return NULL;
}
}
// quick path for global config file
if (global && paths[2][0] != '\0') {
snprintf(str, PATH_MAX, "%s/%s", paths[2], name);
return str;
}
int found = 0;
for (size_t i = 0; i < PATHS_NO; i++) {
if (paths[i][0] == '\0') {
continue;
}
struct stat st;
snprintf(str, PATH_MAX, "%s/%s", paths[i], name);
if (stat(str, &st) && errno != ENOENT) {
err("could not stat %s: %s", str, strerror(errno));
}
if (S_ISREG(st.st_mode)) {
found = 1;
break;
}
}
return found ? str : NULL;
}
// get config file directory path
const char *lcnf_get_conf_dir(const char *id, int global)
{
if (id == NULL) {
return NULL;
}
if (global) {
return paths[2][0] != '\0' ? paths[2] : NULL;
}
if (paths[0][0] != '\0') {
return paths[0];
} else if (paths[1][0] != '\0') {
return paths[1];
} else {
return NULL;
}
}
// TODO: watch directory for file updates
int lcnf_watch_conf_dir(const char *id, int global);
// TODO: collect all files into a temporary one, useful for when you have multiple
// configuration files like 00-foo.conf, 01-bar.conf and 99-baz.conf
const char *lcnf_collect_conf_files(const char *id, int global);
#if 1
int main(void)
{
const char *p = lcnf_get_conf_file("pippo", "pippo.toml", 0);
if (p) {
printf("config found: %s\n", p);
} else {
printf("config not found\n");
}
return 0;
}
#endif