#include "config.h" #include #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_shell(void) { struct passwd *passwd = getpwuid(getuid()); if (passwd == NULL) { LOG_ERRNO("failed to lookup user"); return NULL; } const char *shell = passwd->pw_shell; LOG_DBG("user's shell: %s", shell); return strdup(shell); } 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(const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) { if (strcmp(key, "font") == 0) { free(conf->font); conf->font = strdup(value); } else if (strcmp(key, "shell") == 0) { free(conf->shell); conf->shell = strdup(value); } else { LOG_WARN("%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)( const char *key, const char *value, 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, }; #if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG static const char *const section_names[] = { [SECTION_MAIN] = "main", }; #endif 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; } char *key = strtok(line, "="); char *value = strtok(NULL, "\n"); { while (isspace(*key)) key++; char *end = key + strlen(key) - 1; while (isspace(*end)) end--; *(end + 1) = '\0'; } if (value == NULL) { if (key != NULL && strlen(key) > 0 && key[0] != '#') { LOG_ERR("%s:%d: syntax error: %s", path, lineno, line); free(line); return false; } free(line); continue; } { while (isspace(*value)) value++; char *end = value + strlen(value) - 1; while (isspace(*end)) end--; *(end + 1) = '\0'; } if (key[0] == '#') { free(line); continue; } LOG_DBG("section=%s, key='%s', value='%s'", section_names[section], key, value); parser_fun_t section_parser = section_parser_map[section]; assert(section_parser != NULL); if (!section_parser(key, value, conf, path, lineno)) { free(line); return false; } free(line); } return true; } bool config_load(struct config *conf) { bool ret = false; *conf = (struct config) { .shell = get_shell(), .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; } ret = parse_config_file(f, conf, path); fclose(f); out: free(path); return ret; } void config_free(struct config conf) { free(conf.shell); free(conf.font); }