From 0d1b4449b9268ad008852d38d3f7df544db7a410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 16 Jul 2019 11:52:22 +0200 Subject: [PATCH] conf: initial support for configuration file * Look for configuration file in (in this order): - XDG_CONFIG_HOME/footrc - ~/.config/footrc * Currently supports setting the font --- config.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 8 +++ main.c | 20 +++--- meson.build | 1 + 4 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 config.c create mode 100644 config.h diff --git a/config.c b/config.c new file mode 100644 index 00000000..1bc5de59 --- /dev/null +++ b/config.c @@ -0,0 +1,178 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "config" +#define LOG_ENABLE_DBG 0 +#include "log.h" + +static char * +get_config_path_user_config(void) +{ + struct passwd *passwd = getpwuid(getuid()); + if (passwd == NULL) { + LOG_ERRNO("failed to lookup user"); + return NULL; + } + + const char *home_dir = passwd->pw_dir; + LOG_DBG("user's home directory: %s", home_dir); + + int len = snprintf(NULL, 0, "%s/.config/footrc", home_dir); + char *path = malloc(len + 1); + snprintf(path, len + 1, "%s/.config/footrc", home_dir); + return path; +} + +static char * +get_config_path_xdg(void) +{ + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (xdg_config_home == NULL) + return NULL; + + int len = snprintf(NULL, 0, "%s/footrc", xdg_config_home); + char *path = malloc(len + 1); + snprintf(path, len + 1, "%s/footrc", xdg_config_home); + return path; +} + +static char * +get_config_path(void) +{ + struct stat st; + + char *config = get_config_path_xdg(); + if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode)) + return config; + free(config); + + /* 'Default' XDG_CONFIG_HOME */ + config = get_config_path_user_config(); + if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode)) + return config; + free(config); + + return NULL; +} + +static bool +parse_section_main(char *line, struct config *conf, const char *path, unsigned lineno) +{ + const char *key = strtok(line, "="); + const char *value = strtok(NULL, "\n"); + + LOG_DBG("%s:%u: key = %s, value=%s", path, lineno, key, value); + + if (strcmp(key, "font") == 0) { + free(conf->font); + conf->font = strdup(value); + } + + else { + LOG_ERR("%s:%u: invalid key: %s", path, lineno, key); + return false; + } + + return true; +} + +static bool +parse_config_file(FILE *f, struct config *conf, const char *path) +{ + enum section { + SECTION_MAIN, + } section = SECTION_MAIN; + + /* Function pointer, called for each key/value line */ + typedef bool (*parser_fun_t)( + char *line, struct config *conf, const char *path, unsigned lineno); + + /* Maps sections to line parser functions */ + static const parser_fun_t section_parser_map[] = { + [SECTION_MAIN] = &parse_section_main, + }; + + unsigned lineno = 0; + + while (true) { + errno = 0; + lineno++; + + char *line = NULL; + size_t count = 0; + ssize_t ret = getline(&line, &count, f); + + if (ret < 0) { + free(line); + if (errno != 0) { + LOG_ERRNO("failed to read from configuration"); + return false; + } + break; + } + + /* No sections yet */ + if (line[0] == '[' && line[strlen(line) - 1] == ']') { + assert(false); + return false; + } + + parser_fun_t section_parser = section_parser_map[section]; + assert(section_parser != NULL); + + if (!section_parser(line, conf, path, lineno)) { + free(line); + return false; + } + + free(line); + } + + return true; +} + +struct config +config_load(void) +{ + struct config conf = { + .font = strdup("monospace"), + }; + + char *path = get_config_path(); + LOG_INFO("loading configuration from %s", path); + + if (path == NULL) { + /* Default conf */ + LOG_WARN("no configuration found, using defaults"); + goto out; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + LOG_ERR("%s: failed to open", path); + goto out; + } + + parse_config_file(f, &conf, path); + fclose(f); + +out: + free(path); + return conf; +} + +void +config_free(struct config conf) +{ + free(conf.font); +} diff --git a/config.h b/config.h new file mode 100644 index 00000000..4215a80a --- /dev/null +++ b/config.h @@ -0,0 +1,8 @@ +#pragma once + +struct config { + char *font; +}; + +struct config config_load(void); +void config_free(struct config conf); diff --git a/main.c b/main.c index 542363f4..b88d8000 100644 --- a/main.c +++ b/main.c @@ -19,15 +19,16 @@ #define LOG_ENABLE_DBG 0 #include "log.h" +#include "config.h" #include "font.h" #include "grid.h" #include "input.h" #include "render.h" +#include "selection.h" #include "shm.h" #include "slave.h" #include "terminal.h" #include "vt.h" -#include "selection.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -264,12 +265,14 @@ main(int argc, char *const *argv) { int ret = EXIT_FAILURE; + struct config conf = config_load(); + static const struct option longopts[] = { {"font", required_argument, 0, 'f'}, {NULL, no_argument, 0, 0}, }; - const char *font_name = "monospace"; + //const char *font_name = "monospace"; while (true) { int c = getopt_long(argc, argv, ":f:h", longopts, NULL); @@ -278,7 +281,8 @@ main(int argc, char *const *argv) switch (c) { case 'f': - font_name = optarg; + free(conf.font); + conf.font = strdup(optarg); break; case 'h': @@ -340,19 +344,19 @@ main(int argc, char *const *argv) thrd_t keyboard_repeater_id; thrd_create(&keyboard_repeater_id, &keyboard_repeater, &term); - term.fonts[0] = font_from_name(font_name); + term.fonts[0] = font_from_name(conf.font); if (term.fonts[0] == NULL) goto out; { char fname[1024]; - snprintf(fname, sizeof(fname), "%s:style=bold", font_name); + snprintf(fname, sizeof(fname), "%s:style=bold", conf.font); term.fonts[1] = font_from_name(fname); - snprintf(fname, sizeof(fname), "%s:style=italic", font_name); + snprintf(fname, sizeof(fname), "%s:style=italic", conf.font); term.fonts[2] = font_from_name(fname); - snprintf(fname, sizeof(fname), "%s:style=bold italic", font_name); + snprintf(fname, sizeof(fname), "%s:style=bold italic", conf.font); term.fonts[3] = font_from_name(fname); } @@ -703,6 +707,8 @@ out: close(term.kbd.repeat.pipe_read_fd); close(term.kbd.repeat.pipe_write_fd); + config_free(conf); + cairo_debug_reset_static_data(); return ret; } diff --git a/meson.build b/meson.build index 33ae6fcf..37a3ed26 100644 --- a/meson.build +++ b/meson.build @@ -59,6 +59,7 @@ endforeach executable( 'foot', + 'config.c', 'config.h', 'commands.c', 'commands.h', 'csi.c', 'csi.h', 'font.c', 'font.h',