summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/settings.c329
-rw-r--r--src/settings.h36
3 files changed, 366 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 59ba58c..9ee9d88 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
SUBDIRS = modules
bin_PROGRAMS = rototiller
-rototiller_SOURCES = drmsetup.c drmsetup.h drm_fb.c drm_fb.h fb.c fb.h fps.c fps.h rototiller.c rototiller.h threads.c threads.h util.c util.h
+rototiller_SOURCES = drmsetup.c drmsetup.h drm_fb.c drm_fb.h fb.c fb.h fps.c fps.h rototiller.c rototiller.h settings.h settings.c threads.c threads.h util.c util.h
rototiller_LDADD = @ROTOTILLER_LIBS@ -lm modules/julia/libjulia.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/sparkler/libsparkler.a modules/stars/libstars.a
rototiller_CPPFLAGS = @ROTOTILLER_CFLAGS@
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..e958b43
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,329 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "settings.h"
+
+/* Split form of key=value[,key=value...] settings string */
+typedef struct settings_t {
+ unsigned num;
+ const char **keys;
+ const char **values;
+} settings_t;
+
+typedef enum settings_fsm_state_t {
+ SETTINGS_FSM_STATE_KEY,
+ SETTINGS_FSM_STATE_EQUAL,
+ SETTINGS_FSM_STATE_VALUE,
+ SETTINGS_FSM_STATE_COMMA,
+} settings_fsm_state_t;
+
+
+static int add_value(settings_t *settings, const char *key, const char *value)
+{
+ assert(settings);
+
+ settings->num++;
+ /* TODO errors */
+ settings->keys = realloc(settings->keys, settings->num * sizeof(const char **));
+ settings->values = realloc(settings->values, settings->num * sizeof(const char **));
+ settings->keys[settings->num - 1] = key;
+ settings->values[settings->num - 1] = value;
+
+ return 0;
+}
+
+
+/* split settings_string into a data structure */
+settings_t * settings_new(const char *settings_string)
+{
+ settings_fsm_state_t state = SETTINGS_FSM_STATE_KEY;
+ const char *p, *token;
+ settings_t *settings;
+
+ settings = calloc(1, sizeof(settings_t));
+ if (!settings)
+ return NULL;
+
+ if (!settings_string)
+ return settings;
+
+ /* TODO: unescaping? */
+ for (token = p = settings_string; ;p++) {
+
+ switch (state) {
+ case SETTINGS_FSM_STATE_COMMA:
+ token = p;
+ state = SETTINGS_FSM_STATE_KEY;
+ break;
+
+ case SETTINGS_FSM_STATE_KEY:
+ if (*p == '=' || *p == ',' || *p == '\0') {
+ add_value(settings, strndup(token, p - token), NULL);
+
+ if (*p == '=')
+ state = SETTINGS_FSM_STATE_EQUAL;
+ else if (*p == ',')
+ state = SETTINGS_FSM_STATE_COMMA;
+ }
+ break;
+
+ case SETTINGS_FSM_STATE_EQUAL:
+ token = p;
+ state = SETTINGS_FSM_STATE_VALUE;
+ break;
+
+ case SETTINGS_FSM_STATE_VALUE:
+ if (*p == ',' || *p == '\0') {
+ settings->values[settings->num - 1] = strndup(token, p - token);
+ state = SETTINGS_FSM_STATE_COMMA;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (*p == '\0')
+ break;
+ }
+
+ /* FIXME: this should probably never leave a value or key entry NULL */
+
+ return settings;
+}
+
+
+/* free structure attained via settings_new() */
+void settings_free(settings_t *settings)
+{
+ unsigned i;
+
+ assert(settings);
+
+ for (i = 0; i < settings->num; i++) {
+ free((void *)settings->keys[i]);
+ free((void *)settings->values[i]);
+ }
+
+ free((void *)settings->keys);
+ free((void *)settings->values);
+ free(settings);
+}
+
+
+/* find key= in settings, return dup of value side or NULL if missing */
+const char * settings_get_value(settings_t *settings, const char *key)
+{
+ int i;
+
+ assert(settings);
+ assert(key);
+
+ for (i = 0; i < settings->num; i++) {
+ if (!strcmp(key, settings->keys[i]))
+ return settings->values[i];
+ }
+
+ return NULL;
+}
+
+
+/* return positional key from settings */
+const char * settings_get_key(settings_t *settings, unsigned pos)
+{
+ assert(settings);
+
+ if (pos < settings->num)
+ return settings->keys[pos];
+
+ return NULL;
+}
+
+
+/* add key=value to the settings,
+ * or just key if value is NULL.
+ */
+/* returns < 0 on error */
+int settings_add_value(settings_t *settings, const char *key, const char *value)
+{
+ assert(settings);
+ assert(key);
+
+ return add_value(settings, strdup(key), value ? strdup(value) : NULL);
+}
+
+
+/* apply the supplied setting description generators to the supplied settings */
+/* returns 0 when input settings are complete */
+/* returns 1 when input settings are incomplete, storing the next setting's description needed in *next_setting */
+/* returns -errno on error */
+int settings_apply_desc_generators(settings_t *settings, const setting_desc_generator_t generators[], unsigned n_generators, void *setup_context, setting_desc_t **next_setting)
+{
+ unsigned i;
+ setting_desc_t *next;
+
+ assert(settings);
+ assert(generators);
+ assert(n_generators > 0);
+ assert(next_setting);
+
+ for (i = 0; i < n_generators; i++) {
+ const setting_desc_generator_t *g = &generators[i];
+ const char *value;
+
+ value = settings_get_value(settings, g->key);
+ if (value) {
+ if (g->value_ptr)
+ *g->value_ptr = value;
+
+ continue;
+ }
+
+ next = g->func(setup_context);
+ if (!next)
+ return -ENOMEM;
+
+ *next_setting = next;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* convenience helper for creating a new setting description */
+/* copies of everything supplied are made in newly allocated memory */
+setting_desc_t * setting_desc_new(const char *name, const char *key, const char *regex, const char *preferred, const char *values[], const char *annotations[])
+{
+ setting_desc_t *desc;
+
+ assert(name);
+ assert(preferred); /* XXX: require a preferred default? */
+ assert(!annotations || values);
+
+ desc = calloc(1, sizeof(setting_desc_t));
+ if (!desc)
+ return NULL;
+
+ desc->name = strdup(name);
+ if (key) /* This is inappropriately subtle, but when key is NULL, the value will be the key, and there will be no value side at all. */
+ desc->key = strdup(key);
+ if (regex)
+ desc->regex = strdup(regex);
+
+ desc->preferred = strdup(preferred);
+
+ if (values) {
+ unsigned i;
+
+ for (i = 0; values[i]; i++);
+
+ desc->values = calloc(i + 1, sizeof(*values));
+
+ if (annotations)
+ desc->annotations = calloc(i + 1, sizeof(*annotations));
+
+ for (i = 0; values[i]; i++) {
+ desc->values[i] = strdup(values[i]);
+
+ if (annotations) {
+ assert(annotations[i]);
+ desc->annotations[i] = strdup(annotations[i]);
+ }
+ }
+ }
+
+ /* TODO: handle allocation errors above... */
+
+ return desc;
+}
+
+
+void setting_desc_free(setting_desc_t *desc)
+{
+ free((void *)desc->name);
+ free((void *)desc->key);
+ free((void *)desc->regex);
+ free((void *)desc->preferred);
+
+ if (desc->values) {
+ unsigned i;
+
+ for (i = 0; desc->values[i]; i++) {
+ free((void *)desc->values[i]);
+
+ if (desc->annotations)
+ free((void *)desc->annotations[i]);
+ }
+
+ free((void *)desc->values);
+ free((void *)desc->annotations);
+ }
+
+ free(desc);
+}
+
+
+/* wrapper around sprintf for convenient buffer size computation */
+/* supply NULL buf when computing size, size and offset are ignored.
+ * supply non-NULL for actual writing into buf of size bytes @ offset.
+ * return value is number of bytes (potentially if !buf) written
+ */
+static int snpf(char *buf, size_t size, off_t offset, const char *format, ...)
+{
+ size_t avail = 0;
+ va_list ap;
+ int r;
+
+ if (buf) {
+ assert(size > offset);
+
+ avail = size - offset;
+ buf += offset;
+ }
+
+ va_start(ap, format);
+ r = vsnprintf(buf, avail, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+
+char * settings_as_arg(const settings_t *settings)
+{
+ char *buf = NULL;
+ size_t off, size;
+
+ /* intentionally avoided open_memstream for portability reasons */
+ for (;;) {
+ unsigned i;
+
+ for (i = off = 0; i < settings->num; i++) {
+ if (i > 0)
+ off += snpf(buf, size, off, ",");
+
+ off += snpf(buf, size, off, "%s", settings->keys[i]);
+
+ if (settings->values[i])
+ off += snpf(buf, size, off, "=%s", settings->values[i]);
+ }
+
+ if (!buf) {
+ size = off + 1;
+ buf = calloc(size, sizeof(char));
+ if (!buf)
+ return NULL;
+
+ continue;
+ }
+
+ break;
+ }
+
+ return buf;
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 0000000..6334faa
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,36 @@
+#ifndef _SETTINGS_H
+#define _SETTINGS_H
+
+#include <stdio.h>
+
+/* Individual setting description */
+typedef struct setting_desc_t {
+ const char *name; /* long-form/human name for setting */
+ const char *key; /* short-form/key for setting, used as left side of =value in settings string */
+ const char *regex; /* value must conform to this regex */
+ const char *preferred; /* if there's a default, this is it */
+ const char **values; /* if a set of values is provided, listed here */
+ const char **annotations; /* if a set of values is provided, annotations for those values may be listed here */
+} setting_desc_t;
+
+/* For conveniently representing setting description generators */
+typedef struct setting_desc_generator_t {
+ const char *key; /* key this generator applies to */
+ const char **value_ptr; /* where to put the value */
+ setting_desc_t *(*func)(void *setup_context);
+} setting_desc_generator_t;
+
+typedef struct settings_t settings_t;
+
+settings_t * settings_new(const char *settings);
+void settings_free(settings_t *settings);
+const char * settings_get_value(settings_t *settings, const char *key);
+const char * settings_get_key(settings_t *settings, unsigned pos);
+int settings_add_value(settings_t *settings, const char *key, const char *value);
+char * settings_as_arg(const settings_t *settings);
+int settings_apply_desc_generators(settings_t *settings, const setting_desc_generator_t generators[], unsigned n_generators, void *setup_context, setting_desc_t **next_setting);
+
+setting_desc_t * setting_desc_new(const char *name, const char *key, const char *regex, const char *preferred, const char *values[], const char *annotations[]);
+void setting_desc_free(setting_desc_t *desc);
+
+#endif
© All Rights Reserved