aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Jeffery <harry@exec64.co.uk>2017-09-23 00:16:38 +0100
committerHarry Jeffery <harry@exec64.co.uk>2017-11-23 21:34:11 +0000
commit4579df9114c00075166b59a67724ec7472facb27 (patch)
tree0da6d65a35864d031ed2aa4421e20bdb4ba95db2
parentc51dff6a4e5bd21491bb0c085cb08f35275332a0 (diff)
downloadimv-4579df9114c00075166b59a67724ec7472facb27.tar.gz
Add support for a basic config file
-rw-r--r--src/imv.c190
-rw-r--r--src/imv.h1
-rw-r--r--src/ini.c188
-rw-r--r--src/ini.h13
-rw-r--r--src/main.c5
5 files changed, 367 insertions, 30 deletions
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 <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
+#include <wordexp.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 <stdio.h>
+
+#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;