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_ */