From 4579df9114c00075166b59a67724ec7472facb27 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Sat, 23 Sep 2017 00:16:38 +0100 Subject: Add support for a basic config file --- src/imv.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++---------- src/imv.h | 1 + src/ini.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ini.h | 13 +++++ src/main.c | 5 ++ 5 files changed, 367 insertions(+), 30 deletions(-) create mode 100644 src/ini.c create mode 100644 src/ini.h (limited to 'src') diff --git a/src/imv.c b/src/imv.c index afefce7..db2a9fb 100644 --- a/src/imv.c +++ b/src/imv.c @@ -24,11 +24,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include +#include #include #include #include "commands.h" +#include "ini.h" #include "list.h" #include "loader.h" #include "texture.h" @@ -91,6 +93,11 @@ struct imv { bool ttf_init; }; +enum config_section { + CFG_OPTIONS, + CFG_BINDS +}; + void command_quit(struct imv_list *args, void *data); void command_pan(struct imv_list *args, void *data); void command_select_rel(struct imv_list *args, void *data); @@ -187,12 +194,56 @@ void imv_free(struct imv *imv) free(imv); } +static bool parse_bg(struct imv *imv, const char *bg) +{ + if(strcmp("checks", bg) == 0) { + imv->background_type = BACKGROUND_CHEQUERED; + } else { + imv->background_type = BACKGROUND_SOLID; + if(*bg == '#') + ++bg; + char *ep; + uint32_t n = strtoul(bg, &ep, 16); + if(*ep != '\0' || ep - bg != 6 || n > 0xFFFFFF) { + fprintf(stderr, "Invalid hex color: '%s'\n", bg); + return false; + } + imv->background_color.b = n & 0xFF; + imv->background_color.g = (n >> 8) & 0xFF; + imv->background_color.r = (n >> 16); + } + return true; +} + +static bool parse_slideshow_duration(struct imv *imv, const char *duration) +{ + char *decimal; + imv->slideshow_image_duration = strtoul(duration, &decimal, 10); + imv->slideshow_image_duration *= 1000; + if (*decimal == '.') { + char *ep; + long delay = strtoul(++decimal, &ep, 10); + for (int i = 3 - (ep - decimal); i; i--) { + delay *= 10; + } + if (delay < 1000) { + imv->slideshow_image_duration += delay; + } else { + imv->slideshow_image_duration = ULONG_MAX; + } + } + if (imv->slideshow_image_duration == ULONG_MAX) { + fprintf(stderr, "Wrong slideshow delay '%s'. Aborting.\n", optarg); + return false; + } + return true; +} + bool imv_parse_args(struct imv *imv, int argc, char **argv) { /* Do not print getopt errors */ opterr = 0; - char *argp, *ep = *argv; int o; while((o = getopt(argc, argv, "frasSudxhln:b:e:t:")) != -1) { @@ -226,37 +277,12 @@ bool imv_parse_args(struct imv *imv, int argc, char **argv) imv->quit = true; return true; case 'b': - if(strcmp("checks", optarg) == 0) { - imv->background_type = BACKGROUND_CHEQUERED; - } else { - imv->background_type = BACKGROUND_SOLID; - argp = (*optarg == '#') ? optarg + 1 : optarg; - uint32_t n = strtoul(argp, &ep, 16); - if(*ep != '\0' || ep - argp != 6 || n > 0xFFFFFF) { - fprintf(stderr, "Invalid hex color: '%s'\n", optarg); - return false; - } - imv->background_color.b = n & 0xFF; - imv->background_color.g = (n >> 8) & 0xFF; - imv->background_color.r = (n >> 16); + if(!parse_bg(imv, optarg)) { + return false; } break; case 't': - imv->slideshow_image_duration = strtoul(optarg, &argp, 10); - imv->slideshow_image_duration *= 1000; - if (*argp == '.') { - long delay = strtoul(++argp, &ep, 10); - for (int i = 3 - (ep - argp); i; i--) { - delay *= 10; - } - if (delay < 1000) { - imv->slideshow_image_duration += delay; - } else { - imv->slideshow_image_duration = ULONG_MAX; - } - } - if (imv->slideshow_image_duration == ULONG_MAX) { - fprintf(stderr, "Wrong slideshow delay '%s'. Aborting.\n", optarg); + if(!parse_slideshow_duration(imv, optarg)) { return false; } break; @@ -409,7 +435,7 @@ int imv_run(struct imv *imv) imv_navigator_remove(imv->navigator, err_path); /* special case: the image came from stdin */ - if(strncmp(err_path, "-", 2) == 0) { + if(strcmp(err_path, "-") == 0) { if(imv->stdin_image_data) { free(imv->stdin_image_data); imv->stdin_image_data = NULL; @@ -834,6 +860,110 @@ static void render_window(struct imv *imv) imv->need_redraw = false; } +static char *get_config_path(void) +{ + const char *config_paths[] = { + "$HOME/.imv_config", + "$HOME/.imv/config", + "$XDG_CONFIG_HOME/imv/config", + }; + + for(size_t i = 0; i < sizeof(config_paths) / sizeof(char*); ++i) { + wordexp_t word; + if(wordexp(config_paths[i], &word, 0) == 0) { + char *path = strdup(word.we_wordv[0]); + wordfree(&word); + + if(!path || access(path, R_OK) == -1) { + free(path); + continue; + } + + return path; + } + } + return NULL; +} + +static bool parse_bool(const char *str) +{ + return ( + !strcmp(str, "1") || + !strcmp(str, "yes") || + !strcmp(str, "true") || + !strcmp(str, "on") + ); +} + +bool imv_load_config(struct imv *imv) +{ + const char *path = get_config_path(); + if(!path) { + return true; + } + + FILE *f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Unable to open config file: %s\n", path); + return false; + } + + enum config_section sect = CFG_OPTIONS; + int type; + do { + char key[128], value[128]; + type = parse_ini_file(f, &key[0], sizeof(key), &value[0], sizeof(value)); + + if(type == INI_SECTION) { + if(!strcmp(key, "binds")) { + sect = CFG_BINDS; + } else { + fprintf(stderr, "Unknown config section: %s\n", key); + return false; + } + } else if(type == INI_VALUE) { + if(sect == CFG_OPTIONS) { + if(!strcmp(key, "fullscreen")) { + imv->fullscreen = parse_bool(value); + } else if(!strcmp(key, "overlay")) { + imv->overlay_enabled = parse_bool(value); + } else if(!strcmp(key, "sampling")) { + imv->nearest_neighbour = !strcmp(value, "nearest_neighbour"); + } else if(!strcmp(key, "recursive")) { + imv->recursive_load = parse_bool(value); + } else if(!strcmp(key, "cycle_input")) { + imv->cycle_input = parse_bool(value); + } else if(!strcmp(key, "list_at_exit")) { + imv->list_at_exit = parse_bool(value); + } else if(!strcmp(key, "scaling")) { + if(!strcmp(value, "none")) { + imv->scaling_mode = SCALING_NONE; + } else if(!strcmp(value, "shrink")) { + imv->scaling_mode = SCALING_DOWN; + } else if(!strcmp(value, "full")) { + imv->scaling_mode = SCALING_FULL; + } + } else if(!strcmp(key, "background")) { + if(!parse_bg(imv, value)) { + return false; + } + } else if(!strcmp(key, "slideshow")) { + if(!parse_slideshow_duration(imv, value)) { + return false; + } + } else if(!strcmp(key, "overlay_font")) { + free(imv->font_name); + imv->font_name = strdup(value); + } else { + fprintf(stderr, "Ignoring unknown option: %s\n", key); + } + } + } + } while(type); + + return true; +} + void command_quit(struct imv_list *args, void *data) { (void)args; diff --git a/src/imv.h b/src/imv.h index 7c00bb7..a546b1c 100644 --- a/src/imv.h +++ b/src/imv.h @@ -25,6 +25,7 @@ struct imv; struct imv *imv_create(void); void imv_free(struct imv *imv); +bool imv_load_config(struct imv *imv); bool imv_parse_args(struct imv *imv, int argc, char **argv); void imv_add_path(struct imv *imv, const char *path); diff --git a/src/ini.c b/src/ini.c new file mode 100644 index 0000000..e4e734d --- /dev/null +++ b/src/ini.c @@ -0,0 +1,188 @@ +#include "ini.h" +#include +#include +#include + +enum parse_state { + START, + READING_KEY, + SEEKING_EQ, + SEEKING_VALUE, + READING_VALUE, + READING_SECTION_KEY, + SEEKING_SECTION_VALUE, + SEEKING_END_SECTION, + READING_SECTION_VALUE, + ACCEPT, + REJECT +}; + +int parse_ini_file(FILE* f, char *out_key, size_t key_size, char *out_value, size_t value_size) +{ + char buf[512]; + while(fgets(&buf[0], sizeof(buf), f)) { + int type = parse_ini_str(&buf[0], out_key, key_size, out_value, value_size); + if(type != 0) { + return type; + } + } + return 0; +} + +int parse_ini_str(const char* str, char *out_key, size_t key_size, char *out_value, size_t value_size) +{ + /* ignore comments */ + if(*str == ';') { + out_key[0] = 0; + out_value[0] = 0; + return INI_UNKNOWN; + } + + const char *key_start = 0; + size_t key_len = 0; + const char *value_start = 0; + size_t value_len = 0; + + int type = INI_UNKNOWN; + enum parse_state state = START; + char quote = 0; + char c = 0; + + do { + c = *str; + switch(state) { + case START: + if(c == '[') { + type = INI_SECTION; + state = READING_SECTION_KEY; + } else if(c != '=') { + type = INI_VALUE; + state = READING_KEY; + key_start = str; + ++key_len; + } else { + state = REJECT; + } + break; + case READING_KEY: + if(isspace(c)) { + state = SEEKING_EQ; + } else if(c == '=') { + state = SEEKING_VALUE; + } else { + ++key_len; + } + break; + case SEEKING_EQ: + if(c == '=') { + state = SEEKING_VALUE; + } else if(!isspace(c)) { + state = REJECT; + } + break; + case SEEKING_VALUE: + if(c == '"' || c == '\'') { + if(!value_start) { + quote = c; + state = READING_VALUE; + } else { + state = REJECT; + } + } else if(!isspace(c)) { + value_start = str; + ++value_len; + state = READING_VALUE; + } + break; + case READING_VALUE: + if(quote == 0 && isspace(c)) { + state = ACCEPT; + } else if(quote && c == quote) { + state = ACCEPT; + } else if(c == 0) { + state = ACCEPT; + } else if(c) { + if(!value_start) { + value_start = str; + } + ++value_len; + } else { + state = REJECT; + } + break; + case READING_SECTION_KEY: + if(isspace(c)) { + state = SEEKING_SECTION_VALUE; + } else if(c == ']') { + state = ACCEPT; + } else { + if(!key_start) { + key_start = str; + } + ++key_len; + } + break; + case SEEKING_SECTION_VALUE: + if(c == '"' || c == '\'') { + quote = c; + state = READING_SECTION_VALUE; + } else if(c == ']') { + state = ACCEPT; + } else if(!isspace(c)) { + state = REJECT; + } + break; + case READING_SECTION_VALUE: + if(quote == 0 && isspace(c)) { + state = SEEKING_END_SECTION; + } else if(quote && c == quote) { + state = SEEKING_END_SECTION; + } else if(c == ']') { + state = ACCEPT; + } else if(c) { + if(!value_start) { + value_start = str; + } + ++value_len; + } else { + state = REJECT; + } + break; + case SEEKING_END_SECTION: + if(c == ']') { + state = ACCEPT; + } else if(!isspace(c)) { + state = REJECT; + } + break; + case ACCEPT: + if(c != 0 && !isspace(c)) { + state = REJECT; + } + break; + case REJECT: + return 0; + break; + } + ++str; + } while(c != 0 && state != REJECT); + + if(state == ACCEPT) { + if(key_len >= key_size) { + key_len = key_size - 1; + } + if(value_len >= value_size) { + value_len = value_size - 1; + } + memcpy(out_key, key_start, key_len); + memcpy(out_value, value_start, value_len); + out_key[key_len] = 0; + out_value[value_len] = 0; + return type; + } else { + out_key[0] = 0; + out_value[0] = 0; + } + + return INI_UNKNOWN; +} diff --git a/src/ini.h b/src/ini.h new file mode 100644 index 0000000..12f4228 --- /dev/null +++ b/src/ini.h @@ -0,0 +1,13 @@ +#ifndef INI_H +#define INI_H + +#include + +#define INI_UNKNOWN 0 +#define INI_VALUE 1 +#define INI_SECTION 2 + +int parse_ini_file(FILE* f, char *out_key, size_t key_size, char *out_value, size_t value_size); +int parse_ini_str(const char* str, char *out_key, size_t key_size, char *out_value, size_t value_size); + +#endif diff --git a/src/main.c b/src/main.c index c2eb394..a930308 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,11 @@ int main(int argc, char** argv) return 1; } + if(!imv_load_config(imv)) { + imv_free(imv); + return 1; + } + if(!imv_parse_args(imv, argc, argv)) { imv_free(imv); return 1; -- cgit v1.2.3