From 5ec584f35dad8646d9ac3802379455dacda17be1 Mon Sep 17 00:00:00 2001 From: Jarcode Date: Tue, 6 Feb 2018 19:06:30 -0800 Subject: [PATCH 1/9] Updated version --- glava.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glava.c b/glava.c index f8815f1..4cf9f61 100644 --- a/glava.c +++ b/glava.c @@ -18,7 +18,7 @@ #include "render.h" #include "xwin.h" -#define GLAVA_VERSION "1.1" +#define GLAVA_VERSION "1.2" #ifdef GLAD_DEBUG #define GLAVA_RELEASE_TYPE_PREFIX "debug, " #else From 5e813e25a9014880ce26e6ce4d5e69362237987a Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 16:41:12 -0800 Subject: [PATCH 2/9] Changed position of 'glfwShowWindow' call, related to #17 --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 21822e0..fe85d9d 100644 --- a/render.c +++ b/render.c @@ -1219,8 +1219,6 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force } overlay(&gl->overlay); - - glfwShowWindow(gl->w); glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a); @@ -1233,6 +1231,8 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force xwin_addstate(r, xwinstates[t]); } free(xwinstates); + + glfwShowWindow(gl->w); return r; } From ab55156826cd230fcf2201eff8b7b157737a6dfa Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 16:58:39 -0800 Subject: [PATCH 3/9] Set window types and state ealier during initialization, fixed #17 --- render.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/render.c b/render.c index fe85d9d..bacc209 100644 --- a/render.c +++ b/render.c @@ -1055,6 +1055,16 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force abort(); } + if (xwintype) { + xwin_settype(r, xwintype); + free(xwintype); + } + + for (size_t t = 0; t < xwinstates_sz; ++t) { + xwin_addstate(r, xwinstates[t]); + } + free(xwinstates); + glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]); glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]); @@ -1086,7 +1096,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force struct gl_sfbo* stages; size_t count = 0; - + { char buf[32]; DIR* dir = opendir(shaders); @@ -1221,16 +1231,6 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force overlay(&gl->overlay); glClearColor(gl->clear_color.r, gl->clear_color.g, gl->clear_color.b, gl->clear_color.a); - - if (xwintype) { - xwin_settype(r, xwintype); - free(xwintype); - } - - for (size_t t = 0; t < xwinstates_sz; ++t) { - xwin_addstate(r, xwinstates[t]); - } - free(xwinstates); glfwShowWindow(gl->w); From f539f1813578313785156a65b210fa5c33d2abd2 Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 17:12:45 -0800 Subject: [PATCH 4/9] Always define GLFW_EXPOSE_NATIVE_GLX --- xwin.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/xwin.c b/xwin.c index 3f81118..95d690f 100644 --- a/xwin.c +++ b/xwin.c @@ -18,9 +18,7 @@ #define GLFW_EXPOSE_NATIVE_X11 /* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but the old headers require one of them to be selected for exposure in glfw3native.h. */ -#if (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1) #define GLFW_EXPOSE_NATIVE_GLX -#endif #include #include #include From 8c3a404f3763b6914a160b624ccce833d0934635 Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 17:17:58 -0800 Subject: [PATCH 5/9] fixed include order in xwin.c, fixes #13 --- xwin.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/xwin.c b/xwin.c index 95d690f..97e7164 100644 --- a/xwin.c +++ b/xwin.c @@ -16,11 +16,16 @@ #include #define GLFW_EXPOSE_NATIVE_X11 -/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but - the old headers require one of them to be selected for exposure in glfw3native.h. */ -#define GLFW_EXPOSE_NATIVE_GLX + #include #include + +/* Hack to make GLFW 3.1 headers work with GLava. We don't use the context APIs from GLFW, but + the old headers require one of them to be selected for exposure in glfw3native.h. */ +#if GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR <= 1 +#define GLFW_EXPOSE_NATIVE_GLX +#error "GLX defined" +#endif #include #include "render.h" From 79d391938dcc0d65b5c130938ebb5a7327c99655 Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 17:46:34 -0800 Subject: [PATCH 6/9] moved hex color parsing to ext_parse_color, removed debug message --- glsl_ext.c | 39 ++++++++++++++++++++++++++++++++++++++- glsl_ext.h | 1 + render.c | 39 +++------------------------------------ 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/glsl_ext.c b/glsl_ext.c index e765b03..6c890ef 100644 --- a/glsl_ext.c +++ b/glsl_ext.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,42 @@ struct schar { size_t sz; }; +bool ext_parse_color(const char* str, size_t elem_sz, float** results) { + size_t t, len = strlen(str), i = 0, s = 0; + uint8_t elem_bytes[elem_sz]; + /* Ignore '0x' prefix, if present */ + if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + len -= 2; + str += 2; + } + for (t = 0; t < len && t < 8; ++t) { + char c = str[t]; + uint8_t b; + /* obtain value from character */ + switch (c) { + case 'a' ... 'f': b = (c - 'a') + 10; break; + case 'A' ... 'F': b = (c - 'A') + 10; break; + case '0' ... '9': b = c - '0'; break; + default: return false; + } + elem_bytes[s] = b; + if (s >= elem_sz - 1) { /* advance to next element */ + uint32_t e = 0; /* component storage */ + /* mask storage with input data */ + for (size_t v = 0; v < elem_sz; ++v) { + e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4); + } + /* convert to [0, 1] as floating point value */ + *results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1); + s = 0; + ++i; + } else { /* advance character */ + ++s; + } + } + return true; +} + /* handle raw arguments for #include and #request directives */ /* NOTE: munmap needs to be called on the result */ static struct schar directive(struct glsl_ext* ext, char** args, @@ -128,7 +165,7 @@ static struct schar directive(struct glsl_ext* ext, char** args, struct request_handler* handler; bool found = false; - size_t t, i; + size_t t; for (t = 0; (handler = &ext->handlers[t])->name != NULL; ++t) { if(!strcmp(handler->name, request)) { found = true; diff --git a/glsl_ext.h b/glsl_ext.h index fa9fc09..9b13338 100644 --- a/glsl_ext.h +++ b/glsl_ext.h @@ -35,3 +35,4 @@ struct glsl_ext { void ext_process(struct glsl_ext* ext, const char* f); void ext_free (struct glsl_ext* ext); +bool ext_parse_color(const char* hex, size_t elem_sz, float** results); diff --git a/render.c b/render.c index bacc209..9d51d08 100644 --- a/render.c +++ b/render.c @@ -783,42 +783,9 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force &gl->clear_color.b, &gl->clear_color.a }; - const size_t elem_sz = 2; - char* str = (char*) args[0]; - size_t t, len = strlen(str), i = 0, s = 0; - uint8_t elem_bytes[elem_sz]; - /* Ignore '0x' prefix, if present */ - if (len >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { - len -= 2; - str += 2; - } - for (t = 0; t < len && t < 8; ++t) { - char c = str[t]; - uint8_t b; - /* obtain value from character */ - switch (c) { - case 'a' ... 'f': b = (c - 'a') + 10; break; - case 'A' ... 'F': b = (c - 'A') + 10; break; - case '0' ... '9': b = c - '0'; break; - default: - fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]); - exit(EXIT_FAILURE); - } - elem_bytes[s] = b; - if (s >= elem_sz - 1) { /* advance to next element */ - uint32_t e = 0; /* component storage */ - /* mask storage with input data */ - for (size_t v = 0; v < elem_sz; ++v) { - e |= (uint32_t) elem_bytes[v] << (((elem_sz - 1) - v) * 4); - } - /* convert to [0, 1] as floating point value */ - *results[i] = (float) e / (float) ((1 << (elem_sz * 4)) - 1); - printf("[DEBUG] component %d value: %d (float: %f)\n", i, e, *results[i]); - s = 0; - ++i; - } else { /* advance character */ - ++s; - } + if (!ext_parse_color((char*) args[0], 2, results)) { + fprintf(stderr, "Invalid value for `setbg` request: '%s'\n", (char*) args[0]); + exit(EXIT_FAILURE); } }) }, From aea30abf672ed724951477bbe4904c24bd732bcc Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 19:29:56 -0800 Subject: [PATCH 7/9] added RGBA hex color parsing for GLSL, fixed alpha blending bug --- glsl_ext.c | 124 ++++++++++++++++++++++++++++++++++++-------- render.c | 4 +- shaders/bars.glsl | 4 +- shaders/circle.glsl | 2 +- shaders/graph.glsl | 2 +- shaders/radial.glsl | 6 +-- 6 files changed, 112 insertions(+), 30 deletions(-) diff --git a/glsl_ext.c b/glsl_ext.c index 6c890ef..82a7830 100644 --- a/glsl_ext.c +++ b/glsl_ext.c @@ -52,7 +52,7 @@ static void se_append(struct sbuf* sbuf, size_t elen, const char* fmt, ...) { int written; if ((written = vsnprintf(sbuf->buf + sbuf->at, space, fmt, args)) < 0) abort(); - sbuf->at += space; + sbuf->at += written; va_end(args); } @@ -128,7 +128,8 @@ static struct schar directive(struct glsl_ext* ext, char** args, int fd = open(path, O_RDONLY); if (fd == -1) { - parse_error(line, f, "failed to load GLSL shader source specified by #include directive '%s': %s\n", + parse_error(line, f, "failed to load GLSL shader source " + "specified by #include directive '%s': %s\n", path, strerror(errno)); } @@ -137,7 +138,8 @@ static struct schar directive(struct glsl_ext* ext, char** args, char* map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (!map) { - parse_error(line, f, "failed to map GLSL shader source specified by #include directive '%s': %s\n", + parse_error(line, f, "failed to map GLSL shader source " + "specified by #include directive '%s': %s\n", path, strerror(errno)); } @@ -210,10 +212,12 @@ static struct schar directive(struct glsl_ext* ext, char** args, case '1': { v = true; break; } case '0': { v = false; break; } default: - parse_error_s(line, f, "tried to parse invalid raw string into a boolean"); + parse_error_s(line, f, + "tried to parse invalid raw string into a boolean"); } } else { - parse_error_s(line, f, "tried to parse invalid raw string into a boolean"); + parse_error_s(line, f, + "tried to parse invalid raw string into a boolean"); } processed_args[i] = malloc(sizeof(bool)); *(bool*) processed_args[i] = v; @@ -248,10 +252,11 @@ static struct schar directive(struct glsl_ext* ext, char** args, void ext_process(struct glsl_ext* ext, const char* f) { #define LINE_START 0 - #define IGNORING 1 + #define GLSL 1 #define MACRO 2 #define REQUEST 3 #define INCLUDE 4 + #define COLOR 5 struct sbuf sbuf = { .buf = malloc(256), @@ -263,12 +268,15 @@ void ext_process(struct glsl_ext* ext, const char* f) { size_t t; char at; int state = LINE_START; - size_t macro_start_idx, arg_start_idx; + size_t macro_start_idx, arg_start_idx, cbuf_idx; size_t line = 1; bool quoted = false, arg_start; + char cbuf[9]; char** args = NULL; size_t args_sz = 0; - + + bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false; + for (t = 0; t <= source_len; ++t) { at = source_len == t ? '\0' : ext->source[t]; if (at == '\n') @@ -281,40 +289,114 @@ void ext_process(struct glsl_ext* ext, const char* f) { macro_start_idx = t; state = MACRO; continue; } - case ' ': - case '\t': case '\n': + if (comment && comment_line) { + comment = false; + comment_line = false; + } + case '\t': + case ' ': goto copy; - default: { - state = IGNORING; - goto copy; } + default: state = GLSL; + /* let execution continue into next state */ } } - case IGNORING: /* copying GLSL source or unrelated preprocessor syntax */ - if (at == '\n') { - state = LINE_START; + case GLSL: /* copying GLSL source or unrelated preprocessor syntax */ + { + switch (at) { + case '/': + if (!comment) { + if (prev_slash) { + comment = true; + comment_line = true; + prev_slash = false; + } else prev_slash = true; + } else if (!comment_line) { + if (prev_asterix) { + comment = false; + prev_asterix = false; + } + } + goto copy; + case '*': + if (!comment) { + if (prev_slash) { + comment = true; + prev_slash = false; + } + } else prev_asterix = true; + goto copy; + case '#': { + /* handle hex color syntax */ + if (!comment) { + state = COLOR; + cbuf_idx = 0; + continue; + } else goto normal_char; + } + case '\n': + if (comment && comment_line) { + comment = false; + comment_line = false; + } + state = LINE_START; + normal_char: + default: + prev_asterix = false; + prev_slash = false; + goto copy; + } + } + case COLOR: /* parse hex color syntax (#ffffffff -> vec4(1.0, 1.0, 1.0, 1.0)) */ + { + switch (at) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': { + cbuf[cbuf_idx] = at; + ++cbuf_idx; + if (cbuf_idx >= 8) + goto emit_color; + else continue; + } + emit_color: + default: + cbuf[cbuf_idx] = '\0'; /* null terminate */ + float r = 0.0F, g = 0.0F, b = 0.0F, a = 1.0F; + if (ext_parse_color(cbuf, 2, (float*[]) { &r, &g, &b, &a })) { + se_append(&sbuf, 64, " vec4(%.6f, %.6f, %.6f, %.6f) ", r, g, b, a); + } else { + parse_error(line, f, "Invalid color format '#%s' while " + "parsing GLSL color syntax extension", cbuf); + } + state = at == '\n' ? LINE_START : GLSL; + if (cbuf_idx >= 8) + continue; + else goto copy; /* copy character if it ended the sequence */ + } } - goto copy; case MACRO: /* processing start of macro */ { switch (at) { case '\n': case ' ': case '\t': - case '\0': + case '\0': { /* end parsing directive */ if (!strncmp("#request", &ext->source[macro_start_idx], t - macro_start_idx) || !strncmp("#REQUEST", &ext->source[macro_start_idx], t - macro_start_idx)) { state = REQUEST; goto prepare_arg_parse; - } else if (!strncmp("#include", &ext->source[macro_start_idx], t - macro_start_idx) - || !strncmp("#INCLUDE", &ext->source[macro_start_idx], t - macro_start_idx)) { + } else if (!strncmp("#include", &ext->source[macro_start_idx], + t - macro_start_idx) + || !strncmp("#INCLUDE", &ext->source[macro_start_idx], + t - macro_start_idx)) { state = INCLUDE; goto prepare_arg_parse; } else { n_append(&sbuf, t - macro_start_idx, &ext->source[macro_start_idx]); - state = IGNORING; + state = at == '\n' ? LINE_START : GLSL; goto copy; } prepare_arg_parse: diff --git a/render.c b/render.c index 9d51d08..5ad31bd 100644 --- a/render.c +++ b/render.c @@ -753,10 +753,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force #ifdef GLFW_TRANSPARENT_FRAMEBUFFER glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE); - gl->use_alpha = false; + if (native_opacity) gl->use_alpha = false; #elif GLFW_TRANSPARENT glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE); - gl->use_alpha = false; + if (native_opacity) gl->use_alpha = false; #else if (native_opacity) printf("WARNING: the linked version of GLFW3 does not have transparency support" diff --git a/shaders/bars.glsl b/shaders/bars.glsl index 29b179c..c0ba8d8 100644 --- a/shaders/bars.glsl +++ b/shaders/bars.glsl @@ -7,13 +7,13 @@ /* width (in pixels) of each bar gap */ #define BAR_GAP 2 /* outline color */ -#define BAR_OUTLINE vec4(0.15, 0.15, 0.15, 1) +#define BAR_OUTLINE #262626 /* outline width (in pixels, set to 0 to disable outline drawing) */ #define BAR_OUTLINE_WIDTH 0 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 300 /* Bar color */ -#define COLOR (vec4(0.2, 0.4, 0.7, 1) * ((d / 60) + 1)) +#define COLOR (#3366b2 * ((d / 60) + 1)) /* Direction that the bars are facing, 0 for inward, 1 for outward */ #define DIRECTION 0 /* Whether to switch left/right audio buffers */ diff --git a/shaders/circle.glsl b/shaders/circle.glsl index ea4ffba..6be8473 100644 --- a/shaders/circle.glsl +++ b/shaders/circle.glsl @@ -3,7 +3,7 @@ /* center line thickness (pixels) */ #define C_LINE 2 /* outline color */ -#define OUTLINE vec4(0.20, 0.20, 0.20, 1) +#define OUTLINE #333333 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 150 /* Angle (in radians) for how much to rotate the visualizer */ diff --git a/shaders/graph.glsl b/shaders/graph.glsl index 113f48c..8faf0c3 100644 --- a/shaders/graph.glsl +++ b/shaders/graph.glsl @@ -17,7 +17,7 @@ /* actual color definition */ #define COLOR vec4((0.3 + RCOL_OFF) + LSTEP, 0.6 - LSTEP, (0.3 + LCOL_OFF) + LSTEP, 1) /* outline color */ -#define OUTLINE vec4(0.15, 0.15, 0.15, 1) +#define OUTLINE #262626 /* 1 to invert (vertically), 0 otherwise */ #define INVERT 0 diff --git a/shaders/radial.glsl b/shaders/radial.glsl index 1f66a7c..3d3269b 100644 --- a/shaders/radial.glsl +++ b/shaders/radial.glsl @@ -4,7 +4,7 @@ /* center line thickness (pixels) */ #define C_LINE 2 /* outline color */ -#define OUTLINE vec4(0.20, 0.20, 0.20, 1) +#define OUTLINE #333333 /* number of bars (use even values for best results) */ #define NBARS 180 /* width (in pixels) of each bar*/ @@ -15,8 +15,8 @@ #define BAR_OUTLINE_WIDTH 0 /* Amplify magnitude of the results each bar displays */ #define AMPLIFY 300 -/* Bar color */ -#define COLOR (vec4(0.8, 0.2, 0.2, 1) * ((d / 40) + 1)) +/* Bar color */ +#define COLOR (#cc3333 * ((d / 40) + 1)) /* Angle (in radians) for how much to rotate the visualizer */ #define ROTATE (PI / 2) /* Whether to switch left/right audio buffers */ From ec6d4d0c37c4e3a8b86750cc1aa2fcadfa1d944e Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 19:53:23 -0800 Subject: [PATCH 8/9] exempted glsl color syntax from string contents --- glsl_ext.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/glsl_ext.c b/glsl_ext.c index 82a7830..5710e40 100644 --- a/glsl_ext.c +++ b/glsl_ext.c @@ -275,7 +275,8 @@ void ext_process(struct glsl_ext* ext, const char* f) { char** args = NULL; size_t args_sz = 0; - bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false; + bool prev_slash = false, comment = false, comment_line = false, prev_asterix = false, + prev_escape = false, string = false; for (t = 0; t <= source_len; ++t) { at = source_len == t ? '\0' : ext->source[t]; @@ -304,6 +305,17 @@ void ext_process(struct glsl_ext* ext, const char* f) { case GLSL: /* copying GLSL source or unrelated preprocessor syntax */ { switch (at) { + case '"': + if (!comment && !prev_escape) + string = !string; + goto normal_char; + case '\\': + if (!comment) { + prev_escape = !prev_escape; + prev_asterix = false; + prev_slash = false; + goto copy; + } else goto normal_char; case '/': if (!comment) { if (prev_slash) { @@ -317,6 +329,7 @@ void ext_process(struct glsl_ext* ext, const char* f) { prev_asterix = false; } } + prev_escape = false; goto copy; case '*': if (!comment) { @@ -325,10 +338,11 @@ void ext_process(struct glsl_ext* ext, const char* f) { prev_slash = false; } } else prev_asterix = true; + prev_escape = false; goto copy; case '#': { /* handle hex color syntax */ - if (!comment) { + if (!comment && !string) { state = COLOR; cbuf_idx = 0; continue; @@ -344,6 +358,7 @@ void ext_process(struct glsl_ext* ext, const char* f) { default: prev_asterix = false; prev_slash = false; + prev_escape = false; goto copy; } } From d82762a47174a69d0f0160df656b4cfaf67356ac Mon Sep 17 00:00:00 2001 From: Jarcode Date: Wed, 7 Feb 2018 20:02:24 -0800 Subject: [PATCH 9/9] changed version, added -V (--version) flag --- glava.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/glava.c b/glava.c index 4cf9f61..c67391d 100644 --- a/glava.c +++ b/glava.c @@ -18,7 +18,7 @@ #include "render.h" #include "xwin.h" -#define GLAVA_VERSION "1.2" +#define GLAVA_VERSION "1.3" #ifdef GLAD_DEBUG #define GLAVA_RELEASE_TYPE_PREFIX "debug, " #else @@ -151,6 +151,8 @@ static void copy_cfg(const char* path, const char* dest, bool verbose) { closedir(dir); } +#define GLAVA_VERSION_STRING "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")" + static const char* help_str = "Usage: %s [OPTIONS]...\n" "Opens a window with an OpenGL context to draw an audio visualizer.\n" @@ -165,17 +167,19 @@ static const char* help_str = "-C, --copy-config creates copies and symbolic links in the user configuration\n" " directory for glava, copying any files in the root directory\n" " of the installed shader directory, and linking any modules.\n" + "-V, --version print application version and exit\n" "\n" - "GLava (glava) " GLAVA_VERSION " (" GLAVA_RELEASE_TYPE ")\n" + GLAVA_VERSION_STRING "\n" " -- Copyright (C) 2017 Levi Webb\n"; -static const char* opt_str = "hve:Cm:"; +static const char* opt_str = "hvVe:Cm:"; static struct option p_opts[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"entry", required_argument, 0, 'e'}, {"force-mod", required_argument, 0, 'm'}, {"copy-config", no_argument, 0, 'C'}, + {"version", no_argument, 0, 'V'}, {0, 0, 0, 0 } }; @@ -198,10 +202,15 @@ int main(int argc, char** argv) { case 'e': entry = optarg; break; case 'm': force = optarg; break; case '?': exit(EXIT_FAILURE); break; + case 'V': + puts(GLAVA_VERSION_STRING); + exit(EXIT_SUCCESS); + break; default: case 'h': printf(help_str, argc > 0 ? argv[0] : "glava"); exit(EXIT_SUCCESS); + break; } }