summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils/nsoption.c697
-rw-r--r--utils/nsoption.h274
2 files changed, 971 insertions, 0 deletions
diff --git a/utils/nsoption.c b/utils/nsoption.c
new file mode 100644
index 000000000..34c653b23
--- /dev/null
+++ b/utils/nsoption.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Option reading and saving (implementation).
+ *
+ * Options are stored in the format key:value, one per line.
+ *
+ * For bool options, value is "0" or "1".
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+
+struct nsoption_s *nsoptions = NULL;
+struct nsoption_s *nsoptions_default = NULL;
+
+#define NSOPTION_BOOL(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_BOOL, { .b = DEFAULT } },
+
+#define NSOPTION_STRING(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_STRING, { .cs = DEFAULT } },
+
+#define NSOPTION_INTEGER(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_INTEGER, { .i = DEFAULT } },
+
+#define NSOPTION_UINT(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_UINT, { .u = DEFAULT } },
+
+#define NSOPTION_COLOUR(NAME, DEFAULT) \
+ { #NAME, sizeof(#NAME) - 1, OPTION_COLOUR, { .c = DEFAULT } },
+
+struct nsoption_s defaults[] = {
+#include "desktop/options.h"
+
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+ { NULL, 0, OPTION_INTEGER, { 0 } }
+};
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+/**
+ * Set an option value based on a string
+ */
+static bool
+strtooption(const char *value, struct nsoption_s *option)
+{
+ bool ret = true;
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ option->value.b = (value[0] == '1');
+ break;
+
+ case OPTION_INTEGER:
+ option->value.i = atoi(value);
+ break;
+
+ case OPTION_UINT:
+ option->value.u = strtoul(value, NULL, 0);
+ break;
+
+ case OPTION_COLOUR:
+ sscanf(value, "%x", &rgbcolour);
+ option->value.c = (((0x000000FF & rgbcolour) << 16) |
+ ((0x0000FF00 & rgbcolour) << 0) |
+ ((0x00FF0000 & rgbcolour) >> 16));
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ free(option->value.s);
+ }
+
+ if (*value == 0) {
+ /* do not allow empty strings in text options */
+ option->value.s = NULL;
+ } else {
+ option->value.s = strdup(value);
+ }
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/* validate options to sane values */
+static void nsoption_validate(struct nsoption_s *opts)
+{
+ if (opts[NSOPTION_font_size].value.i < 50) {
+ opts[NSOPTION_font_size].value.i = 50;
+ }
+
+ if (opts[NSOPTION_font_size].value.i > 1000) {
+ opts[NSOPTION_font_size].value.i = 1000;
+ }
+
+ if (opts[NSOPTION_font_min_size].value.i < 10) {
+ opts[NSOPTION_font_min_size].value.i = 10;
+ }
+
+ if (opts[NSOPTION_font_min_size].value.i > 500) {
+ opts[NSOPTION_font_min_size].value.i = 500;
+ }
+
+ if (opts[NSOPTION_memory_cache_size].value.i < 0) {
+ opts[NSOPTION_memory_cache_size].value.i = 0;
+ }
+}
+
+/** Output choices to file stream
+ *
+ * @param fp the file stream to write to
+ * @param opts The options table to write
+ * @param defs the default value table to compare with.
+ * @param all Output all entries not just ones changed from defaults
+ */
+static nserror
+nsoption_output(FILE *fp,
+ struct nsoption_s *opts,
+ struct nsoption_s *defs,
+ bool all)
+{
+ unsigned int entry;
+ for (entry = 0; entry < NSOPTION_LISTEND; entry++) {
+ switch (opts[entry].type) {
+ case OPTION_BOOL:
+ if (all ||
+ (opts[entry].value.b != defs[entry].value.b)) {
+ fprintf(fp, "%s:%c\n",
+ opts[entry].key,
+ opts[entry].value.b ? '1' : '0');
+ }
+ break;
+
+ case OPTION_INTEGER:
+ if (all ||
+ (opts[entry].value.i != defs[entry].value.i)) {
+ fprintf(fp, "%s:%i\n",
+ opts[entry].key,
+ opts[entry].value.i);
+ }
+ break;
+
+ case OPTION_UINT:
+ if (all ||
+ (opts[entry].value.u != defs[entry].value.u)) {
+ fprintf(fp, "%s:%u\n",
+ opts[entry].key,
+ opts[entry].value.u);
+ }
+ break;
+
+ case OPTION_COLOUR:
+ if (all ||
+ (opts[entry].value.c != defs[entry].value.c)) {
+ colour rgbcolour; /* RRGGBB */
+ rgbcolour = (((0x000000FF & opts[entry].value.c) << 16) |
+ ((0x0000FF00 & opts[entry].value.c) << 0) |
+ ((0x00FF0000 & opts[entry].value.c) >> 16));
+ fprintf(fp, "%s:%06x\n",
+ opts[entry].key,
+ rgbcolour);
+ }
+ break;
+
+ case OPTION_STRING:
+ /* output the key if:
+ * - defs is null.
+ * - default is null but value is not.
+ * - default and value pointers are different
+ * (acts as a null check because of previous check)
+ * and the strings content differ.
+ */
+ if (all ||
+ ((defs[entry].value.s == NULL) &&
+ (opts[entry].value.s != NULL)) ||
+ ((defs[entry].value.s != opts[entry].value.s) &&
+ (strcmp(opts[entry].value.s, defs[entry].value.s) != 0))) {
+ fprintf(fp, "%s:%s\n",
+ opts[entry].key,
+ ((opts[entry].value.s == NULL) ||
+ (*opts[entry].value.s == 0)) ? "" : opts[entry].value.s);
+ }
+ break;
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Output an option value into a string, in HTML format.
+ *
+ * \param option The option to output the value of.
+ * \param size The size of the string buffer.
+ * \param pos The current position in string
+ * \param string The string in which to output the value.
+ * \return The number of bytes written to string or -1 on error
+ */
+static size_t
+nsoption_output_value_html(struct nsoption_s *option,
+ size_t size,
+ size_t pos,
+ char *string)
+{
+ size_t slen = 0; /* length added to string */
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%s",
+ option->value.b ? "true" : "false");
+ break;
+
+ case OPTION_INTEGER:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%i",
+ option->value.i);
+ break;
+
+ case OPTION_UINT:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%u",
+ option->value.u);
+ break;
+
+ case OPTION_COLOUR:
+ rgbcolour = (((0x000000FF & option->value.c) << 16) |
+ ((0x0000FF00 & option->value.c) << 0) |
+ ((0x00FF0000 & option->value.c) >> 16));
+ slen = snprintf(string + pos,
+ size - pos,
+ "<span style=\"background-color: #%06x; "
+ "color: #%06x;\">#%06x</span>",
+ rgbcolour,
+ (~rgbcolour) & 0xffffff,
+ rgbcolour);
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ slen = snprintf(string + pos, size - pos, "%s",
+ option->value.s);
+ } else {
+ slen = snprintf(string + pos, size - pos,
+ "<span class=\"null-content\">NULL"
+ "</span>");
+ }
+ break;
+ }
+
+ return slen;
+}
+
+
+/**
+ * Output an option value into a string, in plain text format.
+ *
+ * \param option The option to output the value of.
+ * \param size The size of the string buffer.
+ * \param pos The current position in string
+ * \param string The string in which to output the value.
+ * \return The number of bytes written to string or -1 on error
+ */
+static size_t
+nsoption_output_value_text(struct nsoption_s *option,
+ size_t size,
+ size_t pos,
+ char *string)
+{
+ size_t slen = 0; /* length added to string */
+ colour rgbcolour; /* RRGGBB */
+
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%c",
+ option->value.b ? '1' : '0');
+ break;
+
+ case OPTION_INTEGER:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%i",
+ option->value.i);
+ break;
+
+ case OPTION_UINT:
+ slen = snprintf(string + pos,
+ size - pos,
+ "%u",
+ option->value.u);
+ break;
+
+ case OPTION_COLOUR:
+ rgbcolour = (((0x000000FF & option->value.c) << 16) |
+ ((0x0000FF00 & option->value.c) << 0) |
+ ((0x00FF0000 & option->value.c) >> 16));
+ slen = snprintf(string + pos, size - pos, "%06x", rgbcolour);
+ break;
+
+ case OPTION_STRING:
+ if (option->value.s != NULL) {
+ slen = snprintf(string + pos,
+ size - pos,
+ "%s",
+ option->value.s);
+ }
+ break;
+ }
+
+ return slen;
+}
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_init(nsoption_set_default_t *set_defaults,
+ struct nsoption_s **popts,
+ struct nsoption_s **pdefs)
+{
+ nserror ret;
+ struct nsoption_s *src;
+ struct nsoption_s *dst;
+ struct nsoption_s *opts;
+
+ /* update the default table */
+ if (set_defaults != NULL) {
+ nsoptions = &defaults[0];
+ ret = set_defaults(&defaults[0]);
+
+ if (ret != NSERROR_OK) {
+ nsoptions = NULL;
+ return ret;
+ }
+ }
+
+ opts = malloc(sizeof(defaults));
+ if (opts == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ /* copy the default values into the working set */
+ src = &defaults[0];
+ dst = opts;
+
+ while (src->key != NULL) {
+ dst->key = src->key;
+ dst->key_len = src->key_len;
+ dst->type = src->type;
+ if ((src->type == OPTION_STRING) && (src->value.s != NULL)) {
+ dst->value.s = strdup(src->value.s);
+ } else {
+ dst->value = src->value;
+ }
+ src++;
+ dst++;
+ }
+ dst->key = src->key;
+
+ /* return values if wanted */
+ if (popts != NULL) {
+ *popts = opts;
+ } else {
+ nsoptions = opts;
+ }
+
+ if (pdefs != NULL) {
+ *pdefs = &defaults[0];
+ }
+
+ return NSERROR_OK;
+}
+
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_read(const char *path, struct nsoption_s *opts)
+{
+ char s[100];
+ FILE *fp;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ LOG(("Failed to open file '%s'", path));
+ return NSERROR_NOT_FOUND;
+ }
+
+ LOG(("Sucessfully opened '%s' for Options file", path));
+
+ while (fgets(s, 100, fp)) {
+ char *colon, *value;
+ unsigned int idx;
+
+ if ((s[0] == 0) || (s[0] == '#')) {
+ continue;
+ }
+
+ colon = strchr(s, ':');
+ if (colon == 0) {
+ continue;
+ }
+
+ s[strlen(s) - 1] = 0; /* remove \n at end */
+ *colon = 0; /* terminate key */
+ value = colon + 1;
+
+ for (idx = 0; opts[idx].key != NULL; idx++) {
+ if (strcasecmp(s, opts[idx].key) != 0) {
+ continue;
+ }
+
+ strtooption(value, &opts[idx]);
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ nsoption_validate(opts);
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_write(const char *path,
+ struct nsoption_s *opts,
+ struct nsoption_s *defs)
+{
+ FILE *fp;
+ nserror ret;
+
+ if (path == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ /* check to see if global table selected */
+ if (defs == NULL) {
+ defs = &defaults[0];
+ }
+
+ fp = fopen(path, "w");
+ if (!fp) {
+ LOG(("failed to open file '%s' for writing", path));
+ return NSERROR_NOT_FOUND;
+ }
+
+ ret = nsoption_output(fp, opts, defs, false);
+
+ fclose(fp);
+
+ return ret;
+}
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_dump(FILE *outf, struct nsoption_s *opts)
+{
+ if (outf == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ return nsoption_output(outf, opts, NULL, true);
+}
+
+
+/* exported interface documented in utils/nsoption.h */
+nserror
+nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts)
+{
+ char *arg;
+ char *val;
+ int arglen;
+ int idx = 1;
+ int mv_loop;
+ unsigned int entry_loop;
+
+ /* check to see if global table selected */
+ if (opts == NULL) {
+ opts = nsoptions;
+ }
+
+ while (idx < *pargc) {
+ arg = argv[idx];
+ arglen = strlen(arg);
+
+ /* check we have an option */
+ /* option must start -- and be as long as the shortest option*/
+ if ((arglen < (2+5) ) || (arg[0] != '-') || (arg[1] != '-'))
+ break;
+
+ arg += 2; /* skip -- */
+
+ val = strchr(arg, '=');
+ if (val == NULL) {
+ /* no equals sign - next parameter is val */
+ idx++;
+ if (idx >= *pargc)
+ break;
+ val = argv[idx];
+ } else {
+ /* equals sign */
+ arglen = val - arg ;
+ val++;
+ }
+
+ /* arg+arglen is the option to set, val is the value */
+
+ LOG(("%.*s = %s", arglen, arg, val));
+
+ for (entry_loop = 0;
+ entry_loop < NSOPTION_LISTEND;
+ entry_loop++) {
+ if (strncmp(arg, opts[entry_loop].key, arglen) == 0) {
+ strtooption(val, opts + entry_loop);
+ break;
+ }
+ }
+
+ idx++;
+ }
+
+ /* remove processed options from argv */
+ for (mv_loop=0; mv_loop < (*pargc - idx); mv_loop++) {
+ argv[mv_loop + 1] = argv[mv_loop + idx];
+ }
+ *pargc -= (idx - 1);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in options.h */
+int
+nsoption_snoptionf(char *string,
+ size_t size,
+ enum nsoption_e option_idx,
+ const char *fmt)
+{
+ size_t slen = 0; /* current output string length */
+ int fmtc = 0; /* current index into format string */
+ struct nsoption_s *option;
+
+ if (option_idx >= NSOPTION_LISTEND) {
+ return -1;
+ }
+
+ option = &nsoptions[option_idx]; /* assume the global table */
+ if (option == NULL || option->key == NULL)
+ return -1;
+
+
+ while ((slen < size) && (fmt[fmtc] != 0)) {
+ if (fmt[fmtc] == '%') {
+ fmtc++;
+ switch (fmt[fmtc]) {
+ case 'k':
+ slen += snprintf(string + slen,
+ size - slen,
+ "%s",
+ option->key);
+ break;
+
+ case 't':
+ switch (option->type) {
+ case OPTION_BOOL:
+ slen += snprintf(string + slen,
+ size - slen,
+ "boolean");
+ break;
+
+ case OPTION_INTEGER:
+ slen += snprintf(string + slen,
+ size - slen,
+ "integer");
+ break;
+
+ case OPTION_UINT:
+ slen += snprintf(string + slen,
+ size - slen,
+ "unsigned integer");
+ break;
+
+ case OPTION_COLOUR:
+ slen += snprintf(string + slen,
+ size - slen,
+ "colour");
+ break;
+
+ case OPTION_STRING:
+ slen += snprintf(string + slen,
+ size - slen,
+ "string");
+ break;
+
+ }
+ break;
+
+
+ case 'V':
+ slen += nsoption_output_value_html(option,
+ size,
+ slen,
+ string);
+ break;
+ case 'v':
+ slen += nsoption_output_value_text(option,
+ size,
+ slen,
+ string);
+ break;
+ }
+ fmtc++;
+ } else {
+ string[slen] = fmt[fmtc];
+ slen++;
+ fmtc++;
+ }
+ }
+
+ /* Ensure that we NUL-terminate the output */
+ string[min(slen, size - 1)] = '\0';
+
+ return slen;
+}
diff --git a/utils/nsoption.h b/utils/nsoption.h
new file mode 100644
index 000000000..3ace847b9
--- /dev/null
+++ b/utils/nsoption.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Option reading and saving (interface).
+ *
+ * Global options are defined in desktop/options.h
+ * Distinct target options are defined in <TARGET>/options.h
+ *
+ * The implementation API is slightly compromised because it still has
+ * "global" tables for both the default and current option tables.
+ *
+ * The initialisation and read/write interfaces take pointers to an
+ * option table which would let us to make the option structure
+ * opaque.
+ *
+ * All the actual acessors assume direct access to a global option
+ * table (nsoptions). To avoid this the acessors would have to take a
+ * pointer to the active options table and be implemented as functions
+ * within nsoptions.c
+ *
+ * Indirect acees would have an impact on performance of NetSurf as
+ * the expected option lookup cost is currently that of a simple
+ * dereference (which this current implementation keeps).
+ */
+
+#ifndef _NETSURF_UTILS_NSOPTION_H_
+#define _NETSURF_UTILS_NSOPTION_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+typedef uint32_t colour;
+
+/* allow targets to include any necessary headers of their own */
+#define NSOPTION_BOOL(NAME, DEFAULT)
+#define NSOPTION_STRING(NAME, DEFAULT)
+#define NSOPTION_INTEGER(NAME, DEFAULT)
+#define NSOPTION_UINT(NAME, DEFAULT)
+#define NSOPTION_COLOUR(NAME, DEFAULT)
+
+#include "desktop/options.h"
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+
+
+enum { OPTION_HTTP_PROXY_AUTH_NONE = 0,
+ OPTION_HTTP_PROXY_AUTH_BASIC = 1,
+ OPTION_HTTP_PROXY_AUTH_NTLM = 2 };
+
+#define DEFAULT_MARGIN_TOP_MM 10
+#define DEFAULT_MARGIN_BOTTOM_MM 10
+#define DEFAULT_MARGIN_LEFT_MM 10
+#define DEFAULT_MARGIN_RIGHT_MM 10
+#define DEFAULT_EXPORT_SCALE 0.7
+
+#ifndef DEFAULT_REFLOW_PERIOD
+#define DEFAULT_REFLOW_PERIOD 25 /* time in cs */
+#endif
+
+enum nsoption_type_e {
+ OPTION_BOOL,
+ OPTION_INTEGER,
+ OPTION_UINT,
+ OPTION_STRING,
+ OPTION_COLOUR
+};
+
+struct nsoption_s {
+ const char *key;
+ int key_len;
+ enum nsoption_type_e type;
+ union {
+ bool b;
+ int i;
+ unsigned int u;
+ char *s;
+ const char *cs;
+ colour c;
+ } value;
+};
+
+/* construct the option enumeration */
+#define NSOPTION_BOOL(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_STRING(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_INTEGER(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_UINT(NAME, DEFAULT) NSOPTION_##NAME,
+#define NSOPTION_COLOUR(NAME, DEFAULT) NSOPTION_##NAME,
+
+enum nsoption_e {
+#include "desktop/options.h"
+#if defined(riscos)
+#include "riscos/options.h"
+#elif defined(nsgtk)
+#include "gtk/options.h"
+#elif defined(nsbeos)
+#include "beos/options.h"
+#elif defined(nsamiga)
+#include "amiga/options.h"
+#elif defined(nsframebuffer)
+#include "framebuffer/options.h"
+#elif defined(nsatari)
+#include "atari/options.h"
+#elif defined(nsmonkey)
+#include "monkey/options.h"
+#endif
+ NSOPTION_LISTEND /* end of list */
+};
+
+#undef NSOPTION_BOOL
+#undef NSOPTION_STRING
+#undef NSOPTION_INTEGER
+#undef NSOPTION_UINT
+#undef NSOPTION_COLOUR
+
+/* global option table */
+extern struct nsoption_s *nsoptions;
+
+/* global default option table */
+extern struct nsoption_s *nsoptions_default;
+
+/* default setting callback */
+typedef nserror(nsoption_set_default_t)(struct nsoption_s *defaults);
+
+
+/** Initialise option system.
+ *
+ * @param set_default callback to allow the customisation of the default
+ * options.
+ * @param ppots pointer to update to get options table or NULL.
+ * @param pdefs pointer to update to get default options table or NULL.
+ * @return The error status
+ */
+nserror nsoption_init(nsoption_set_default_t *set_default, struct nsoption_s **popts, struct nsoption_s **pdefs);
+
+
+/** Read choices file and set them in the passed table
+ *
+ * @param path The path to read the file from
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_read(const char *path, struct nsoption_s *opts);
+
+
+/** Write options that have changed from the defaults to a file.
+ *
+ * The \a nsoption_dump can be used to output all entries not just
+ * changed ones.
+ *
+ * @param path The path to read the file from
+ * @param opts The options table to enerate values from or NULL to use global
+ * @param defs The default table to use or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_write(const char *path, struct nsoption_s *opts, struct nsoption_s *defs);
+
+
+/**
+ * Write all options to a stream.
+ *
+ * @param outf The stream to write to
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_dump(FILE *outf, struct nsoption_s *opts);
+
+/**
+ * Process commandline and set options approriately.
+ *
+ * @param pargc Pointer to the size of the argument vector.
+ * @param argv The argument vector.
+ * @param opts The options table to enerate values from or NULL to use global
+ * @return The error status
+ */
+nserror nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts);
+
+/**
+ * Fill a buffer with an option using a format.
+ *
+ * The format string is copied into the output buffer with the
+ * following replaced:
+ * %k - The options key
+ * %t - The options type
+ * %V - value (HTML formatting)
+ * %v - value (plain formatting)
+ *
+ * @param string The buffer in which to place the results.
+ * @param size The size of the string buffer.
+ * @param option The option .
+ * @param fmt The format string.
+ * @return The number of bytes written to \a string or -1 on error
+ */
+int nsoption_snoptionf(char *string, size_t size, enum nsoption_e option, const char *fmt);
+
+
+
+
+/* value acessors - caution should be taken with type as this is not verified */
+#define nsoption_bool(OPTION) (nsoptions[NSOPTION_##OPTION].value.b)
+#define nsoption_int(OPTION) (nsoptions[NSOPTION_##OPTION].value.i)
+#define nsoption_uint(OPTION) (nsoptions[NSOPTION_##OPTION].value.u)
+#define nsoption_charp(OPTION) (nsoptions[NSOPTION_##OPTION].value.s)
+#define nsoption_colour(OPTION) (nsoptions[NSOPTION_##OPTION].value.c)
+
+#define nsoption_set_bool(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.b = VALUE
+#define nsoption_set_int(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.i = VALUE
+#define nsoption_set_colour(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.c = VALUE
+#define nsoption_set_charp(OPTION, VALUE) \
+ do { \
+ if (nsoptions[NSOPTION_##OPTION].value.s != NULL) { \
+ free(nsoptions[NSOPTION_##OPTION].value.s); \
+ } \
+ nsoptions[NSOPTION_##OPTION].value.s = VALUE; \
+ if ((nsoptions[NSOPTION_##OPTION].value.s != NULL) && \
+ (*nsoptions[NSOPTION_##OPTION].value.s == 0)) { \
+ free(nsoptions[NSOPTION_##OPTION].value.s); \
+ nsoptions[NSOPTION_##OPTION].value.s = NULL; \
+ } \
+ } while (0)
+
+/* if a string option is unset set it otherwise leave it set */
+#define nsoption_setnull_charp(OPTION, VALUE) \
+ do { \
+ if (nsoptions[NSOPTION_##OPTION].value.s == NULL) { \
+ nsoptions[NSOPTION_##OPTION].value.s = VALUE; \
+ if (*nsoptions[NSOPTION_##OPTION].value.s == 0) { \
+ free(nsoptions[NSOPTION_##OPTION].value.s); \
+ nsoptions[NSOPTION_##OPTION].value.s = NULL; \
+ } \
+ } else { \
+ free(VALUE); \
+ } \
+ } while (0)
+
+
+#endif