diff --git a/src/base/config.c b/src/base/config.c index de0cdb2da51473c2e17b373d91a896f5d29bfae7..7db7087865c948a48b0e2774e7d29ccad5d86ead 100644 --- a/src/base/config.c +++ b/src/base/config.c @@ -3,6 +3,7 @@ #include <lektor/database.h> #include <lektor/net.h> #include <lektor/reg.h> +#include <lektor/lib/strv.h> #include <dlfcn.h> #include <pwd.h> @@ -20,62 +21,57 @@ PRIVATE_FUNCTION int config_new(lkt_db *db, const char *conf); /* Get the default user config file, the ~/.config/lektor/lektor.ini file. */ PRIVATE_FUNCTION void config_default_file(char *dest, size_t len); -static inline char * -strip(char *s) -{ - char *p = s + strlen(s); - while (p > s && isspace(*(--p))) - *p = 0; - return s; -} - -static inline char * -skip(char *s) -{ - while (*s && isspace(*s)) - s++; - return s; -} - PRIVATE_FUNCTION int -handler(lkt_db *user, const char *section, const char *name, const char *value) +handler(lkt_db *user, const char *section, const struct strv name, const struct strv value) { - RETURN_UNLESS(section && name && value, "Skip incomplete line", 1); - RETURN_UNLESS(database_config_set(user, section, name, value), "Failed to update config", 1); + RETURN_UNLESS(section && strv_len(name) && strv_len(value), "Skip incomplete line", 1); + RETURN_UNLESS(database_config_set_strv_1_2(user, section, name, value), + "Failed to update config", 1); return 0; } PRIVATE_FUNCTION void -___set_log_level_internal(const char *level) +___set_log_level_internal(const struct strv level) { - if (!level[0]) { + static UNUSED struct strv error_strv = STRV_STATIC("error"); + static UNUSED struct strv warning_strv = STRV_STATIC("warning"); + static UNUSED struct strv warn_strv = STRV_STATIC("warn"); + static UNUSED struct strv debug_strv = STRV_STATIC("debug"); + static UNUSED struct strv info_strv = STRV_STATIC("info"); + + if (strv_len(level) == 0) { LOG_WARN("CONFIG", "Invalid empty 'log' option"); return; } - if (STR_MATCH(level, "error")) + if (strv_equal_nocase(level, error_strv)) lkt_set_log_level(LOG_LEVEL_ERROR); - else if (STR_MATCH(level, "warn") || STR_MATCH(level, "warning")) + else if (strv_equal_nocase(level, warn_strv)) lkt_set_log_level(LOG_LEVEL_WARN); - else if (STR_MATCH(level, "info")) + else if (strv_equal_nocase(level, warning_strv)) + lkt_set_log_level(LOG_LEVEL_WARN); + else if (strv_equal_nocase(level, info_strv)) lkt_set_log_level(LOG_LEVEL_INFO); - else if (STR_MATCH(level, "debug")) + else if (strv_equal_nocase(level, debug_strv)) lkt_set_log_level(LOG_LEVEL_DEBUG); else - lkt_set_log_level((LOG_LEVEL)(int)strtol(level, NULL, 0)); + lkt_set_log_level((LOG_LEVEL)(int)strv_to_int(level)); LOG_INFO("CONFIG", "Log level set to %d", lkt_get_log_level()); } PRIVATE_FUNCTION void -___set_log_level(const char *name, const char *level) +___set_log_level(const struct strv name, const struct strv level) { + static const struct strv log_strv = STRV_STATIC("log"); + /* Just check the key name here */ - if (!STR_MATCH(name, "log")) { - LOG_WARN("CONFIG", "Invalid option '%s[:=]%s' with no section", name, level); - return; + if (strv_equal_nocase(name, log_strv)) { + ___set_log_level_internal(level); + } else { + LOG_WARN("CONFIG", "Invalid option '" STRV_FMT "=" STRV_FMT "' with no section", + STRV_ARG(name), STRV_ARG(level)); } - ___set_log_level_internal(level); } PRIVATE_FUNCTION void @@ -89,16 +85,21 @@ ___apply_log_level(lkt_db *db) database_config_set(db, "log", "level", loglevel); } else { /* 'log/level' is present, use that one */ - ___set_log_level_internal(loglevel); + ___set_log_level_internal(strv_from_str(loglevel)); } } +PRIVATE_FUNCTION bool +___char_is_not_comment_delim(char x) +{ + return !(x == ';' || x == '#'); +} + PRIVATE_FUNCTION int ini_parse(const char *path, lkt_db *db) { - char *start, *end, *name, *value; - char section[LKT_LINE_MAX], line[LKT_LINE_MAX]; - int error = 0, linenum = 0, len; + char section[LKT_LINE_MAX], file_line[LKT_LINE_MAX]; + int error = 0, linenum = 0; FILE *file = fopen(path, "r"); if (!file) { LOG_ERROR("PARSER", "Failed to open config file '%s'", path); @@ -106,71 +107,76 @@ ini_parse(const char *path, lkt_db *db) } memset(section, 0, LKT_LINE_MAX); - memset(line, 0, LKT_LINE_MAX); + memset(file_line, 0, LKT_LINE_MAX); + + static struct strv comment_prefix_1 = STRV_STATIC(";"); + static struct strv comment_prefix_2 = STRV_STATIC("#"); + static struct strv bracket_left_strv = STRV_STATIC("["); + static struct strv bracket_right_strv = STRV_STATIC("]"); LOG_INFO("CONFIG", "Save config to database from file"); /* Parse the file */ - while (NULL != fgets(line, LKT_LINE_MAX, file)) { + while (NULL != fgets(file_line, LKT_LINE_MAX, file)) { ++linenum; - start = skip(strip(line)); + struct strv line = strv_trim(strv_from_str(file_line)); /* Skip comments */ - if (strspn(start, ";#")) + if (strv_starts_with(line, comment_prefix_1) || strv_starts_with(line, comment_prefix_2)) continue; /* Handle sections */ - else if (start[0] == '[') { - end = &start[1 + strcspn(start + 1, "]")]; - if (end[0] == ']') { - end[0] = '\0'; - len = (int)strlen(&start[1]); - len = MIN(len + 1, LKT_LINE_MAX); - memcpy(section, &start[1], (size_t)len); - section[LKT_LINE_MAX - 1] = '\0'; - } - - else { + else if (strv_starts_with(line, bracket_left_strv)) { + if (strv_ends_with(line, bracket_right_strv)) { + strv_chop_left(&line, 1); + strv_chop_right(&line, 1); + if (!strv_as_str(line, section, LKT_LINE_MAX)) { + error = 1; + LOG_ERROR("PARSER", "Failed to copy the section name at line '%d'", linenum); + } + } else { error = 1; LOG_ERROR("PARSER", "Invalid section name at line '%d'", linenum); } } - /* Handle name[:=]name pair */ - else if (start[0]) { - end = &start[1 + strcspn(start + 1, ":=")]; - if (end[0] == '=' || end[0] == ':') { - end[0] = '\0'; - name = strip(start); - value = &end[1]; - - /* Find a comment */ - end = &value[strcspn(value, ";#")]; - if (end[0]) - end[0] = '\0'; - - /* Skip all spaces */ - value = skip(value); - strip(value); - - /* Handle the SECTION, NAME[:=]VALUE - The only option that has no SECTION is the log level: - log[:=]ERROR|WARN|INFO|DEBUG|\d+ */ - if (section[0]) { - if (handler(db, section, name, value)) { - error = 1; - LOG_ERROR("PARSER", "Failed to '[handle] %s, %s{:,=}%s' at line '%d'", - section, name, value, linenum); - } - } else { - /* Keep for legacy reasons */ - ___set_log_level(name, value); + /* Handle empty lines */ + else if (strv_len(line) == 0) { + continue; + } + + /* Handle name=name pair */ + else { + int separator_index = -1; + if (!strv_index_of(line, '=', &separator_index)) { + LOG_ERROR("PARSER", "Invalid line '%d', no (key, value) pair found", linenum); + continue; + } + + const struct strv key = strv_trim(strv_chop_left(&line, separator_index)); + strv_chop_left(&line, 1); /* Skip the "=" character */ + const struct strv value = strv_trim(strv_take_left_while(line, ___char_is_not_comment_delim)); + + /* Handle the (key, value) pair */ + if (strv_len(value) == 0 || strv_len(key) == 0) { + LOG_ERROR("PARSER", "Invalid key pair at line '%d'", linenum); + LOG_ERROR_IF(strv_len(key), "PARSER", "The key was: '"STRV_FMT"'", STRV_ARG(key)); + LOG_ERROR_IF(strv_len(value), "PARSER", "The value was: '"STRV_FMT"'", STRV_ARG(value)); + } + + else if (section[0]) { + LOG_DEBUG("PARSER", "Found line: [%s] '"STRV_FMT"' = '"STRV_FMT"'", section, STRV_ARG(key), STRV_ARG(value)); + if (handler(db, section, key, value)) { + error = 1; + LOG_ERROR("PARSER", + "Failed to '[handle] %s, " STRV_FMT "=" STRV_FMT "' at line '%d'", + section, STRV_ARG(key), STRV_ARG(value), linenum); } } else { - error = 1; - LOG_ERROR("PARSER", "Invalid name[:=]value pair at line '%d'", linenum); + /* Keep for legacy reasons */ + ___set_log_level(key, value); } } } @@ -305,7 +311,7 @@ config_default(FILE *output) /* Recursive mkdir, where the last word of the string is a file, not a folder. */ PRIVATE_FUNCTION void -__mkdir(const char *dir) +___mkdir(const char *dir) { char tmp[PATH_MAX]; char *p = NULL; @@ -336,7 +342,7 @@ retry_config: LOG_INFO("INIT", "Creating default config file"); config_default_file(conf_file, conf_len); - __mkdir(conf_file); /* Create the folder for the file. */ + ___mkdir(conf_file); /* Create the folder for the file. */ FILE *file_desc = fopen(conf_file, "w+"); RETURN_UNLESS(file_desc, "Failed to open default config file to initialize it", 1);