xglcomp
changeset 0:d9b3fba68705
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 21 Jan 2016 08:45:31 +0200 |
parents | |
children | b2b7cb950c28 |
files | .hgignore Makefile src/cwin.cc src/cwin.h src/logger.c src/logger.h src/main.cc src/optcfg/optcfg.c src/optcfg/optcfg.h |
diffstat | 9 files changed, 923 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Thu Jan 21 08:45:31 2016 +0200 1.3 @@ -0,0 +1,4 @@ 1.4 +\.o$ 1.5 +\.d$ 1.6 +\.swp$ 1.7 +^xglcomp$
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Thu Jan 21 08:45:31 2016 +0200 2.3 @@ -0,0 +1,44 @@ 2.4 +PREFIX = /usr/local 2.5 + 2.6 +ccsrc = $(wildcard src/*.cc) 2.7 +csrc = $(wildcard src/*.c) $(wildcard src/optcfg/*.c) 2.8 +obj = $(csrc:.c=.o) $(ccsrc:.cc=.o) 2.9 +dep = $(obj:.o=.d) 2.10 +bin = xglcomp 2.11 + 2.12 +warn = -pedantic -Wall 2.13 +dbg = -g 2.14 +opt = -O0 2.15 +incpaths = -Isrc/optcfg 2.16 + 2.17 +CFLAGS = $(warn) $(dbg) $(opt) $(incpaths) 2.18 +CXXFLAGS = $(warn) $(dbg) $(opt) $(incpaths) 2.19 +LDFLAGS = -lGL -lGLEW -lX11 -lXext -lXcomposite -lm 2.20 + 2.21 +$(bin): $(obj) 2.22 + $(CXX) -o $@ $(obj) $(LDFLAGS) 2.23 + 2.24 +-include $(dep) 2.25 + 2.26 +%.d: %.c 2.27 + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.28 + 2.29 +%.d: %.cc 2.30 + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.31 + 2.32 +.PHONY: clean 2.33 +clean: 2.34 + rm -f $(obj) $(bin) 2.35 + 2.36 +.PHONY: cleandep 2.37 +cleandep: 2.38 + rm -f $(dep) 2.39 + 2.40 +.PHONY: install 2.41 +install: $(bin) 2.42 + mkdir -p $(DESTDIR)$(PREFIX)/bin 2.43 + cp $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin) 2.44 + 2.45 +.PHONY: uninstall 2.46 +uninstall: 2.47 + rm -f $(DESTDIR)$(PREFIX)/bin/$(bin)
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/cwin.cc Thu Jan 21 08:45:31 2016 +0200 3.3 @@ -0,0 +1,17 @@ 3.4 +#include <vector> 3.5 +#include "cwin.h" 3.6 +#include "logger.h" 3.7 + 3.8 +static std::vector<CompWindow> cwinlist; 3.9 + 3.10 +void add_window(CompWindow *cwin) 3.11 +{ 3.12 + if(have_window(cwin)) { 3.13 + log_warning("add_window trying to add duplicate, ignoring\n"); 3.14 + return; 3.15 + } 3.16 + 3.17 + cwinlist.push_back(cwin); 3.18 +} 3.19 + 3.20 +void delete_window(
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/cwin.h Thu Jan 21 08:45:31 2016 +0200 4.3 @@ -0,0 +1,21 @@ 4.4 +#ifndef COMP_WIN_H_ 4.5 +#define COMP_WIN_H_ 4.6 + 4.7 +#include <X11/Xlib.h> 4.8 + 4.9 +struct CompWindow { 4.10 + Window xwin; 4.11 + Pixmap xpixmap; 4.12 + 4.13 + struct CompWindow *next, *prev; 4.14 +}; 4.15 + 4.16 +void add_window(CompWindow *cwin); 4.17 +void delete_window(CompWindow *cwin); 4.18 +bool have_window(CompWindow *cwin); 4.19 +CompWindow *find_window_xid(Window xid); 4.20 + 4.21 +int get_window_count(); 4.22 +CompWindow *get_window(int idx); 4.23 + 4.24 +#endif // COMP_WIN_H_
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/logger.c Thu Jan 21 08:45:31 2016 +0200 5.3 @@ -0,0 +1,351 @@ 5.4 +#include <stdio.h> 5.5 +#include <stdlib.h> 5.6 +#include <string.h> 5.7 +#include <errno.h> 5.8 +#include <stdarg.h> 5.9 +#include "logger.h" 5.10 + 5.11 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 5.12 +#include <unistd.h> 5.13 +#include <fcntl.h> 5.14 +#include <pthread.h> 5.15 +#include <sys/select.h> 5.16 +#endif 5.17 + 5.18 +enum targ_type { TARG_STREAM, TARG_FILE, TARG_FUNC }; 5.19 + 5.20 +struct log_target { 5.21 + unsigned int msg_type; 5.22 + enum targ_type targ_type; 5.23 + FILE *fp; 5.24 + void (*func)(const char*, void*); 5.25 + void *func_cls; 5.26 + 5.27 + struct log_target *next; 5.28 +}; 5.29 + 5.30 +static struct log_target *targ_list = (void*)1; /* uninitialized */ 5.31 + 5.32 +static void init_once(void) 5.33 +{ 5.34 + if(targ_list == (void*)1) { 5.35 + /* perform one-time init */ 5.36 + targ_list = 0; 5.37 + log_add_stream(LOG_INFO | LOG_DEBUG, stdout); 5.38 + log_add_stream(LOG_WARNING | LOG_ERROR, stderr); 5.39 + } 5.40 +} 5.41 + 5.42 +static void log_string(unsigned int type, const char *str); 5.43 +static int typecolor(int type); 5.44 + 5.45 +void log_clear_targets(void) 5.46 +{ 5.47 + init_once(); 5.48 + 5.49 + while(targ_list) { 5.50 + struct log_target *tmp = targ_list; 5.51 + targ_list = targ_list->next; 5.52 + 5.53 + if(tmp->targ_type == TARG_FILE) { 5.54 + fclose(tmp->fp); 5.55 + } 5.56 + free(tmp); 5.57 + } 5.58 + targ_list = 0; 5.59 +} 5.60 + 5.61 +int log_add_stream(unsigned int type_mask, FILE *fp) 5.62 +{ 5.63 + struct log_target *targ; 5.64 + 5.65 + init_once(); 5.66 + 5.67 + if(!(targ = malloc(sizeof *targ))) { 5.68 + perror("failed to allocate memory for log target"); 5.69 + return -1; 5.70 + } 5.71 + targ->msg_type = type_mask; 5.72 + targ->targ_type = TARG_STREAM; 5.73 + targ->fp = fp; 5.74 + targ->next = targ_list; 5.75 + targ_list = targ; 5.76 + return 0; 5.77 +} 5.78 + 5.79 +int log_add_file(unsigned int type_mask, const char *fname) 5.80 +{ 5.81 + FILE *fp; 5.82 + struct log_target *targ; 5.83 + 5.84 + init_once(); 5.85 + 5.86 + if(!(fp = fopen(fname, "w"))) { 5.87 + fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno)); 5.88 + return -1; 5.89 + } 5.90 + if(type_mask & (LOG_ERROR | LOG_DEBUG)) { 5.91 + /* make error and debug logs unbuffered */ 5.92 + setvbuf(fp, 0, _IONBF, 0); 5.93 + } 5.94 + if(!(targ = malloc(sizeof *targ))) { 5.95 + perror("failed to allocate memory for log target"); 5.96 + fclose(fp); 5.97 + return -1; 5.98 + } 5.99 + targ->msg_type = type_mask; 5.100 + targ->targ_type = TARG_FILE; 5.101 + targ->fp = fp; 5.102 + targ->next = targ_list; 5.103 + targ_list = targ; 5.104 + return 0; 5.105 +} 5.106 + 5.107 +int log_add_func(unsigned int type_mask, void (*func)(const char*, void*), void *cls) 5.108 +{ 5.109 + struct log_target *targ; 5.110 + 5.111 + init_once(); 5.112 + 5.113 + if(!(targ = malloc(sizeof *targ))) { 5.114 + perror("failed to allocate memory for log target"); 5.115 + return -1; 5.116 + } 5.117 + targ->msg_type = type_mask; 5.118 + targ->targ_type = TARG_FUNC; 5.119 + targ->func = func; 5.120 + targ->func_cls = cls; 5.121 + targ->next = targ_list; 5.122 + targ_list = targ; 5.123 + return 0; 5.124 +} 5.125 + 5.126 + 5.127 +void log_va_msg(unsigned int type, const char *fmt, va_list ap) 5.128 +{ 5.129 + char fixedbuf[256]; 5.130 + char *buf = fixedbuf; 5.131 + int len, sz = sizeof fixedbuf; 5.132 + 5.133 + init_once(); 5.134 + 5.135 + if(!targ_list || !*fmt) return; /* don't waste our time */ 5.136 + 5.137 + /* try with the fixed size buffer first which should be sufficient most of the 5.138 + * time. if this fails, allocate a buffer of the correct size. 5.139 + */ 5.140 + while((len = vsnprintf(buf, sz, fmt, ap)) >= sz || len < 0) { 5.141 + sz = len >= 0 ? len + 1 : sz * 2; 5.142 + if(buf != fixedbuf) 5.143 + free(buf); 5.144 + if(!(buf = malloc(sz))) { 5.145 + return; 5.146 + } 5.147 + } 5.148 + 5.149 + if(buf != fixedbuf) 5.150 + free(buf); 5.151 + 5.152 + log_string(type, buf); 5.153 +} 5.154 + 5.155 +/* helpers */ 5.156 +#define LOG_VA_MSG(t) \ 5.157 + do { \ 5.158 + va_list ap; \ 5.159 + va_start(ap, fmt); \ 5.160 + log_va_msg(t, fmt, ap); \ 5.161 + va_end(ap); \ 5.162 + } while(0) 5.163 + 5.164 +void log_msg(unsigned int type, const char *fmt, ...) 5.165 +{ 5.166 + LOG_VA_MSG(type); 5.167 +} 5.168 + 5.169 +void log_info(const char *fmt, ...) 5.170 +{ 5.171 + LOG_VA_MSG(LOG_INFO); 5.172 +} 5.173 + 5.174 +void log_warning(const char *fmt, ...) 5.175 +{ 5.176 + LOG_VA_MSG(LOG_WARNING); 5.177 +} 5.178 + 5.179 +void log_error(const char *fmt, ...) 5.180 +{ 5.181 + LOG_VA_MSG(LOG_ERROR); 5.182 +} 5.183 + 5.184 +void log_debug(const char *fmt, ...) 5.185 +{ 5.186 + LOG_VA_MSG(LOG_DEBUG); 5.187 +} 5.188 + 5.189 +void log_va_info(const char *fmt, va_list ap) 5.190 +{ 5.191 + log_va_msg(LOG_INFO, fmt, ap); 5.192 +} 5.193 + 5.194 +void log_va_warning(const char *fmt, va_list ap) 5.195 +{ 5.196 + log_va_msg(LOG_WARNING, fmt, ap); 5.197 +} 5.198 + 5.199 +void log_va_error(const char *fmt, va_list ap) 5.200 +{ 5.201 + log_va_msg(LOG_ERROR, fmt, ap); 5.202 +} 5.203 + 5.204 +void log_va_debug(const char *fmt, va_list ap) 5.205 +{ 5.206 + log_va_msg(LOG_DEBUG, fmt, ap); 5.207 +} 5.208 + 5.209 +static void log_string(unsigned int type, const char *str) 5.210 +{ 5.211 + struct log_target *targ; 5.212 + 5.213 + targ = targ_list; 5.214 + while(targ) { 5.215 + if(targ->msg_type & type) { 5.216 + if(targ->targ_type == TARG_STREAM || targ->targ_type == TARG_FILE) { 5.217 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 5.218 + if(isatty(fileno(targ->fp)) && type != LOG_INFO) { 5.219 + int c = typecolor(type); 5.220 + fprintf(targ->fp, "\033[%dm%s\033[0m", c, str); 5.221 + } else 5.222 +#endif 5.223 + { 5.224 + fputs(str, targ->fp); 5.225 + } 5.226 + 5.227 + } else if(targ->targ_type == TARG_FUNC) { 5.228 + targ->func(str, targ->func_cls); 5.229 + } 5.230 + } 5.231 + targ = targ->next; 5.232 + } 5.233 +} 5.234 + 5.235 +enum { 5.236 + BLACK = 0, 5.237 + RED, 5.238 + GREEN, 5.239 + YELLOW, 5.240 + BLUE, 5.241 + MAGENTA, 5.242 + CYAN, 5.243 + WHITE 5.244 +}; 5.245 + 5.246 +#define ANSI_FGCOLOR(x) (30 + (x)) 5.247 +#define ANSI_BGCOLOR(x) (40 + (x)) 5.248 + 5.249 +static int typecolor(int type) 5.250 +{ 5.251 + switch(type) { 5.252 + case LOG_ERROR: 5.253 + return ANSI_FGCOLOR(RED); 5.254 + case LOG_WARNING: 5.255 + return ANSI_FGCOLOR(YELLOW); 5.256 + case LOG_DEBUG: 5.257 + return ANSI_FGCOLOR(MAGENTA); 5.258 + default: 5.259 + break; 5.260 + } 5.261 + return 37; 5.262 +} 5.263 + 5.264 +#if defined(unix) || defined(__unix__) || defined(__APPLE__) 5.265 +static int out_pipe[2], err_pipe[2]; 5.266 +static int thr_running; 5.267 + 5.268 +static void *thr_func(void *arg); 5.269 + 5.270 +static int std_intercept(void) 5.271 +{ 5.272 + pthread_t thr; 5.273 + 5.274 + if(thr_running) return 0; 5.275 + 5.276 + pipe(out_pipe); 5.277 + pipe(err_pipe); 5.278 + 5.279 + fcntl(out_pipe[0], F_SETFL, fcntl(out_pipe[0], F_GETFL) | O_NONBLOCK); 5.280 + fcntl(err_pipe[0], F_SETFL, fcntl(err_pipe[0], F_GETFL) | O_NONBLOCK); 5.281 + 5.282 + if(pthread_create(&thr, 0, thr_func, 0) == -1) { 5.283 + log_error("failed to start std intercept thread\n"); 5.284 + return -1; 5.285 + } 5.286 + thr_running = 1; 5.287 + pthread_detach(thr); 5.288 + return 0; 5.289 +} 5.290 + 5.291 +static void *thr_func(void *arg) 5.292 +{ 5.293 + static char buf[512]; 5.294 + static int bufsz = sizeof buf; 5.295 + int max_fd = out_pipe[0] > err_pipe[0] ? out_pipe[0] : err_pipe[0]; 5.296 + 5.297 + for(;;) { 5.298 + int sz, res; 5.299 + fd_set rdset; 5.300 + 5.301 + FD_ZERO(&rdset); 5.302 + FD_SET(out_pipe[0], &rdset); 5.303 + FD_SET(err_pipe[0], &rdset); 5.304 + 5.305 + while((res = select(max_fd + 1, &rdset, 0, 0, 0)) == -1 && errno == EINTR); 5.306 + if(res == -1) { 5.307 + break; /* assume EPIPE or non-recoverable error */ 5.308 + } 5.309 + if(res == 0) continue; 5.310 + 5.311 + if(FD_ISSET(out_pipe[0], &rdset)) { 5.312 + while((sz = read(out_pipe[0], buf, bufsz - 1)) > 0) { 5.313 + buf[bufsz - 1] = 0; 5.314 + log_info("%s", buf); 5.315 + } 5.316 + if(sz == -1 && errno == EPIPE) break; 5.317 + } 5.318 + if(FD_ISSET(err_pipe[0], &rdset)) { 5.319 + while((sz = read(err_pipe[0], buf, bufsz - 1)) > 0) { 5.320 + buf[bufsz - 1] = 0; 5.321 + log_error("%s", buf); 5.322 + } 5.323 + if(sz == -1 && errno == EPIPE) break; 5.324 + } 5.325 + } 5.326 + return 0; 5.327 +} 5.328 + 5.329 +void log_grab_stdout(void) 5.330 +{ 5.331 + if(std_intercept() == -1) { 5.332 + return; 5.333 + } 5.334 + dup2(out_pipe[1], 1); 5.335 +} 5.336 + 5.337 +void log_grab_stderr(void) 5.338 +{ 5.339 + if(std_intercept() == -1) { 5.340 + return; 5.341 + } 5.342 + dup2(err_pipe[1], 2); 5.343 +} 5.344 +#else 5.345 +void log_grab_stdout(void) 5.346 +{ 5.347 + log_error("log_grab_stdout only works on UNIX\n"); 5.348 +} 5.349 + 5.350 +void log_grab_stderr(void) 5.351 +{ 5.352 + log_error("log_grab_stderr only works on UNIX\n"); 5.353 +} 5.354 +#endif
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/logger.h Thu Jan 21 08:45:31 2016 +0200 6.3 @@ -0,0 +1,54 @@ 6.4 +#ifndef LOGGER_H_ 6.5 +#define LOGGER_H_ 6.6 + 6.7 +#include <stdio.h> 6.8 +#include <stdarg.h> 6.9 + 6.10 +enum { 6.11 + LOG_INFO = 1, 6.12 + LOG_WARNING = 2, 6.13 + LOG_ERROR = 4, 6.14 + LOG_DEBUG = 8 6.15 +}; 6.16 + 6.17 +#ifdef __cplusplus 6.18 +extern "C" { 6.19 +#endif 6.20 + 6.21 +/* Clear log outputs. Initially info/debug messages go to stdout, and 6.22 + * warning/error messages to stderr. 6.23 + */ 6.24 +void log_clear_targets(void); 6.25 + 6.26 +/* set logging outputs for any combination of message types. 6.27 + * type_mask expects a bitmask. 6.28 + */ 6.29 +int log_add_stream(unsigned int type_mask, FILE *fp); 6.30 +int log_add_file(unsigned int type_mask, const char *fname); 6.31 +int log_add_func(unsigned int type_mask, void (*func)(const char*, void*), void *cls); 6.32 + 6.33 +void log_msg(unsigned int type, const char *fmt, ...); 6.34 +/* log_msg helpers */ 6.35 +void log_info(const char *fmt, ...); 6.36 +void log_warning(const char *fmt, ...); 6.37 +void log_error(const char *fmt, ...); 6.38 +void log_debug(const char *fmt, ...); 6.39 + 6.40 +void log_va_msg(unsigned int type, const char *fmt, va_list va); 6.41 +/* log_va_msg helpers */ 6.42 +void log_va_info(const char *fmt, va_list ap); 6.43 +void log_va_warning(const char *fmt, va_list ap); 6.44 +void log_va_error(const char *fmt, va_list ap); 6.45 +void log_va_debug(const char *fmt, va_list ap); 6.46 + 6.47 +/* Intercept stdout/stderr and handle them through the logger. stdout as an 6.48 + * info log, and stderr as an error log. This only works on UNIX. 6.49 + */ 6.50 +void log_grab_stdout(void); 6.51 +void log_grab_stderr(void); 6.52 + 6.53 +#ifdef __cplusplus 6.54 +} 6.55 +#endif 6.56 + 6.57 +#endif /* LOGGER_H_ */
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/main.cc Thu Jan 21 08:45:31 2016 +0200 7.3 @@ -0,0 +1,9 @@ 7.4 +#include <stdio.h> 7.5 +#include <stdlib.h> 7.6 +#include <unistd.h> 7.7 +#include <X11/Xlib.h> 7.8 +#include <X11/Xutil.h> 7.9 +#include <X11/Xatom.h> 7.10 +#include <X11/extensions/Xcomposite.h> 7.11 +#include <GL/glew.h> 7.12 +#include "cwin.h"
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/optcfg/optcfg.c Thu Jan 21 08:45:31 2016 +0200 8.3 @@ -0,0 +1,337 @@ 8.4 +#include <stdio.h> 8.5 +#include <stdlib.h> 8.6 +#include <string.h> 8.7 +#include <ctype.h> 8.8 +#ifdef _MSC_VER 8.9 +#include <malloc.h> 8.10 +#else 8.11 +#include <alloca.h> 8.12 +#endif 8.13 +#include "optcfg.h" 8.14 + 8.15 +struct optcfg { 8.16 + struct optcfg_option *optlist; 8.17 + 8.18 + optcfg_opt_callback opt_func; 8.19 + void *opt_cls; 8.20 + optcfg_arg_callback arg_func; 8.21 + void *arg_cls; 8.22 + 8.23 + int err_abort; 8.24 + 8.25 + /* argument parsing state */ 8.26 + char **argv; 8.27 + int argidx; 8.28 + 8.29 + /* config file parsing state */ 8.30 + const char *cfg_fname; 8.31 + int cfg_nline; 8.32 + char *cfg_value; 8.33 +}; 8.34 + 8.35 +static int get_opt(struct optcfg *oc, const char *s); 8.36 +static char *skip_spaces(char *s); 8.37 +static void strip_comments(char *s); 8.38 +static void strip_trailing_spaces(char *s); 8.39 +static char *parse_keyval(char *line); 8.40 + 8.41 + 8.42 +struct optcfg *optcfg_init(struct optcfg_option *optv) 8.43 +{ 8.44 + struct optcfg *oc; 8.45 + 8.46 + if(!(oc = calloc(1, sizeof *oc))) { 8.47 + return 0; 8.48 + } 8.49 + oc->optlist = optv; 8.50 + oc->err_abort = 1; 8.51 + return oc; 8.52 +} 8.53 + 8.54 +void optcfg_destroy(struct optcfg *oc) 8.55 +{ 8.56 + memset(oc, 0, sizeof *oc); 8.57 + free(oc); 8.58 +} 8.59 + 8.60 +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls) 8.61 +{ 8.62 + oc->opt_func = func; 8.63 + oc->opt_cls = cls; 8.64 +} 8.65 + 8.66 +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls) 8.67 +{ 8.68 + oc->arg_func = func; 8.69 + oc->arg_cls = cls; 8.70 +} 8.71 + 8.72 +void optcfg_set_error_action(struct optcfg *oc, int act) 8.73 +{ 8.74 + if(act == OPTCFG_ERROR_FAIL) { 8.75 + oc->err_abort = 1; 8.76 + } else if(act == OPTCFG_ERROR_IGNORE) { 8.77 + oc->err_abort = 0; 8.78 + } 8.79 +} 8.80 + 8.81 +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv) 8.82 +{ 8.83 + int i; 8.84 + 8.85 + oc->argv = argv; 8.86 + 8.87 + for(i=1; i<argc; i++) { 8.88 + oc->argidx = i; 8.89 + 8.90 + if(argv[i][0] == '-') { 8.91 + if(oc->opt_func) { 8.92 + int o = get_opt(oc, argv[i]); 8.93 + if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) { 8.94 + if(oc->err_abort) { 8.95 + return -1; 8.96 + } 8.97 + } 8.98 + } else { 8.99 + fprintf(stderr, "unexpected option: %s\n", argv[i]); 8.100 + if(oc->err_abort) { 8.101 + return -1; 8.102 + } 8.103 + } 8.104 + } else { 8.105 + if(oc->arg_func) { 8.106 + if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) { 8.107 + if(oc->err_abort) { 8.108 + return -1; 8.109 + } 8.110 + } 8.111 + } else { 8.112 + fprintf(stderr, "unexpected argument: %s\n", argv[i]); 8.113 + if(oc->err_abort) { 8.114 + return -1; 8.115 + } 8.116 + } 8.117 + } 8.118 + 8.119 + i = oc->argidx; 8.120 + } 8.121 + 8.122 + oc->argidx = 0; /* done parsing args */ 8.123 + return 0; 8.124 +} 8.125 + 8.126 +int optcfg_parse_config_file(struct optcfg *oc, const char *fname) 8.127 +{ 8.128 + int res; 8.129 + FILE *fp = fopen(fname, "rb"); 8.130 + if(!fp) { 8.131 + return -1; 8.132 + } 8.133 + 8.134 + oc->cfg_fname = fname; 8.135 + res = optcfg_parse_config_stream(oc, fp); 8.136 + oc->cfg_fname = 0; 8.137 + fclose(fp); 8.138 + return res; 8.139 +} 8.140 + 8.141 +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp) 8.142 +{ 8.143 + char buf[512]; 8.144 + 8.145 + oc->cfg_nline = 0; 8.146 + while(fgets(buf, sizeof buf, fp)) { 8.147 + ++oc->cfg_nline; 8.148 + 8.149 + if(optcfg_parse_config_line(oc, buf) == -1) { 8.150 + if(oc->err_abort) { 8.151 + return -1; 8.152 + } 8.153 + } 8.154 + } 8.155 + return 0; 8.156 +} 8.157 + 8.158 +int optcfg_parse_config_line(struct optcfg *oc, const char *line) 8.159 +{ 8.160 + int opt, len; 8.161 + char *start, *val, *buf; 8.162 + 8.163 + len = strlen(line); 8.164 + buf = alloca(len + 1); 8.165 + memcpy(buf, line, len + 1); 8.166 + 8.167 + start = skip_spaces(buf); 8.168 + strip_comments(start); 8.169 + strip_trailing_spaces(start); 8.170 + if(!*start) { 8.171 + return 0; 8.172 + } 8.173 + 8.174 + if(!(val = parse_keyval(start))) { 8.175 + fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline); 8.176 + return -1; 8.177 + } 8.178 + oc->cfg_value = val; 8.179 + if((opt = get_opt(oc, start)) == -1) { 8.180 + fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "", 8.181 + oc->cfg_nline, start); 8.182 + return -1; 8.183 + } 8.184 + 8.185 + if(oc->opt_func) { 8.186 + if(oc->opt_func(oc, opt, oc->opt_cls) == -1) { 8.187 + return -1; 8.188 + } 8.189 + } 8.190 + oc->cfg_value = 0; 8.191 + return 0; 8.192 +} 8.193 + 8.194 +int optcfg_enabled_value(struct optcfg *oc, int *enabledp) 8.195 +{ 8.196 + if(oc->argidx) { 8.197 + *enabledp = 1; /* TODO take -no- prefix into account */ 8.198 + } else { 8.199 + char *val = optcfg_next_value(oc); 8.200 + if(optcfg_bool_value(val, enabledp) == -1) { 8.201 + return -1; 8.202 + } 8.203 + } 8.204 + return 0; 8.205 +} 8.206 + 8.207 + 8.208 +char *optcfg_next_value(struct optcfg *oc) 8.209 +{ 8.210 + if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */ 8.211 + return oc->argv[++oc->argidx]; 8.212 + } 8.213 + if(oc->cfg_value) { 8.214 + char *val = oc->cfg_value; 8.215 + oc->cfg_value = 0; 8.216 + return val; 8.217 + } 8.218 + return 0; 8.219 +} 8.220 + 8.221 +void optcfg_print_options(struct optcfg *oc) 8.222 +{ 8.223 + int i; 8.224 + for(i=0; oc->optlist[i].opt != -1; i++) { 8.225 + struct optcfg_option *opt = oc->optlist + i; 8.226 + 8.227 + if(opt->c) { 8.228 + printf(" -%c", opt->c); 8.229 + } else { 8.230 + printf(" "); 8.231 + } 8.232 + if(opt->s) { 8.233 + printf("%c-%s: ", opt->c ? ',' : ' ', opt->s); 8.234 + } else { 8.235 + printf(": "); 8.236 + } 8.237 + printf("%s\n", opt->desc ? opt->desc : "undocumented"); 8.238 + } 8.239 +} 8.240 + 8.241 +int optcfg_bool_value(char *s, int *valret) 8.242 +{ 8.243 + if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) { 8.244 + *valret = 1; 8.245 + return 0; 8.246 + } 8.247 + if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) { 8.248 + *valret = 0; 8.249 + return 0; 8.250 + } 8.251 + return -1; 8.252 +} 8.253 + 8.254 +int optcfg_int_value(char *str, int *valret) 8.255 +{ 8.256 + char *endp; 8.257 + *valret = strtol(str, &endp, 0); 8.258 + if(endp == str) { 8.259 + return -1; 8.260 + } 8.261 + return 0; 8.262 +} 8.263 + 8.264 +int optcfg_float_value(char *str, float *valret) 8.265 +{ 8.266 + char *endp; 8.267 + *valret = strtod(str, &endp); 8.268 + if(endp == str) { 8.269 + return -1; 8.270 + } 8.271 + return 0; 8.272 +} 8.273 + 8.274 + 8.275 + 8.276 +static int get_opt(struct optcfg *oc, const char *arg) 8.277 +{ 8.278 + int i, ndashes = 0; 8.279 + 8.280 + while(*arg && *arg == '-') { 8.281 + ndashes++; 8.282 + arg++; 8.283 + } 8.284 + 8.285 + if(ndashes > 2) { 8.286 + arg -= ndashes; 8.287 + } 8.288 + 8.289 + if(arg[1]) { /* match long options */ 8.290 + for(i=0; oc->optlist[i].opt != -1; i++) { 8.291 + if(strcmp(arg, oc->optlist[i].s) == 0) { 8.292 + return i; 8.293 + } 8.294 + } 8.295 + } else { 8.296 + for(i=0; oc->optlist[i].opt != -1; i++) { 8.297 + if(arg[0] == oc->optlist[i].c) { 8.298 + return i; 8.299 + } 8.300 + } 8.301 + } 8.302 + return -1; 8.303 +} 8.304 + 8.305 +static char *skip_spaces(char *s) 8.306 +{ 8.307 + while(*s && isspace(*s)) ++s; 8.308 + return s; 8.309 +} 8.310 + 8.311 +static void strip_comments(char *s) 8.312 +{ 8.313 + while(*s && *s != '#') ++s; 8.314 + if(*s == '#') *s = 0; 8.315 +} 8.316 + 8.317 +static void strip_trailing_spaces(char *s) 8.318 +{ 8.319 + char *end = s + strlen(s) - 1; 8.320 + while(end >= s && isspace(*end)) { 8.321 + *end-- = 0; 8.322 + } 8.323 +} 8.324 + 8.325 +static char *parse_keyval(char *line) 8.326 +{ 8.327 + char *val; 8.328 + char *eq = strchr(line, '='); 8.329 + if(!eq) return 0; 8.330 + 8.331 + *eq = 0; 8.332 + strip_trailing_spaces(line); 8.333 + val = skip_spaces(eq + 1); 8.334 + strip_trailing_spaces(val); 8.335 + 8.336 + if(!*line || !*val) { 8.337 + return 0; 8.338 + } 8.339 + return val; 8.340 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/optcfg/optcfg.h Thu Jan 21 08:45:31 2016 +0200 9.3 @@ -0,0 +1,86 @@ 9.4 +/* generic unified commandline option and config file parsing library */ 9.5 +#ifndef LIBOPTCFG_H_ 9.6 +#define LIBOPTCFG_H_ 9.7 + 9.8 +struct optcfg; 9.9 + 9.10 +struct optcfg_option { 9.11 + char c; /* short (optional): used only for argument parsing */ 9.12 + char *s; /* long: used for long options and config files */ 9.13 + int opt; /* the corresponding option enumeration */ 9.14 + char *desc; /* text description for printing usage information */ 9.15 +}; 9.16 + 9.17 +typedef int (*optcfg_opt_callback)(struct optcfg *oc, int opt, void *cls); 9.18 +typedef int (*optcfg_arg_callback)(struct optcfg *oc, const char *arg, void *cls); 9.19 + 9.20 +#ifdef __cplusplus 9.21 +extern "C" { 9.22 +#endif 9.23 + 9.24 +/* initialize the optcfg object with a valid option vector terminated by an 9.25 + * entry with an opt value of -1 (other fields ignored for termination purposes) 9.26 + * 9.27 + * Example: 9.28 + * struct optcfg_option options[] = { 9.29 + * {'f', "foo", OPT_FOO, "Makes sure the foo is bar"}, 9.30 + * {'h', "help", OPT_HELP, "Print usage information and exit"}, 9.31 + * {0, 0, -1, 0} 9.32 + * }; 9.33 + * struct optcfg *oc = optcfg_init(options); 9.34 + */ 9.35 +struct optcfg *optcfg_init(struct optcfg_option *optv); 9.36 +void optcfg_destroy(struct optcfg *oc); 9.37 + 9.38 +/* The parse_* functions call the option callback for each option. 9.39 + * 9.40 + * The option callback can then call optcfg_next_value to retrieve any 9.41 + * values attached to this option. When optcfg_next_value returns 0, there 9.42 + * are no more values available. 9.43 + * The option callback must return 0 for success, and -1 to abort parsing. 9.44 + */ 9.45 +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls); 9.46 +/* the argument callback is only called from optcfg_parse_args(), when a non-option 9.47 + * argument is encountered (an argument not starting with a dash) 9.48 + */ 9.49 +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls); 9.50 + 9.51 +enum { OPTCFG_ERROR_FAIL, OPTCFG_ERROR_IGNORE }; 9.52 +void optcfg_set_error_action(struct optcfg *oc, int act); 9.53 + 9.54 +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv); 9.55 +int optcfg_parse_config_file(struct optcfg *oc, const char *fname); 9.56 +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp); 9.57 +int optcfg_parse_config_line(struct optcfg *oc, const char *line); 9.58 +/* TODO custom I/O callback version of config file parsing */ 9.59 + 9.60 +/* special value function which returns if the option is enabled or disabled 9.61 + * For config files it works similar to calling optcfg_next_value, and 9.62 + * optcfg_bool_value in sequence. 9.63 + * For argument parsing however, it doesn't consume further arguments. Merely 9.64 + * the presence of the option makes it enabled, and its presence with a -no- 9.65 + * prefix disables it. (TODO the second part of this) 9.66 + */ 9.67 +int optcfg_enabled_value(struct optcfg *oc, int *enabledp); 9.68 + 9.69 +/* call optcfg_next_value in the option callback to retrieve the next value 9.70 + * of the current option. returns 0 if there is no next value. 9.71 + */ 9.72 +char *optcfg_next_value(struct optcfg *oc); 9.73 + 9.74 +/* helper function which can be used to print the available options */ 9.75 +void optcfg_print_options(struct optcfg *oc); 9.76 + 9.77 +/* helper functions to convert value strings to typed values 9.78 + * returns 0 for success and value is returned through the valret pointer, 9.79 + * otherwise it returns -1 for type mismatch, and valret contents are undefined 9.80 + */ 9.81 +int optcfg_bool_value(char *str, int *valret); /* accepts yes/no, true/false, 1/0 */ 9.82 +int optcfg_int_value(char *str, int *valret); 9.83 +int optcfg_float_value(char *str, float *valret); 9.84 + 9.85 +#ifdef __cplusplus 9.86 +} 9.87 +#endif 9.88 + 9.89 +#endif /* LIBOPTCFG_H_ */