#include "Config.h" #include #include #include #include #include #include #include typedef enum { CONFIG_STRING, CONFIG_INTEGER, CONFIG_BOOLEAN, } ConfigValueType; typedef struct { const char *section; const char *key; ConfigValueType type; size_t offset; size_t size; } ConfigOption; #define STRING_OPTION(section_name, key_name, field) \ {section_name, key_name, CONFIG_STRING, offsetof(Config, field), \ sizeof(((Config *)0)->field)} #define INTEGER_OPTION(section_name, key_name, field) \ {section_name, key_name, CONFIG_INTEGER, offsetof(Config, field), 0} #define BOOLEAN_OPTION(section_name, key_name, field) \ {section_name, key_name, CONFIG_BOOLEAN, offsetof(Config, field), 0} static const ConfigOption CONFIG_OPTIONS[] = { STRING_OPTION("server", "host", host), INTEGER_OPTION("server", "port", port), STRING_OPTION("server", "locale", default_locale), STRING_OPTION("proxy", "proxy", proxy), STRING_OPTION("proxy", "list_file", proxy_list_file), INTEGER_OPTION("proxy", "max_retries", max_proxy_retries), BOOLEAN_OPTION("proxy", "randomize_username", randomize_username), BOOLEAN_OPTION("proxy", "randomize_password", randomize_password), STRING_OPTION("cache", "dir", cache_dir), INTEGER_OPTION("cache", "ttl_search", cache_ttl_search), INTEGER_OPTION("cache", "ttl_infobox", cache_ttl_infobox), INTEGER_OPTION("cache", "ttl_image", cache_ttl_image), STRING_OPTION("engines", "engines", engines), STRING_OPTION("engines", "yacy_instance", yacy_instance), INTEGER_OPTION("rate_limit", "search_requests", rate_limit_search_requests), INTEGER_OPTION("rate_limit", "search_interval", rate_limit_search_interval), INTEGER_OPTION("rate_limit", "images_requests", rate_limit_images_requests), INTEGER_OPTION("rate_limit", "images_interval", rate_limit_images_interval), }; void config_set_defaults(Config *config) { if (!config) { return; } *config = (Config){ .host = DEFAULT_HOST, .port = DEFAULT_PORT, .default_locale = "en_gb", .max_proxy_retries = DEFAULT_MAX_PROXY_RETRIES, .cache_dir = DEFAULT_CACHE_DIR, .cache_ttl_search = DEFAULT_CACHE_TTL_SEARCH, .cache_ttl_infobox = DEFAULT_CACHE_TTL_INFOBOX, .cache_ttl_image = DEFAULT_CACHE_TTL_IMAGE, }; } static char *trim(char *value) { while (*value == ' ' || *value == '\t') { value++; } char *end = value + strlen(value); while (end > value && (end[-1] == ' ' || end[-1] == '\t')) { end--; } *end = '\0'; return value; } static char *unquote(char *value) { size_t length = strlen(value); if (length >= 2 && (value[0] == '"' || value[0] == '\'') && value[length - 1] == value[0]) { value[length - 1] = '\0'; return value + 1; } return value; } static int parse_integer(const char *value, int *result) { char *end; errno = 0; long parsed = strtol(value, &end, 10); if (value == end || *end != '\0' || errno == ERANGE || parsed < INT_MIN || parsed > INT_MAX) { return -1; } *result = (int)parsed; return 0; } static int parse_boolean(const char *value, int *result) { if (strcmp(value, "1") == 0 || strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 || strcasecmp(value, "on") == 0) { *result = 1; return 0; } if (strcmp(value, "0") == 0 || strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0 || strcasecmp(value, "off") == 0) { *result = 0; return 0; } return -1; } static void set_option(Config *config, const char *section, const char *key, const char *value) { size_t option_count = sizeof(CONFIG_OPTIONS) / sizeof(CONFIG_OPTIONS[0]); for (size_t i = 0; i < option_count; i++) { const ConfigOption *option = &CONFIG_OPTIONS[i]; if (strcmp(section, option->section) != 0 || strcmp(key, option->key) != 0) { continue; } void *destination = (char *)config + option->offset; if (option->type == CONFIG_STRING) { snprintf(destination, option->size, "%s", value); } else if (option->type == CONFIG_INTEGER) { parse_integer(value, destination); } else { parse_boolean(value, destination); } return; } } int load_config(const char *filename, Config *config) { if (!filename || !config) { return -1; } FILE *file = fopen(filename, "r"); if (!file) { return -1; } char line[1024]; char section[64] = ""; while (fgets(line, sizeof(line), file)) { line[strcspn(line, "\r\n")] = '\0'; char *content = trim(line); if (*content == '\0' || *content == '#' || *content == ';') { continue; } if (*content == '[') { char *end = strchr(content + 1, ']'); if (end) { *end = '\0'; snprintf(section, sizeof(section), "%s", trim(content + 1)); } continue; } char *delimiter = strchr(content, '='); if (!delimiter) { continue; } *delimiter = '\0'; char *key = trim(content); char *value = unquote(trim(delimiter + 1)); set_option(config, section, key, value); } int result = ferror(file) ? -1 : 0; fclose(file); return result; }