From a2769ec68bd81a33096a981d7299bd1b3804882c Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Mon, 27 May 2013 09:34:22 +0100 Subject: add new option API and implementation --- utils/nsoption.c | 697 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 697 insertions(+) create mode 100644 utils/nsoption.c (limited to 'utils/nsoption.c') 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 + * + * 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 . + */ + +/** \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 +#include +#include +#include +#include + +#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, + "#%06x", + 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, + "NULL" + ""); + } + 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; +} -- cgit v1.2.3