- Regex: '".*_private\.h"'
Priority: -6
- Regex: '"mutt/.*\.h"'
+ Priority: -5
+ - Regex: '"config/.*\.h"'
Priority: -4
- Regex: '"email/.*\.h"'
Priority: -3
MUTTLIBS+= $(LIBCONN)
ALLOBJS+= $(LIBCONNOBJS)
+###############################################################################
+# libconfig
+LIBCONFIG= libconfig.a
+LIBCONFIGOBJS= config/address.o config/bool.o config/command.o config/dump.o \
+ config/long.o config/magic.o config/mbtable.o config/number.o \
+ config/path.o config/quad.o config/regex.o config/set.o \
+ config/sort.o config/string.o
+
+CLEANFILES+= $(LIBCONFIG) $(LIBCONFIGOBJS)
+MUTTLIBS+= $(LIBCONFIG)
+ALLOBJS+= $(LIBCONFIGOBJS)
+
###############################################################################
# libhcache
@if USE_HCACHE
$(PWD)/conn:
$(MKDIR_P) $(PWD)/conn
+# libconfig
+$(LIBCONFIG): $(PWD)/config $(LIBCONFIGOBJS)
+ $(AR) cr $@ $(LIBCONFIGOBJS)
+ $(RANLIB) $@
+$(PWD)/config:
+ $(MKDIR_P) $(PWD)/config
+
# libhcache
hcache/hcache.o: hcache/hcversion.h
$(LIBHCACHE): $(PWD)/hcache $(LIBHCACHEOBJS)
--- /dev/null
+## Process this file with automake to produce Makefile.in
+include $(top_srcdir)/flymake.am
+
+AUTOMAKE_OPTIONS = 1.6 foreign
+
+EXTRA_DIST = account.h address.h bool.h inheritance.h magic.h mbtable.h number.h path.h quad.h regex2.h set.h sort.h string3.h types.h
+
+AM_CPPFLAGS = -I$(top_srcdir)
+
+noinst_LIBRARIES = libconfig.a
+
+libconfig_a_SOURCES = account.c address.c bool.c magic.c mbtable.c number.c path.c quad.c regex.c set.c sort.c string.c
+
--- /dev/null
+/**
+ * @file
+ * Type representing an email address
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-address Type: Email address
+ *
+ * Type representing an email address.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "email/address.h"
+#include "address.h"
+#include "set.h"
+#include "types.h"
+
+size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display);
+
+/**
+ * address_destroy - Destroy an Address object
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void address_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ struct Address **a = (struct Address **) var;
+ if (!*a)
+ return;
+
+ address_free(a);
+}
+
+/**
+ * address_string_set - Set an Address by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int address_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Address *addr = NULL;
+
+ /* An empty address "" will be stored as NULL */
+ if (var && value && (value[0] != '\0'))
+ {
+ // addr = mutt_addr_parse_list(NULL, value);
+ }
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) addr, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ address_destroy(cs, &addr, cdef);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ /* ordinary variable setting */
+ address_destroy(cs, var, cdef);
+
+ *(struct Address **) var = addr;
+
+ if (!addr)
+ rc |= CSR_SUC_EMPTY;
+ }
+ else
+ {
+ /* set the default/initial value */
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * address_string_get - Get an Address as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int address_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ char tmp[HUGE_STRING] = "";
+ const char *str = NULL;
+
+ if (var)
+ {
+ struct Address *a = *(struct Address **) var;
+ if (a)
+ {
+ mutt_addr_write(tmp, sizeof(tmp), a, false);
+ str = tmp;
+ }
+ }
+ else
+ {
+ str = (char *) cdef->initial;
+ }
+
+ if (!str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * address_dup - Create a copy of an Address object
+ * @param addr Address to duplicate
+ * @retval ptr New Address object
+ */
+static struct Address *address_dup(struct Address *addr)
+{
+ if (!addr)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ struct Address *a = mutt_mem_calloc(1, sizeof(*a));
+ a->personal = mutt_str_strdup(addr->personal);
+ a->mailbox = mutt_str_strdup(addr->mailbox);
+ return a;
+}
+
+/**
+ * address_native_set - Set an Address config item by Address object
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Address pointer
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int address_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value,
+ struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ address_free(var);
+
+ struct Address *addr = address_dup((struct Address *) value);
+
+ rc = CSR_SUCCESS;
+ if (!addr)
+ rc |= CSR_SUC_EMPTY;
+
+ *(struct Address **) var = addr;
+ return rc;
+}
+
+/**
+ * address_native_get - Get an Address object from an Address config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Address pointer
+ */
+static intptr_t address_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ struct Address *addr = *(struct Address **) var;
+
+ return (intptr_t) addr;
+}
+
+/**
+ * address_reset - Reset an Address to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int address_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Address *a = NULL;
+ const char *initial = (const char *) cdef->initial;
+
+ if (initial)
+ a = address_create(initial);
+
+ int rc = CSR_SUCCESS;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) a, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ address_destroy(cs, &a, cdef);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ if (!a)
+ rc |= CSR_SUC_EMPTY;
+
+ address_destroy(cs, var, cdef);
+
+ *(struct Address **) var = a;
+ return rc;
+}
+
+/**
+ * address_init - Register the Address config type
+ * @param cs Config items
+ */
+void address_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_address = {
+ "address", address_string_set, address_string_get,
+ address_native_set, address_native_get, address_reset,
+ address_destroy,
+ };
+ cs_register_type(cs, DT_ADDRESS, &cst_address);
+}
+
+/**
+ * address_create - Create an Address from a string
+ * @param addr Email address to parse
+ * @retval ptr New Address object
+ */
+struct Address *address_create(const char *addr)
+{
+ struct Address *a = mutt_mem_calloc(1, sizeof(*a));
+ a->personal = mutt_str_strdup(addr);
+ a->mailbox = mutt_str_strdup("dummy3");
+ return a;
+}
+
+/**
+ * address_free - Free an Address object
+ * @param addr Address to free
+ */
+void address_free(struct Address **addr)
+{
+ if (!addr || !*addr)
+ return; /* LCOV_EXCL_LINE */
+
+ FREE(&(*addr)->personal);
+ FREE(&(*addr)->mailbox);
+ FREE(addr);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing an email address
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_ADDRESS_H
+#define _CONFIG_ADDRESS_H
+
+#include <stdbool.h>
+
+struct ConfigSet;
+
+void address_init(struct ConfigSet *cs);
+struct Address *address_create(const char *addr);
+void address_free(struct Address **addr);
+
+#endif /* _CONFIG_ADDRESS_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a boolean
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-bool Type: Boolean
+ *
+ * Type representing a boolean.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/hash.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * bool_values - Valid strings for creating a Bool
+ *
+ * These strings are case-insensitive.
+ */
+const char *bool_values[] = {
+ "no", "yes", "n", "y", "false", "true", "0", "1", "off", "on", NULL,
+};
+
+/**
+ * bool_string_set - Set a Bool by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int bool_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef || !value)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int num = -1;
+ for (size_t i = 0; bool_values[i]; i++)
+ {
+ if (mutt_str_strcasecmp(bool_values[i], value) == 0)
+ {
+ num = i % 2;
+ break;
+ }
+ }
+
+ if (num < 0)
+ {
+ mutt_buffer_printf(err, "Invalid boolean value: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (var)
+ {
+ if (num == (*(bool *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) num, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(bool *) var = num;
+ }
+ else
+ {
+ cdef->initial = num;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * bool_string_get - Get a Bool as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int bool_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int index;
+
+ if (var)
+ index = *(bool *) var;
+ else
+ index = (int) cdef->initial;
+
+ if (index > 1)
+ {
+ mutt_debug(1, "Variable has an invalid value: %d\n", index);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ mutt_buffer_addstr(result, bool_values[index]);
+ return CSR_SUCCESS;
+}
+
+/**
+ * bool_native_set - Set a Bool config item by bool
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Bool value
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int bool_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if ((value < 0) || (value > 1))
+ {
+ mutt_buffer_printf(err, "Invalid boolean value: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (value == (*(bool *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(bool *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * bool_native_get - Get a bool from a Bool config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Bool
+ */
+static intptr_t bool_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(bool *) var;
+}
+
+/**
+ * bool_reset - Reset a Bool to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int bool_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(bool *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(bool *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * bool_init - Register the Bool config type
+ * @param cs Config items
+ */
+void bool_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_bool = {
+ "boolean",
+ bool_string_set,
+ bool_string_get,
+ bool_native_set,
+ bool_native_get,
+ bool_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_BOOL, &cst_bool);
+}
+
+/**
+ * bool_he_toggle - Toggle the value of a bool
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int bool_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
+{
+ if (!cs || !he || !he->data)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (DTYPE(he->type) != DT_BOOL)
+ return CSR_ERR_CODE;
+
+ const struct ConfigDef *cdef = he->data;
+ char *var = cdef->var;
+
+ char value = *var;
+ if ((value < 0) || (value > 1))
+ {
+ mutt_buffer_printf(err, "Invalid boolean value: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ *(char *) var = !value;
+
+ cs_notify_listeners(cs, he, he->key.strkey, CE_SET);
+ return CSR_SUCCESS;
+}
+
+/**
+ * bool_str_toggle - Toggle the value of a bool
+ * @param cs Config items
+ * @param name Name of config item
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int bool_str_toggle(struct ConfigSet *cs, const char *name, struct Buffer *err)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(err, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return bool_he_toggle(cs, he, err);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a boolean
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_BOOL_H
+#define _CONFIG_BOOL_H
+
+struct Buffer;
+struct ConfigSet;
+struct HashElem;
+
+extern const char *bool_values[];
+
+void bool_init(struct ConfigSet *cs);
+int bool_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err);
+int bool_str_toggle(struct ConfigSet *cs, const char *name, struct Buffer *err);
+
+#endif /* _CONFIG_BOOL_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a command
+ *
+ * @authors
+ * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-command Type: Command
+ *
+ * Type representing a command.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * command_destroy - Destroy a Command
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void command_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ const char **str = (const char **) var;
+ if (!*str)
+ return;
+
+ /* Don't free strings from the var definition */
+ if (*(char **) var == (char *) cdef->initial)
+ {
+ *(char **) var = NULL;
+ return;
+ }
+
+ FREE(var);
+}
+
+/**
+ * command_string_set - Set a Command by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int command_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ /* Store empty strings as NULL */
+ if (value && (value[0] == '\0'))
+ value = NULL;
+
+ if (!value && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ if (mutt_str_strcmp(value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ command_destroy(cs, var, cdef);
+
+ const char *str = mutt_str_strdup(value);
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ }
+ else
+ {
+ /* we're already using the initial value */
+ if (*(char **) cdef->var == (char *) cdef->initial)
+ *(char **) cdef->var = mutt_str_strdup((char *) cdef->initial);
+
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * command_string_get - Get a Command as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int command_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ if (var)
+ str = *(const char **) var;
+ else
+ str = (char *) cdef->initial;
+
+ if (!str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * command_native_set - Set a Command config item by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Native pointer/value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int command_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value,
+ struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = (const char *) value;
+
+ /* Store empty strings as NULL */
+ if (str && (str[0] == '\0'))
+ value = 0;
+
+ if ((value == 0) && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (mutt_str_strcmp((const char *) value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ command_destroy(cs, var, cdef);
+
+ str = mutt_str_strdup(str);
+ rc = CSR_SUCCESS;
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ return rc;
+}
+
+/**
+ * command_native_get - Get a string from a Command config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Command string
+ */
+static intptr_t command_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ const char *str = *(const char **) var;
+
+ return (intptr_t) str;
+}
+
+/**
+ * command_reset - Reset a Command to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int command_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc = CSR_SUCCESS;
+
+ const char *command = (const char *) cdef->initial;
+ if (!command)
+ rc |= CSR_SUC_EMPTY;
+
+ if (mutt_str_strcmp(command, (*(char **) var)) == 0)
+ return (rc | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ command_destroy(cs, var, cdef);
+
+ if (!command)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = command;
+ return rc;
+}
+
+/**
+ * command_init - Register the Command config type
+ * @param cs Config items
+ */
+void command_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_command = {
+ "command", command_string_set, command_string_get,
+ command_native_set, command_native_get, command_reset,
+ command_destroy,
+ };
+ cs_register_type(cs, DT_COMMAND, &cst_command);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a command
+ *
+ * @authors
+ * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_COMMAND_H
+#define _CONFIG_COMMAND_H
+
+struct ConfigSet;
+
+void command_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_COMMAND_H */
--- /dev/null
+/**
+ * @file
+ * Dump all the config
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-dump Dump all the config
+ *
+ * Dump all the config items in various formats.
+ */
+
+#include "config.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mutt/buffer.h"
+#include "mutt/hash.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "dump.h"
+#include "set.h"
+#include "types.h"
+
+void mutt_pretty_mailbox(char *s, size_t buflen)
+{
+}
+
+/**
+ * escape_string - Write a string to a buffer, escaping special characters
+ * @param buf Buffer to write to
+ * @param src String to write
+ * @retval num Bytes written to buffer
+ */
+size_t escape_string(struct Buffer *buf, const char *src)
+{
+ if (!buf || !src)
+ return 0;
+
+ size_t len = 0;
+ for (; *src; src++)
+ {
+ switch (*src)
+ {
+ case '\n':
+ len += mutt_buffer_addstr(buf, "\\n");
+ break;
+ case '\r':
+ len += mutt_buffer_addstr(buf, "\\r");
+ break;
+ case '\t':
+ len += mutt_buffer_addstr(buf, "\\t");
+ break;
+ default:
+ if ((*src == '\\') || (*src == '"'))
+ len += mutt_buffer_addch(buf, '\\');
+ len += mutt_buffer_addch(buf, src[0]);
+ }
+ }
+ return len;
+}
+
+/**
+ * pretty_var - Escape and stringify a config item value
+ * @param str String to escape
+ * @param buf Buffer to write to
+ * @retval num Number of bytes written to buffer
+ */
+size_t pretty_var(const char *str, struct Buffer *buf)
+{
+ if (!buf || !str)
+ return 0;
+
+ int len = 0;
+
+ len += mutt_buffer_addch(buf, '"');
+ len += escape_string(buf, str);
+ len += mutt_buffer_addch(buf, '"');
+
+ return len;
+}
+
+/**
+ * elem_list_sort - Sort two HashElem pointers to config
+ * @param a First HashElem
+ * @param b Second HashElem
+ * @retval -1 a precedes b
+ * @retval 0 a and b are identical
+ * @retval 1 b precedes a
+ */
+int elem_list_sort(const void *a, const void *b)
+{
+ const struct HashElem *hea = *(struct HashElem **) a;
+ const struct HashElem *heb = *(struct HashElem **) b;
+
+ return mutt_str_strcasecmp(hea->key.strkey, heb->key.strkey);
+}
+
+/**
+ * get_elem_list - Create a sorted list of all config items
+ * @param cs ConfigSet to read
+ * @retval ptr Null-terminated array of HashElem
+ */
+struct HashElem **get_elem_list(struct ConfigSet *cs)
+{
+ if (!cs)
+ return NULL;
+
+ struct HashElem **list = mutt_mem_calloc(1024, sizeof(struct HashElem *));
+ size_t index = 0;
+
+ struct HashWalkState walk;
+ memset(&walk, 0, sizeof(walk));
+
+ struct HashElem *he = NULL;
+ while ((he = mutt_hash_walk(cs->hash, &walk)))
+ {
+ list[index++] = he;
+ if (index == 1022)
+ {
+ mutt_debug(1, "Too many config items to sort\n");
+ break;
+ }
+ }
+
+ qsort(list, index, sizeof(struct HashElem *), elem_list_sort);
+
+ return list;
+}
+
+/**
+ * dump_config_mutt - Dump the config in the style of Mutt
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param value Current value of the config item
+ * @param initial Initial value of the config item
+ * @param flags Flags, e.g. #CS_DUMP_ONLY_CHANGED
+ */
+void dump_config_mutt(struct ConfigSet *cs, struct HashElem *he,
+ struct Buffer *value, struct Buffer *initial, int flags)
+{
+ const char *name = he->key.strkey;
+
+ if (DTYPE(he->type) == DT_BOOL)
+ {
+ if ((value->data[0] == 'y') || ((value->data[0] == '"') && (value->data[1] == 'y')))
+ {
+ printf("%s is set\n", name);
+ }
+ else
+ {
+ printf("%s is unset\n", name);
+ }
+ }
+ else
+ {
+ printf("%s=%s\n", name, value->data);
+ }
+}
+
+/**
+ * dump_config_neo - Dump the config in the style of NeoMutt
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param value Current value of the config item
+ * @param initial Initial value of the config item
+ * @param flags Flags, e.g. #CS_DUMP_ONLY_CHANGED
+ */
+void dump_config_neo(struct ConfigSet *cs, struct HashElem *he,
+ struct Buffer *value, struct Buffer *initial, int flags)
+{
+ const char *name = he->key.strkey;
+
+ if ((flags & CS_DUMP_ONLY_CHANGED) && (mutt_str_strcmp(value->data, initial->data) == 0))
+ return;
+
+ if (he->type == DT_SYNONYM)
+ {
+ const struct ConfigDef *cdef = he->data;
+ const char *syn = (const char *) cdef->initial;
+ printf("# synonym: %s -> %s\n", name, syn);
+ return;
+ }
+
+ bool show_name = !(flags & CS_DUMP_HIDE_NAME);
+ bool show_value = !(flags & CS_DUMP_HIDE_VALUE);
+
+ if (show_name && show_value)
+ printf("set ");
+ if (show_name)
+ printf("%s", name);
+ if (show_name && show_value)
+ printf(" = ");
+ if (show_value)
+ printf("%s", value->data);
+ if (show_name || show_value)
+ printf("\n");
+
+ if (flags & CS_DUMP_SHOW_DEFAULTS)
+ {
+ const struct ConfigSetType *cst = cs_get_type_def(cs, he->type);
+ printf("# %s %s %s\n", cst->name, name, value->data);
+ }
+}
+
+/**
+ * dump_config - Write all the config to stdout
+ * @param cs ConfigSet to dump
+ * @param style Output style, e.g. #CS_DUMP_STYLE_MUTT
+ * @param flags Display flags, e.g. #CS_DUMP_ONLY_CHANGED
+ */
+bool dump_config(struct ConfigSet *cs, int style, int flags)
+{
+ if (!cs)
+ return false;
+
+ struct HashElem *he = NULL;
+
+ struct HashElem **list = get_elem_list(cs);
+ if (!list)
+ return false;
+
+ bool result = true;
+
+ struct Buffer *value = mutt_buffer_alloc(1024);
+ struct Buffer *initial = mutt_buffer_alloc(1024);
+ struct Buffer *tmp = mutt_buffer_alloc(1024);
+
+ for (size_t i = 0; list[i]; i++)
+ {
+ mutt_buffer_reset(value);
+ mutt_buffer_reset(initial);
+ he = list[i];
+ const int type = DTYPE(he->type);
+
+ if ((type == DT_SYNONYM) && !(flags & CS_DUMP_SHOW_SYNONYMS))
+ continue;
+
+ if ((type == DT_DISABLED) && !(flags & CS_DUMP_SHOW_DISABLED))
+ continue;
+
+ if (type != DT_SYNONYM)
+ {
+ /* If necessary, get the current value */
+ if ((flags & CS_DUMP_ONLY_CHANGED) || !(flags & CS_DUMP_HIDE_VALUE) ||
+ (flags & CS_DUMP_SHOW_DEFAULTS))
+ {
+ int rc = cs_he_string_get(cs, he, value);
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ result = false;
+ break;
+ }
+
+ const struct ConfigDef *cdef = he->data;
+ if (IS_SENSITIVE(*cdef) && (flags & CS_DUMP_HIDE_SENSITIVE) &&
+ !mutt_buffer_is_empty(value))
+ {
+ mutt_buffer_reset(value);
+ mutt_buffer_addstr(value, "***");
+ }
+
+ if (type == DT_PATH)
+ mutt_pretty_mailbox(value->data, value->dsize);
+
+ if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_QUAD) &&
+ !(flags & CS_DUMP_NO_ESCAPING))
+ {
+ mutt_buffer_reset(tmp);
+ size_t len = pretty_var(value->data, tmp);
+ mutt_str_strfcpy(value->data, tmp->data, len + 1);
+ }
+ }
+
+ /* If necessary, get the default value */
+ if (flags & (CS_DUMP_ONLY_CHANGED | CS_DUMP_SHOW_DEFAULTS))
+ {
+ int rc = cs_he_initial_get(cs, he, initial);
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ result = false;
+ break;
+ }
+
+ if (type == DT_PATH)
+ mutt_pretty_mailbox(value->data, value->dsize);
+
+ if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_QUAD) &&
+ !(flags & CS_DUMP_NO_ESCAPING))
+ {
+ mutt_buffer_reset(tmp);
+ size_t len = pretty_var(initial->data, tmp);
+ mutt_str_strfcpy(value->data, tmp->data, len + 1);
+ }
+ }
+ }
+
+ if (style == CS_DUMP_STYLE_MUTT)
+ dump_config_mutt(cs, he, value, initial, flags);
+ else
+ dump_config_neo(cs, he, value, initial, flags);
+ }
+
+ FREE(&list);
+ mutt_buffer_free(&value);
+ mutt_buffer_free(&initial);
+ mutt_buffer_free(&tmp);
+
+ return result;
+}
--- /dev/null
+/**
+ * @file
+ * Dump all the config
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_DUMP_H
+#define _CONFIG_DUMP_H
+
+#include <stddef.h>
+
+struct ConfigSet;
+
+#define CS_DUMP_STYLE_MUTT 0 /**< Display config in Mutt style */
+#define CS_DUMP_STYLE_NEO 1 /**< Display config in NeoMutt style */
+
+#define CS_DUMP_ONLY_CHANGED (1 << 0) /**< Only show config that the user has changed */
+#define CS_DUMP_HIDE_SENSITIVE (1 << 1) /**< Obscure sensitive information like passwords */
+#define CS_DUMP_NO_ESCAPING (1 << 2) /**< Do not escape special chars, or quote the string */
+#define CS_DUMP_HIDE_NAME (1 << 3) /**< Do not print the name of the config item */
+#define CS_DUMP_HIDE_VALUE (1 << 4) /**< Do not print the value of the config item */
+#define CS_DUMP_SHOW_DEFAULTS (1 << 5) /**< Show the default value for the config item */
+#define CS_DUMP_SHOW_DISABLED (1 << 6) /**< Show disabled config items, too */
+#define CS_DUMP_SHOW_SYNONYMS (1 << 7) /**< Show synonyms and the config items their linked to */
+
+void dump_config_mutt(struct ConfigSet *cs, struct HashElem *he, struct Buffer *value, struct Buffer *initial, int flags);
+void dump_config_neo(struct ConfigSet *cs, struct HashElem *he, struct Buffer *value, struct Buffer *initial, int flags);
+bool dump_config(struct ConfigSet *cs, int style, int flags);
+int elem_list_sort(const void *a, const void *b);
+size_t escape_string(struct Buffer *buf, const char *src);
+struct HashElem **get_elem_list(struct ConfigSet *cs);
+size_t pretty_var(const char *str, struct Buffer *buf);
+
+#endif /* _CONFIG_DUMP_H */
--- /dev/null
+/**
+ * @file
+ * An inherited config item
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_INHERITANCE_H
+#define _CONFIG_INHERITANCE_H
+
+#include <stdint.h>
+
+/**
+ * struct Inheritance - An inherited config item
+ */
+struct Inheritance
+{
+ struct HashElem *parent; /**< HashElem of parent config item */
+ const char *name; /**< Name of this config item */
+ struct Account *ac; /**< Account holding this config item */
+ intptr_t var; /**< (Pointer to) value, of config item */
+};
+
+#endif /* _CONFIG_INHERITANCE_H */
--- /dev/null
+/**
+ * @file
+ * Convenience wrapper for the config headers
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config Flexible handling of config items
+ *
+ * User configurable variables.
+ *
+ * -# @subpage config-set
+ * -# @subpage config-dump
+ * -# @subpage config-address
+ * -# @subpage config-bool
+ * -# @subpage config-magic
+ * -# @subpage config-mbtable
+ * -# @subpage config-number
+ * -# @subpage config-path
+ * -# @subpage config-quad
+ * -# @subpage config-regex
+ * -# @subpage config-sort
+ * -# @subpage config-string
+ */
+
+#ifndef _CONFIG_CONFIG_H
+#define _CONFIG_CONFIG_H
+
+#include "address.h"
+#include "bool.h"
+#include "command.h"
+#include "dump.h"
+#include "inheritance.h"
+#include "long.h"
+#include "magic.h"
+#include "mbtable.h"
+#include "number.h"
+#include "path.h"
+#include "quad.h"
+#include "regex2.h"
+#include "set.h"
+#include "sort.h"
+#include "string3.h"
+#include "types.h"
+
+#endif /* _CONFIG_CONFIG_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a long
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-long Type: Long
+ *
+ * Type representing a long.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * long_string_set - Set a Long by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int long_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ long num = 0;
+ if (!value || !value[0] || (mutt_str_atol(value, &num) < 0))
+ {
+ mutt_buffer_printf(err, "Invalid long: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((num < LONG_MIN) || (num > LONG_MAX))
+ {
+ mutt_buffer_printf(err, "Long is too big: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((num < 0) && (cdef->type & DT_NOT_NEGATIVE))
+ {
+ mutt_buffer_printf(err, "Option %s may not be negative", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (var)
+ {
+ if (num == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) num, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = num;
+ }
+ else
+ {
+ cdef->initial = num;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * long_string_get - Get a Long as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int long_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int value;
+
+ if (var)
+ value = *(short *) var;
+ else
+ value = (int) cdef->initial;
+
+ mutt_buffer_printf(result, "%d", value);
+ return CSR_SUCCESS;
+}
+
+/**
+ * long_native_set - Set a Long config item by int
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Long
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int long_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if ((value < LONG_MIN) || (value > LONG_MAX))
+ {
+ mutt_buffer_printf(err, "Invalid long: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((value < 0) && (cdef->type & DT_NOT_NEGATIVE))
+ {
+ mutt_buffer_printf(err, "Option %s may not be negative", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (value == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * long_native_get - Get an int from a Long config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Long
+ */
+static intptr_t long_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(short *) var;
+}
+
+/**
+ * long_reset - Reset a Long to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int long_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * long_init - Register the Long config type
+ * @param cs Config items
+ */
+void long_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_long = {
+ "long",
+ long_string_set,
+ long_string_get,
+ long_native_set,
+ long_native_get,
+ long_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_LONG, &cst_long);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a long
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_LONG_H
+#define _CONFIG_LONG_H
+
+struct ConfigSet;
+
+void long_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_LONG_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a mailbox
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-magic Type: Mailbox types
+ *
+ * Type representing a mailbox.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * magic_values - Valid strings for mailbox types
+ */
+const char *magic_values[] = {
+ NULL, "mbox", "MMDF", "MH", "Maildir", NULL,
+};
+
+/**
+ * magic_string_set - Set a Mailbox Magic by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int magic_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef || !value)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int num = -1;
+ for (size_t i = 1; magic_values[i]; i++)
+ {
+ if (mutt_str_strcasecmp(magic_values[i], value) == 0)
+ {
+ num = i;
+ break;
+ }
+ }
+
+ if (num < 1)
+ {
+ mutt_buffer_printf(err, "Invalid magic value: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (var)
+ {
+ if (num == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) num, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = num;
+ }
+ else
+ {
+ cdef->initial = num;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * magic_string_get - Get a Mailbox Magic as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int magic_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ unsigned int value;
+
+ if (var)
+ value = *(short *) var;
+ else
+ value = (int) cdef->initial;
+
+ if ((value < 1) || (value >= (mutt_array_size(magic_values) - 1)))
+ {
+ mutt_debug(1, "Variable has an invalid value: %d\n", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ mutt_buffer_addstr(result, magic_values[value]);
+ return CSR_SUCCESS;
+}
+
+/**
+ * magic_native_set - Set a Mailbox Magic config item by int
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Mailbox magic
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int magic_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if ((value < 1) || (value >= (mutt_array_size(magic_values) - 1)))
+ {
+ mutt_buffer_printf(err, "Invalid magic value: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (value == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * magic_native_get - Get an int from a Mailbox Magic config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Mailbox magic
+ */
+static intptr_t magic_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(short *) var;
+}
+
+/**
+ * magic_reset - Reset a Mailbox Magic to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int magic_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * magic_init - Register the Mailbox Magic config type
+ * @param cs Config items
+ */
+void magic_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_magic = {
+ "magic",
+ magic_string_set,
+ magic_string_get,
+ magic_native_set,
+ magic_native_get,
+ magic_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_MAGIC, &cst_magic);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a mailbox
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_MAGIC_H
+#define _CONFIG_MAGIC_H
+
+struct ConfigSet;
+
+extern const char *magic_values[];
+
+/**
+ * enum MailboxTypes - Supported mailbox formats
+ */
+enum MailboxTypes
+{
+ MUTT_MBOX = 1,
+ MUTT_MMDF,
+ MUTT_MH,
+ MUTT_MAILDIR,
+ MUTT_NNTP,
+ MUTT_IMAP,
+ MUTT_NOTMUCH,
+ MUTT_POP,
+ MUTT_COMPRESSED,
+};
+
+void magic_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_MAGIC_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a multibyte character table
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-mbtable Type: Multi-byte character table
+ *
+ * Type representing a multibyte character table.
+ */
+
+#include "config.h"
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <wchar.h>
+#include "mutt/buffer.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "config/mbtable.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * mbtable_parse - Parse a multibyte string into a table
+ * @param s String of multibyte characters
+ * @retval ptr New MbTable object
+ */
+struct MbTable *mbtable_parse(const char *s)
+{
+ struct MbTable *t = NULL;
+ size_t slen, k;
+ mbstate_t mbstate;
+ char *d = NULL;
+
+ slen = mutt_str_strlen(s);
+ if (!slen)
+ return NULL;
+
+ t = mutt_mem_calloc(1, sizeof(struct MbTable));
+
+ t->orig_str = mutt_str_strdup(s);
+ /* This could be more space efficient. However, being used on tiny
+ * strings (Tochars and StatusChars), the overhead is not great. */
+ t->chars = mutt_mem_calloc(slen, sizeof(char *));
+ d = t->segmented_str = mutt_mem_calloc(slen * 2, sizeof(char));
+
+ memset(&mbstate, 0, sizeof(mbstate));
+ while (slen && (k = mbrtowc(NULL, s, slen, &mbstate)))
+ {
+ if (k == (size_t)(-1) || k == (size_t)(-2))
+ {
+ /* XXX put message in err buffer; fail? warning? */
+ mutt_debug(1, "mbtable_parse: mbrtowc returned %d converting %s in %s\n",
+ (k == (size_t)(-1)) ? -1 : -2, s, t->orig_str);
+ if (k == (size_t)(-1))
+ memset(&mbstate, 0, sizeof(mbstate));
+ k = (k == (size_t)(-1)) ? 1 : slen;
+ }
+
+ slen -= k;
+ t->chars[t->len++] = d;
+ while (k--)
+ *d++ = *s++;
+ *d++ = '\0';
+ }
+
+ return t;
+}
+
+/**
+ * mbtable_destroy - Destroy an MbTable object
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void mbtable_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ struct MbTable **m = (struct MbTable **) var;
+ if (!*m)
+ return;
+
+ mbtable_free(m);
+}
+
+/**
+ * mbtable_string_set - Set a MbTable by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int mbtable_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (value && (value[0] == '\0'))
+ value = NULL;
+
+ struct MbTable *table = NULL;
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ struct MbTable *curval = *(struct MbTable **) var;
+ if (curval && (mutt_str_strcmp(value, curval->orig_str) == 0))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ table = mbtable_parse(value);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) table, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ mbtable_free(&table);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ mbtable_destroy(cs, var, cdef);
+
+ *(struct MbTable **) var = table;
+
+ if (!table)
+ rc |= CSR_SUC_EMPTY;
+ }
+ else
+ {
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * mbtable_string_get - Get a MbTable as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int mbtable_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ if (var)
+ {
+ struct MbTable *table = *(struct MbTable **) var;
+ if (!table || !table->orig_str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+ str = table->orig_str;
+ }
+ else
+ {
+ str = (char *) cdef->initial;
+ }
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * mbtable_dup - Create a copy of an MbTable object
+ * @param table MbTable to duplicate
+ * @retval ptr New MbTable object
+ */
+static struct MbTable *mbtable_dup(struct MbTable *table)
+{
+ if (!table)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ struct MbTable *m = mutt_mem_calloc(1, sizeof(*m));
+ m->orig_str = mutt_str_strdup(table->orig_str);
+ return m;
+}
+
+/**
+ * mbtable_native_set - Set a MbTable config item by MbTable object
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value MbTable pointer
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int mbtable_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value,
+ struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ mbtable_free(var);
+
+ struct MbTable *table = mbtable_dup((struct MbTable *) value);
+
+ rc = CSR_SUCCESS;
+ if (!table)
+ rc |= CSR_SUC_EMPTY;
+
+ *(struct MbTable **) var = table;
+ return rc;
+}
+
+/**
+ * mbtable_native_get - Get an MbTable object from a MbTable config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t MbTable pointer
+ */
+static intptr_t mbtable_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ struct MbTable *table = *(struct MbTable **) var;
+
+ return (intptr_t) table;
+}
+
+/**
+ * mbtable_reset - Reset an MbTable to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int mbtable_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct MbTable *table = NULL;
+ const char *initial = (const char *) cdef->initial;
+
+ struct MbTable *curtable = *(struct MbTable **) var;
+ const char *curval = curtable ? curtable->orig_str : NULL;
+
+ int rc = CSR_SUCCESS;
+ if (!curtable)
+ rc |= CSR_SUC_EMPTY;
+
+ if (mutt_str_strcmp(initial, curval) == 0)
+ return (rc | CSR_SUC_NO_CHANGE);
+
+ if (initial)
+ table = mbtable_parse(initial);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) table, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ mbtable_destroy(cs, &table, cdef);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ if (!table)
+ rc |= CSR_SUC_EMPTY;
+
+ mbtable_destroy(cs, var, cdef);
+
+ *(struct MbTable **) var = table;
+ return rc;
+}
+
+/**
+ * mbtable_init - Register the MbTable config type
+ * @param cs Config items
+ */
+void mbtable_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_mbtable = {
+ "mbtable", mbtable_string_set, mbtable_string_get,
+ mbtable_native_set, mbtable_native_get, mbtable_reset,
+ mbtable_destroy,
+ };
+ cs_register_type(cs, DT_MBTABLE, &cst_mbtable);
+}
+
+/**
+ * mbtable_free - Free an MbTable object
+ * @param table MbTable to free
+ */
+void mbtable_free(struct MbTable **table)
+{
+ if (!table || !*table)
+ return; /* LCOV_EXCL_LINE */
+
+ FREE(&(*table)->orig_str);
+ FREE(&(*table)->chars);
+ FREE(&(*table)->segmented_str);
+ FREE(table);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a multibyte character table
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_MBTABLE_H
+#define _CONFIG_MBTABLE_H
+
+struct ConfigSet;
+
+/**
+ * struct MbTable - multibyte character table
+ *
+ * Allows for direct access to the individual multibyte characters in a
+ * string. This is used for the Flagchars, Fromchars, StatusChars and Tochars
+ * option types.
+ */
+struct MbTable
+{
+ char *orig_str; /**< Original string used to generate this object */
+ int len; /**< Number of characters */
+ char **chars; /**< The array of multibyte character strings */
+ char *segmented_str; /**< Each chars entry points inside this string */
+};
+
+void mbtable_init(struct ConfigSet *cs);
+struct MbTable *mbtable_parse(const char *str);
+void mbtable_free(struct MbTable **table);
+
+#endif /* _CONFIG_MBTABLE_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a number
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-number Type: Number
+ *
+ * Type representing a number.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * number_string_set - Set a Number by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int number_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int num = 0;
+ if (!value || !value[0] || (mutt_str_atoi(value, &num) < 0))
+ {
+ mutt_buffer_printf(err, "Invalid number: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((num < SHRT_MIN) || (num > SHRT_MAX))
+ {
+ mutt_buffer_printf(err, "Number is too big: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((num < 0) && (cdef->type & DT_NOT_NEGATIVE))
+ {
+ mutt_buffer_printf(err, "Option %s may not be negative", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (var)
+ {
+ if (num == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) num, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = num;
+ }
+ else
+ {
+ cdef->initial = num;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * number_string_get - Get a Number as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int number_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int value;
+
+ if (var)
+ value = *(short *) var;
+ else
+ value = (int) cdef->initial;
+
+ mutt_buffer_printf(result, "%d", value);
+ return CSR_SUCCESS;
+}
+
+/**
+ * number_native_set - Set a Number config item by int
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Number
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int number_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value,
+ struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if ((value < SHRT_MIN) || (value > SHRT_MAX))
+ {
+ mutt_buffer_printf(err, "Invalid number: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if ((value < 0) && (cdef->type & DT_NOT_NEGATIVE))
+ {
+ mutt_buffer_printf(err, "Option %s may not be negative", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (value == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * number_native_get - Get an int from a Number config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Number
+ */
+static intptr_t number_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(short *) var;
+}
+
+/**
+ * number_reset - Reset a Number to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int number_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * number_init - Register the Number config type
+ * @param cs Config items
+ */
+void number_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_number = {
+ "number",
+ number_string_set,
+ number_string_get,
+ number_native_set,
+ number_native_get,
+ number_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_NUMBER, &cst_number);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a number
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_NUMBER_H
+#define _CONFIG_NUMBER_H
+
+struct ConfigSet;
+
+void number_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_NUMBER_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a path
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-path Type: Path
+ *
+ * Type representing a path.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * path_destroy - Destroy a Path
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void path_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ const char **str = (const char **) var;
+ if (!*str)
+ return;
+
+ /* Don't free strings from the var definition */
+ if (*(char **) var == (char *) cdef->initial)
+ {
+ *(char **) var = NULL;
+ return;
+ }
+
+ FREE(var);
+}
+
+/**
+ * path_string_set - Set a Path by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int path_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ /* Store empty strings as NULL */
+ if (value && (value[0] == '\0'))
+ value = NULL;
+
+ if (!value && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ if (mutt_str_strcmp(value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ path_destroy(cs, var, cdef);
+
+ const char *str = mutt_str_strdup(value);
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ }
+ else
+ {
+ /* we're already using the initial value */
+ if (*(char **) cdef->var == (char *) cdef->initial)
+ *(char **) cdef->var = mutt_str_strdup((char *) cdef->initial);
+
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * path_string_get - Get a Path as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int path_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ if (var)
+ str = *(const char **) var;
+ else
+ str = (char *) cdef->initial;
+
+ if (!str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * path_native_set - Set a Path config item by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Native pointer/value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int path_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = (const char *) value;
+
+ /* Store empty strings as NULL */
+ if (str && (str[0] == '\0'))
+ value = 0;
+
+ if ((value == 0) && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (mutt_str_strcmp((const char *) value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ path_destroy(cs, var, cdef);
+
+ str = mutt_str_strdup(str);
+ rc = CSR_SUCCESS;
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ return rc;
+}
+
+/**
+ * path_native_get - Get a string from a Path config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Path string
+ */
+static intptr_t path_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ const char *str = *(const char **) var;
+
+ return (intptr_t) str;
+}
+
+/**
+ * path_reset - Reset a Path to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int path_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc = CSR_SUCCESS;
+
+ const char *path = (const char *) cdef->initial;
+ if (!path)
+ rc |= CSR_SUC_EMPTY;
+
+ if (mutt_str_strcmp(path, (*(char **) var)) == 0)
+ return (rc | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ path_destroy(cs, var, cdef);
+
+ if (!path)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = path;
+ return rc;
+}
+
+/**
+ * path_init - Register the Path config type
+ * @param cs Config items
+ */
+void path_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_path = {
+ "path", path_string_set, path_string_get, path_native_set,
+ path_native_get, path_reset, path_destroy,
+ };
+ cs_register_type(cs, DT_PATH, &cst_path);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a path
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_PATH_H
+#define _CONFIG_PATH_H
+
+struct ConfigSet;
+
+void path_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_PATH_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a quad-option
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-quad Type: Quad-option
+ *
+ * Type representing a quad-option.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/hash.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * quad_values - Valid strings for creating a QuadValue
+ *
+ * These strings are case-insensitive.
+ */
+const char *quad_values[] = {
+ "no", "yes", "ask-no", "ask-yes", NULL,
+};
+
+/**
+ * quad_string_set - Set a Quad-option by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int quad_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef || !value)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int num = -1;
+ for (size_t i = 0; quad_values[i]; i++)
+ {
+ if (mutt_str_strcasecmp(quad_values[i], value) == 0)
+ {
+ num = i;
+ break;
+ }
+ }
+
+ if (num < 0)
+ {
+ mutt_buffer_printf(err, "Invalid quad value: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (var)
+ {
+ if (num == (*(char *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) num, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(char *) var = num;
+ }
+ else
+ {
+ cdef->initial = num;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * quad_string_get - Get a Quad-option as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int quad_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ unsigned int value;
+
+ if (var)
+ value = *(char *) var;
+ else
+ value = (int) cdef->initial;
+
+ if (value >= (mutt_array_size(quad_values) - 1))
+ {
+ mutt_debug(1, "Variable has an invalid value: %d\n", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ mutt_buffer_addstr(result, quad_values[value]);
+ return CSR_SUCCESS;
+}
+
+/**
+ * quad_native_set - Set a Quad-option config item by int
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Quad-option value
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int quad_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if ((value < 0) || (value >= (mutt_array_size(quad_values) - 1)))
+ {
+ mutt_buffer_printf(err, "Invalid quad value: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (value == (*(char *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(char *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * quad_native_get - Get an int object from a Quad-option config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Quad-option value
+ */
+static intptr_t quad_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(char *) var;
+}
+
+/**
+ * quad_reset - Reset a Quad-option to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int quad_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(char *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(char *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * quad_init - Register the Quad-option config type
+ * @param cs Config items
+ */
+void quad_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_quad = {
+ "quad",
+ quad_string_set,
+ quad_string_get,
+ quad_native_set,
+ quad_native_get,
+ quad_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_QUAD, &cst_quad);
+}
+
+/**
+ * quad_toggle - Toggle (invert) the value of a quad option
+ *
+ * By toggling the low bit, the following are swapped:
+ * - #MUTT_NO <--> #MUTT_YES
+ * - #MUTT_ASKNO <--> #MUTT_ASKYES
+ */
+static int quad_toggle(int opt)
+{
+ return opt ^= 1;
+}
+
+/**
+ * quad_he_toggle - Toggle the value of a quad
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * @sa quad_toggle()
+ */
+int quad_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
+{
+ if (!cs || !he || !he->data)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (DTYPE(he->type) != DT_QUAD)
+ return CSR_ERR_CODE;
+
+ const struct ConfigDef *cdef = he->data;
+ char *var = cdef->var;
+
+ char value = *var;
+ if ((value < 0) || (value >= (mutt_array_size(quad_values) - 1)))
+ {
+ mutt_buffer_printf(err, "Invalid quad value: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ *(char *) var = quad_toggle(value);
+
+ cs_notify_listeners(cs, he, he->key.strkey, CE_SET);
+ return CSR_SUCCESS;
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a quad-option
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_QUAD_H
+#define _CONFIG_QUAD_H
+
+struct Buffer;
+struct ConfigSet;
+struct HashElem;
+
+extern const char *quad_values[];
+
+/**
+ * enum QuadOption - Possible values for a quad-option
+ */
+enum QuadOption
+{
+ MUTT_ABORT = -1,
+ MUTT_NO,
+ MUTT_YES,
+ MUTT_ASKNO,
+ MUTT_ASKYES
+};
+
+void quad_init(struct ConfigSet *cs);
+int quad_he_toggle(struct ConfigSet *cs, struct HashElem *he, struct Buffer *err);
+
+#endif /* _CONFIG_QUAD_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a regular expression
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-regex Type: Regular expression
+ *
+ * Type representing a regular expression.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/mbyte.h"
+#include "mutt/memory.h"
+#include "mutt/regex3.h"
+#include "mutt/string2.h"
+#include "regex2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * regex_destroy - Destroy a Regex object
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void regex_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ struct Regex **r = (struct Regex **) var;
+ if (!*r)
+ return;
+
+ regex_free(r);
+}
+
+/**
+ * regex_string_set - Set a Regex by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int regex_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ /* Store empty strings as NULL */
+ if (value && (value[0] == '\0'))
+ value = NULL;
+
+ struct Regex *r = NULL;
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ struct Regex *curval = *(struct Regex **) var;
+ if (curval && (mutt_str_strcmp(value, curval->pattern) == 0))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (value)
+ {
+ r = regex_create(value, cdef->type, err);
+ if (!r)
+ return CSR_ERR_INVALID;
+ }
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) r, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ regex_free(&r);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ regex_destroy(cs, var, cdef);
+
+ *(struct Regex **) var = r;
+
+ if (!r)
+ rc |= CSR_SUC_EMPTY;
+ }
+ else
+ {
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * regex_string_get - Get a Regex as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int regex_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ if (var)
+ {
+ struct Regex *r = *(struct Regex **) var;
+ if (r)
+ str = r->pattern;
+ }
+ else
+ {
+ str = (char *) cdef->initial;
+ }
+
+ if (!str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * regex_native_set - Set a Regex config item by Regex object
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Regex pointer
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int regex_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ rc = CSR_SUCCESS;
+ struct Regex *orig = (struct Regex *) value;
+ struct Regex *r = NULL;
+
+ if (orig && orig->pattern)
+ {
+ r = regex_create(orig->pattern, cdef->flags, err);
+ if (!r)
+ rc = CSR_ERR_INVALID;
+ }
+ else
+ {
+ rc |= CSR_SUC_EMPTY;
+ }
+
+ if (CSR_RESULT(rc) == CSR_SUCCESS)
+ {
+ regex_free(var);
+ *(struct Regex **) var = r;
+ }
+
+ return rc;
+}
+
+/**
+ * regex_native_get - Get a Regex object from a Regex config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Regex pointer
+ */
+static intptr_t regex_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ struct Regex *r = *(struct Regex **) var;
+
+ return (intptr_t) r;
+}
+
+/**
+ * regex_reset - Reset a Regex to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int regex_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Regex *r = NULL;
+ const char *initial = (const char *) cdef->initial;
+
+ struct Regex *currx = *(struct Regex **) var;
+ const char *curval = currx ? currx->pattern : NULL;
+
+ int rc = CSR_SUCCESS;
+ if (!currx)
+ rc |= CSR_SUC_EMPTY;
+
+ if (mutt_str_strcmp(initial, curval) == 0)
+ return (rc | CSR_SUC_NO_CHANGE);
+
+ if (initial)
+ {
+ r = regex_create(initial, cdef->flags, err);
+ if (!r)
+ return CSR_ERR_CODE;
+ }
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) r, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ {
+ regex_destroy(cs, &r, cdef);
+ return (rc | CSR_INV_VALIDATOR);
+ }
+ }
+
+ if (!r)
+ rc |= CSR_SUC_EMPTY;
+
+ regex_destroy(cs, var, cdef);
+
+ *(struct Regex **) var = r;
+ return rc;
+}
+
+/**
+ * regex_init - Register the Regex config type
+ * @param cs Config items
+ */
+void regex_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_regex = {
+ "regex", regex_string_set, regex_string_get, regex_native_set,
+ regex_native_get, regex_reset, regex_destroy,
+ };
+ cs_register_type(cs, DT_REGEX, &cst_regex);
+}
+
+/**
+ * regex_create - Create an Regex from a string
+ * @param str Regular expression
+ * @param flags Type flags, e.g. #DT_REGEX_MATCH_CASE
+ * @param err Buffer for error messages
+ * @retval ptr New Regex object
+ * @retval NULL Error
+ */
+struct Regex *regex_create(const char *str, int flags, struct Buffer *err)
+{
+ if (!str)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ int rflags = 0;
+ struct Regex *reg = mutt_mem_calloc(1, sizeof(struct Regex));
+
+ reg->regex = mutt_mem_calloc(1, sizeof(regex_t));
+ reg->pattern = mutt_str_strdup(str);
+
+ /* Should we use smart case matching? */
+ if (((flags & DT_REGEX_MATCH_CASE) == 0) && mutt_mb_is_lower(str))
+ rflags |= REG_ICASE;
+
+ if ((flags & DT_REGEX_NOSUB))
+ rflags |= REG_NOSUB;
+
+ /* Is a prefix of '!' allowed? */
+ if (((flags & DT_REGEX_ALLOW_NOT) != 0) && (str[0] == '!'))
+ {
+ reg->not = true;
+ str++;
+ }
+
+ int rc = REGCOMP(reg->regex, str, rflags);
+ if ((rc != 0) && err)
+ {
+ regerror(rc, reg->regex, err->data, err->dsize);
+ regex_free(®);
+ return NULL;
+ }
+
+ return reg;
+}
+
+/**
+ * regex_free - Free a Regex object
+ * @param r Regex to free
+ */
+void regex_free(struct Regex **r)
+{
+ if (!r || !*r)
+ return; /* LCOV_EXCL_LINE */
+
+ FREE(&(*r)->pattern);
+ if ((*r)->regex)
+ regfree((*r)->regex);
+ FREE(&(*r)->regex);
+ FREE(r);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a regular expression
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_REGEX_H
+#define _CONFIG_REGEX_H
+
+#include <features.h>
+#include <regex.h>
+#include <stdbool.h>
+
+struct Buffer;
+struct ConfigSet;
+
+void regex_init(struct ConfigSet *cs);
+struct Regex *regex_create(const char *str, int flags, struct Buffer *err);
+void regex_free(struct Regex **regex);
+
+#endif /* _CONFIG_REGEX_H */
--- /dev/null
+/**
+ * @file
+ * A collection of config items
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-set Config Set
+ *
+ * A collection of config items.
+ */
+
+#include "config.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mutt/buffer.h"
+#include "mutt/hash.h"
+#include "mutt/logging.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "inheritance.h"
+#include "types.h"
+
+struct ConfigSetType RegisteredTypes[18] = {
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+};
+
+/**
+ * destroy - Callback function for the Hash Table
+ * @param type Object type, e.g. #DT_STRING
+ * @param obj Object to destroy
+ * @param data ConfigSet associated with the object
+ */
+static void destroy(int type, void *obj, intptr_t data)
+{
+ if (!obj || (data == 0))
+ return; /* LCOV_EXCL_LINE */
+
+ struct ConfigSet *cs = (struct ConfigSet *) data;
+
+ const struct ConfigSetType *cst = NULL;
+
+ if (type & DT_INHERITED)
+ {
+ struct Inheritance *i = obj;
+ FREE(&i->name);
+ FREE(&i);
+ }
+ else
+ {
+ struct ConfigDef *cdef = obj;
+
+ cst = cs_get_type_def(cs, type);
+ if (cst && cst->destroy)
+ cst->destroy(cs, cdef->var, cdef);
+
+ /* If we allocated the initial value, clean it up */
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+ }
+}
+
+/**
+ * create_synonym - Create an alternative name for a config item
+ * @param cs Config items
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval ptr New HashElem representing the config item synonym
+ */
+static struct HashElem *create_synonym(const struct ConfigSet *cs,
+ struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ const char *name = (const char *) cdef->initial;
+ struct HashElem *parent = cs_get_elem(cs, name);
+ if (!parent)
+ {
+ mutt_buffer_printf(err, "No such variable: %s", name);
+ return NULL;
+ }
+
+ struct HashElem *child =
+ mutt_hash_typed_insert(cs->hash, cdef->name, cdef->type, (void *) cdef);
+ if (!child)
+ return NULL;
+
+ cdef->var = parent;
+ return child;
+}
+
+/**
+ * reg_one_var - Register one config item
+ * @param cs Config items
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval ptr New HashElem representing the config item
+ */
+static struct HashElem *reg_one_var(const struct ConfigSet *cs,
+ struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ if (cdef->type == DT_SYNONYM)
+ return create_synonym(cs, cdef, err);
+
+ const struct ConfigSetType *cst = cs_get_type_def(cs, cdef->type);
+ if (!cst)
+ {
+ mutt_buffer_printf(err, "Variable '%s' has an invalid type %d", cdef->name, cdef->type);
+ return NULL;
+ }
+
+ struct HashElem *he =
+ mutt_hash_typed_insert(cs->hash, cdef->name, cdef->type, (void *) cdef);
+ if (!he)
+ return NULL;
+
+ if (cst && cst->reset)
+ cst->reset(cs, cdef->var, cdef, err);
+
+ return he;
+}
+
+/**
+ * cs_create - Create a new Config Set
+ * @param size Number of expected config items
+ * @retval ptr New ConfigSet object
+ */
+struct ConfigSet *cs_create(size_t size)
+{
+ struct ConfigSet *cs = mutt_mem_malloc(sizeof(*cs));
+ cs_init(cs, size);
+ return cs;
+}
+
+/**
+ * cs_init - Initialise a Config Set
+ * @param cs Config items
+ * @param size Number of expected config items
+ */
+void cs_init(struct ConfigSet *cs, size_t size)
+{
+ if (!cs)
+ return; /* LCOV_EXCL_LINE */
+
+ memset(cs, 0, sizeof(*cs));
+ cs->hash = mutt_hash_create(size, 0);
+ mutt_hash_set_destructor(cs->hash, destroy, (intptr_t) cs);
+}
+
+/**
+ * cs_free - Free a Config Set
+ * @param cs Config items
+ */
+void cs_free(struct ConfigSet **cs)
+{
+ if (!cs || !*cs)
+ return; /* LCOV_EXCL_LINE */
+
+ mutt_hash_destroy(&(*cs)->hash);
+ FREE(cs);
+}
+
+/**
+ * cs_get_elem - Get the HashElem representing a config item
+ * @param cs Config items
+ * @param name Name of config item
+ * @retval ptr HashElem representing the config item
+ */
+struct HashElem *cs_get_elem(const struct ConfigSet *cs, const char *name)
+{
+ if (!cs || !name)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = mutt_hash_find_elem(cs->hash, name);
+ if (!he)
+ return NULL;
+
+ if (he->type != DT_SYNONYM)
+ return he;
+
+ const struct ConfigDef *cdef = he->data;
+
+ return cdef->var;
+}
+
+/**
+ * cs_get_type_def - Get the definition for a type
+ * @param cs Config items
+ * @param type Type to lookup, e.g. #DT_NUMBER
+ * @retval ptr ConfigSetType representing the type
+ */
+const struct ConfigSetType *cs_get_type_def(const struct ConfigSet *cs, unsigned int type)
+{
+ if (!cs)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ type = DTYPE(type);
+ if ((type < 1) || (type >= mutt_array_size(cs->types)))
+ return NULL;
+
+ if (!cs->types[type].name)
+ return NULL;
+
+ return &cs->types[type];
+}
+
+/**
+ * cs_register_type - Register a type of config item
+ * @param cs Config items
+ * @param type Object type, e.g. #DT_BOOL
+ * @param cst Structure defining the type
+ * @retval bool True, if type was registered successfully
+ */
+bool cs_register_type(struct ConfigSet *cs, unsigned int type, const struct ConfigSetType *cst)
+{
+ if (!cs || !cst)
+ return false; /* LCOV_EXCL_LINE */
+
+ if (!cst->name || !cst->string_set || !cst->string_get || !cst->reset ||
+ !cst->native_set || !cst->native_get)
+ return false;
+
+ if (type >= mutt_array_size(cs->types))
+ return false;
+
+ if (cs->types[type].name)
+ return false; /* already registered */
+
+ cs->types[type] = *cst;
+ return true;
+}
+
+/**
+ * cs_register_variables - Register a set of config items
+ * @param cs Config items
+ * @param vars Variable definition
+ * @param flags Flags, e.g. #CS_REG_DISABLED
+ * @retval bool True, if all variables were registered successfully
+ */
+bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags)
+{
+ if (!cs || !vars)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Buffer err;
+ mutt_buffer_init(&err);
+ err.data = calloc(1, STRING);
+ err.dsize = STRING;
+
+ bool rc = true;
+
+ for (size_t i = 0; vars[i].name; i++)
+ {
+ if (!reg_one_var(cs, &vars[i], &err))
+ {
+ mutt_debug(1, "%s\n", err.data);
+ rc = false;
+ }
+ }
+
+ FREE(&err.data);
+ return rc;
+}
+
+/**
+ * cs_inherit_variable - Create in inherited config item
+ * @param cs Config items
+ * @param parent HashElem of parent config item
+ * @param name Name of account-specific config item
+ * @retval ptr New HashElem representing the inherited config item
+ */
+struct HashElem *cs_inherit_variable(const struct ConfigSet *cs,
+ struct HashElem *parent, const char *name)
+{
+ if (!cs || !parent)
+ return NULL; /* LCOV_EXCL_LINE */
+
+ struct Buffer err;
+ mutt_buffer_init(&err);
+ err.data = calloc(1, STRING);
+ err.dsize = STRING;
+
+ struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i));
+ i->parent = parent;
+ i->name = mutt_str_strdup(name);
+
+ struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i);
+ if (!he)
+ {
+ FREE(&i->name);
+ FREE(&i);
+ }
+
+ FREE(&err.data);
+ return he;
+}
+
+/**
+ * cs_add_listener - Add a listener (callback function)
+ * @param cs Config items
+ * @param fn Listener callback function
+ */
+void cs_add_listener(struct ConfigSet *cs, cs_listener fn)
+{
+ if (!cs || !fn)
+ return; /* LCOV_EXCL_LINE */
+
+ for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
+ {
+ if (cs->listeners[i] == fn)
+ {
+ mutt_debug(1, "Listener was already registered\n");
+ return;
+ }
+ }
+
+ for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
+ {
+ if (!cs->listeners[i])
+ {
+ cs->listeners[i] = fn;
+ return;
+ }
+ }
+}
+
+/**
+ * cs_remove_listener - Remove a listener (callback function)
+ * @param cs Config items
+ * @param fn Listener callback function
+ */
+void cs_remove_listener(struct ConfigSet *cs, cs_listener fn)
+{
+ if (!cs || !fn)
+ return; /* LCOV_EXCL_LINE */
+
+ for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
+ {
+ if (cs->listeners[i] == fn)
+ {
+ cs->listeners[i] = NULL;
+ return;
+ }
+ }
+ mutt_debug(1, "Listener wasn't registered\n");
+}
+
+/**
+ * cs_notify_listeners - Notify all listeners of an event
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param name Name of config item
+ * @param ev Type of event
+ */
+void cs_notify_listeners(const struct ConfigSet *cs, struct HashElem *he,
+ const char *name, enum ConfigEvent ev)
+{
+ if (!cs || !he || !name)
+ return; /* LCOV_EXCL_LINE */
+
+ for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
+ {
+ if (!cs->listeners[i])
+ return;
+
+ cs->listeners[i](cs, he, name, ev);
+ }
+}
+
+/**
+ * cs_he_reset - Reset a config item to its initial value
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_he_reset(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ /* An inherited var that's already pointing to its parent.
+ * Return 'success', but don't send a notification. */
+ if ((he->type & DT_INHERITED) && (DTYPE(he->type) == 0))
+ return CSR_SUCCESS;
+
+ const struct ConfigSetType *cst = NULL;
+ const struct ConfigDef *cdef = NULL;
+
+ int rc = CSR_SUCCESS;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ cst = cs_get_type_def(cs, i->parent->type);
+ cdef = i->parent->data;
+
+ if (cst && cst->destroy)
+ cst->destroy(cs, (void **) &i->var, cdef);
+
+ he->type = DT_INHERITED;
+ }
+ else
+ {
+ cst = cs_get_type_def(cs, he->type);
+ cdef = he->data;
+
+ if (cst)
+ rc = cst->reset(cs, cdef->var, cdef, err);
+ }
+
+ if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
+ cs_notify_listeners(cs, he, he->key.strkey, CE_RESET);
+ return rc;
+}
+
+/**
+ * cs_str_reset - Reset a config item to its initial value
+ * @param cs Config items
+ * @param name Name of config item
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_str_reset(const struct ConfigSet *cs, const char *name, struct Buffer *err)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(err, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return cs_he_reset(cs, he, err);
+}
+
+/**
+ * cs_he_initial_set - Set the initial value of a config item
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_he_initial_set(const struct ConfigSet *cs, struct HashElem *he,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ cdef = i->parent->data;
+ mutt_debug(1, "Variable '%s' is inherited type.\n", cdef->name);
+ return CSR_ERR_CODE;
+ }
+
+ cdef = he->data;
+ cst = cs_get_type_def(cs, he->type);
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
+ return CSR_ERR_CODE;
+ }
+
+ int rc = cst->string_set(cs, NULL, cdef, value, err);
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return rc;
+
+ cs_notify_listeners(cs, he, he->key.strkey, CE_INITIAL_SET);
+ return CSR_SUCCESS;
+}
+
+/**
+ * cs_str_initial_set - Set the initial value of a config item
+ * @param cs Config items
+ * @param name Name of config item
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_str_initial_set(const struct ConfigSet *cs, const char *name,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(err, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return cs_he_initial_set(cs, he, value, err);
+}
+
+/**
+ * cs_he_initial_get - Get the initial, or parent, value of a config item
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If a config item is inherited from another, then this will get the parent's
+ * value. Otherwise, it will get the config item's initial value.
+ */
+int cs_he_initial_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Inheritance *i = NULL;
+ const struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ i = he->data;
+ cdef = i->parent->data;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, DTYPE(he->type));
+ return CSR_ERR_CODE;
+ }
+
+ return cst->string_get(cs, NULL, cdef, result);
+}
+
+/**
+ * cs_str_initial_get - Get the initial, or parent, value of a config item
+ * @param cs Config items
+ * @param name Name of config item
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If a config item is inherited from another, then this will get the parent's
+ * value. Otherwise, it will get the config item's initial value.
+ */
+int cs_str_initial_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(result, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return cs_he_initial_get(cs, he, result);
+}
+
+/**
+ * cs_he_string_set - Set a config item by string
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+ void *var = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ cdef = i->parent->data;
+ var = &i->var;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ var = cdef->var;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
+ return CSR_ERR_CODE;
+ }
+
+ if (!var)
+ return CSR_ERR_CODE;
+
+ int rc = cst->string_set(cs, var, cdef, value, err);
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return rc;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ he->type = i->parent->type | DT_INHERITED;
+ }
+ if (!(rc & CSR_SUC_NO_CHANGE))
+ cs_notify_listeners(cs, he, he->key.strkey, CE_SET);
+ return rc;
+}
+
+/**
+ * cs_str_string_set - Set a config item by string
+ * @param cs Config items
+ * @param name Name of config item
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_str_string_set(const struct ConfigSet *cs, const char *name,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(err, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return cs_he_string_set(cs, he, value, err);
+}
+
+/**
+ * cs_he_string_get - Get a config item as a string
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct Inheritance *i = NULL;
+ const struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+ void *var = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ i = he->data;
+ cdef = i->parent->data;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if ((he->type & DT_INHERITED) && (DTYPE(he->type) != 0))
+ {
+ var = &i->var; /* Local value */
+ }
+ else
+ {
+ var = cdef->var; /* Normal var */
+ }
+
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, DTYPE(he->type));
+ return CSR_ERR_CODE;
+ }
+
+ return cst->string_get(cs, var, cdef, result);
+}
+
+/**
+ * cs_str_string_get - Get a config item as a string
+ * @param cs Config items
+ * @param name Name of config item
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_str_string_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(result, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ return cs_he_string_get(cs, he, result);
+}
+
+/**
+ * cs_he_native_set - Natively set the value of a HashElem config item
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param value Native pointer/value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_he_native_set(const struct ConfigSet *cs, struct HashElem *he,
+ intptr_t value, struct Buffer *err)
+{
+ if (!cs || !he)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+ void *var = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ cdef = i->parent->data;
+ var = &i->var;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ var = cdef->var;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
+ return CSR_ERR_CODE;
+ }
+
+ int rc = cst->native_set(cs, var, cdef, value, err);
+ if (CSR_RESULT(rc) == CSR_SUCCESS)
+ {
+ if (he->type & DT_INHERITED)
+ he->type = cdef->type | DT_INHERITED;
+ if (!(rc & CSR_SUC_NO_CHANGE))
+ cs_notify_listeners(cs, he, cdef->name, CE_SET);
+ }
+
+ return rc;
+}
+
+/**
+ * cs_str_native_set - Natively set the value of a string config item
+ * @param cs Config items
+ * @param name Name of config item
+ * @param value Native pointer/value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+int cs_str_native_set(const struct ConfigSet *cs, const char *name,
+ intptr_t value, struct Buffer *err)
+{
+ if (!cs || !name)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ if (!he)
+ {
+ mutt_buffer_printf(err, "Unknown var '%s'", name);
+ return CSR_ERR_UNKNOWN;
+ }
+
+ const struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+ void *var = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ struct Inheritance *i = he->data;
+ cdef = i->parent->data;
+ var = &i->var;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ var = cdef->var;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if (!cst)
+ {
+ mutt_debug(1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
+ return CSR_ERR_CODE;
+ }
+
+ int rc = cst->native_set(cs, var, cdef, value, err);
+ if (CSR_RESULT(rc) == CSR_SUCCESS)
+ {
+ if (he->type & DT_INHERITED)
+ he->type = cdef->type | DT_INHERITED;
+ if (!(rc & CSR_SUC_NO_CHANGE))
+ cs_notify_listeners(cs, he, cdef->name, CE_SET);
+ }
+
+ return rc;
+}
+
+/**
+ * cs_he_native_get - Natively get the value of a HashElem config item
+ * @param cs Config items
+ * @param he HashElem representing config item
+ * @param err Buffer for results or error messages
+ * @retval intptr_t Native pointer/value
+ */
+intptr_t cs_he_native_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
+{
+ if (!cs || !he)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ struct Inheritance *i = NULL;
+ const struct ConfigDef *cdef = NULL;
+ const struct ConfigSetType *cst = NULL;
+ void *var = NULL;
+
+ if (he->type & DT_INHERITED)
+ {
+ i = he->data;
+ cdef = i->parent->data;
+ cst = cs_get_type_def(cs, i->parent->type);
+ }
+ else
+ {
+ cdef = he->data;
+ cst = cs_get_type_def(cs, he->type);
+ }
+
+ if ((he->type & DT_INHERITED) && (DTYPE(he->type) != 0))
+ {
+ var = &i->var;
+ }
+ else
+ {
+ var = cdef->var;
+ }
+
+ if (!cst)
+ {
+ mutt_buffer_printf(err, "Variable '%s' has an invalid type %d", cdef->name, he->type);
+ return INT_MIN;
+ }
+
+ return cst->native_get(cs, var, cdef, err);
+}
+
+/**
+ * cs_str_native_get - Natively get the value of a string config item
+ * @param cs Config items
+ * @param name Name of config item
+ * @param err Buffer for error messages
+ * @retval intptr_t Native pointer/value
+ */
+intptr_t cs_str_native_get(const struct ConfigSet *cs, const char *name, struct Buffer *err)
+{
+ if (!cs || !name)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ struct HashElem *he = cs_get_elem(cs, name);
+ return cs_he_native_get(cs, he, err);
+}
--- /dev/null
+/**
+ * @file
+ * A collection of config items
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_SET_H
+#define _CONFIG_SET_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct Buffer;
+struct ConfigSet;
+struct HashElem;
+struct ConfigDef;
+
+/**
+ * enum ConfigEvent - Config notification types
+ */
+enum ConfigEvent
+{
+ CE_SET = 1, /**< Config item has been set */
+ CE_RESET, /**< Config item has been reset to initial, or parent, value */
+ CE_INITIAL_SET, /**< Config item's initial value has been set */
+};
+
+/* Config Set Results */
+#define CSR_SUCCESS 0 /**< Action completed successfully */
+#define CSR_ERR_CODE 1 /**< Problem with the code */
+#define CSR_ERR_UNKNOWN 2 /**< Unrecognised config item */
+#define CSR_ERR_INVALID 3 /**< Value hasn't been set */
+
+/* Flags for CSR_SUCCESS */
+#define CSR_SUC_INHERITED (1 << 4) /**< Value is inherited */
+#define CSR_SUC_EMPTY (1 << 5) /**< Value is empty/unset */
+#define CSR_SUC_WARNING (1 << 6) /**< Notify the user of a warning */
+#define CSR_SUC_NO_CHANGE (1 << 7) /**< The value hasn't changed */
+
+/* Flags for CSR_INVALID */
+#define CSR_INV_TYPE (1 << 4) /**< Value is not valid for the type */
+#define CSR_INV_VALIDATOR (1 << 5) /**< Value was rejected by the validator */
+
+#define CSR_RESULT_MASK 0x0F
+#define CSR_RESULT(x) ((x) & CSR_RESULT_MASK)
+
+/**
+ * enum CsListenerAction - Config Listener responses
+ */
+enum CsListenerAction
+{
+ CSLA_CONTINUE = 1, /**< Continue notifying listeners */
+ CSLA_STOP, /**< Stop notifying listeners */
+};
+
+typedef bool (*cs_listener) (const struct ConfigSet *cs, struct HashElem *he, const char *name, enum ConfigEvent ev);
+typedef int (*cs_validator) (const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err);
+
+typedef int (*cst_string_set)(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef, const char *value, struct Buffer *err);
+typedef int (*cst_string_get)(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef, struct Buffer *result);
+typedef int (*cst_native_set)(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err);
+typedef intptr_t(*cst_native_get)(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef, struct Buffer *err);
+
+typedef int (*cst_reset) (const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef, struct Buffer *err);
+typedef void (*cst_destroy) (const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef);
+
+#define IP (intptr_t)
+
+#define CS_REG_DISABLED (1 << 0)
+
+/**
+ * struct ConfigDef - Config item definition
+ *
+ * Every config variable that NeoMutt supports is backed by a ConfigDef.
+ */
+struct ConfigDef
+{
+ const char *name; /**< User-visible name */
+ unsigned int type; /**< Variable type, e.g. #DT_STRING */
+ intptr_t flags; /**< Notification flags, e.g. #R_PAGER */
+ void *var; /**< Pointer to the global variable */
+ intptr_t initial; /**< Initial value */
+ cs_validator validator; /**< Validator callback function */
+};
+
+/**
+ * struct ConfigSetType - Type definition for a config item
+ *
+ * Each config item has a type which is defined by a set of callback functions.
+ */
+struct ConfigSetType
+{
+ const char *name; /**< Name of the type, e.g. "String" */
+ cst_string_set string_set; /**< Convert the variable to a string */
+ cst_string_get string_get; /**< Initialise a variable from a string */
+ cst_native_set native_set; /**< Set the variable using a C-native type */
+ cst_native_get native_get; /**< Get the variable's value as a C-native type */
+ cst_reset reset; /**< Reset the variable to its initial, or parent, value */
+ cst_destroy destroy; /**< Free the resources for a variable */
+};
+
+/**
+ * struct ConfigSet - Container for lots of config items
+ *
+ * The config items are stored in a HashTable so that their names can be looked
+ * up efficiently. Each config item is repesented by a HashElem. Once
+ * created, this HashElem is static and may be used for the lifetime of the
+ * ConfigSet.
+ */
+struct ConfigSet
+{
+ struct Hash *hash; /**< HashTable storing the config itesm */
+ struct ConfigSetType types[18]; /**< All the defined config types */
+ cs_listener listeners[8]; /**< Listeners for notifications of changes to config items */
+};
+
+struct ConfigSet *cs_create(size_t size);
+void cs_init(struct ConfigSet *cs, size_t size);
+void cs_free(struct ConfigSet **cs);
+
+struct HashElem * cs_get_elem(const struct ConfigSet *cs, const char *name);
+const struct ConfigSetType *cs_get_type_def(const struct ConfigSet *cs, unsigned int type);
+
+bool cs_register_type(struct ConfigSet *cs, unsigned int type, const struct ConfigSetType *cst);
+bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags);
+struct HashElem *cs_inherit_variable(const struct ConfigSet *cs, struct HashElem *parent, const char *name);
+
+void cs_add_listener(struct ConfigSet *cs, cs_listener fn);
+void cs_remove_listener(struct ConfigSet *cs, cs_listener fn);
+void cs_notify_listeners(const struct ConfigSet *cs, struct HashElem *he, const char *name, enum ConfigEvent ev);
+
+int cs_he_initial_get (const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result);
+int cs_he_initial_set (const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err);
+intptr_t cs_he_native_get (const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err);
+int cs_he_native_set (const struct ConfigSet *cs, struct HashElem *he, intptr_t value, struct Buffer *err);
+int cs_he_reset (const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err);
+int cs_he_string_get (const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result);
+int cs_he_string_set (const struct ConfigSet *cs, struct HashElem *he, const char *value, struct Buffer *err);
+
+int cs_str_initial_get(const struct ConfigSet *cs, const char *name, struct Buffer *result);
+int cs_str_initial_set(const struct ConfigSet *cs, const char *name, const char *value, struct Buffer *err);
+intptr_t cs_str_native_get (const struct ConfigSet *cs, const char *name, struct Buffer *err);
+int cs_str_native_set (const struct ConfigSet *cs, const char *name, intptr_t value, struct Buffer *err);
+int cs_str_reset (const struct ConfigSet *cs, const char *name, struct Buffer *err);
+int cs_str_string_get (const struct ConfigSet *cs, const char *name, struct Buffer *result);
+int cs_str_string_set (const struct ConfigSet *cs, const char *name, const char *value, struct Buffer *err);
+
+#endif /* _CONFIG_SET_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a sort option
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-sort Type: Sorting
+ *
+ * Type representing a sort option.
+ */
+
+#include "config.h"
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include "mutt/buffer.h"
+#include "mutt/logging.h"
+#include "mutt/mapping.h"
+#include "mutt/string2.h"
+#include "sort.h"
+#include "set.h"
+#include "types.h"
+
+// clang-format off
+/**
+ * SortAliasMethods - Sort methods for email aliases
+ */
+const struct Mapping SortAliasMethods[] = {
+ { "address", SORT_ADDRESS },
+ { "alias", SORT_ALIAS },
+ { "unsorted", SORT_ORDER },
+ { NULL, 0 },
+};
+
+/**
+ * SortAuxMethods - Sort methods for '$sort_aux' for the index
+ */
+const struct Mapping SortAuxMethods[] = {
+ { "date", SORT_DATE },
+ { "date-received", SORT_RECEIVED },
+ { "date-sent", SORT_DATE },
+ { "from", SORT_FROM },
+ { "label", SORT_LABEL },
+ { "mailbox-order", SORT_ORDER },
+ { "score", SORT_SCORE },
+ { "size", SORT_SIZE },
+ { "spam", SORT_SPAM },
+ { "subject", SORT_SUBJECT },
+ { "threads", SORT_DATE },
+ { "to", SORT_TO },
+ { NULL, 0 },
+};
+
+/**
+ * SortBrowserMethods - Sort methods for the folder/dir browser
+ */
+const struct Mapping SortBrowserMethods[] = {
+ { "alpha", SORT_SUBJECT },
+ { "count", SORT_COUNT },
+ { "date", SORT_DATE },
+ { "desc", SORT_DESC },
+ { "new", SORT_UNREAD },
+ { "unread", SORT_UNREAD },
+ { "size", SORT_SIZE },
+ { "unsorted", SORT_ORDER },
+ { NULL, 0 },
+};
+
+/**
+ * SortKeyMethods - Sort methods for encryption keys
+ */
+const struct Mapping SortKeyMethods[] = {
+ { "address", SORT_ADDRESS },
+ { "date", SORT_DATE },
+ { "keyid", SORT_KEYID },
+ { "trust", SORT_TRUST },
+ { NULL, 0 },
+};
+
+/**
+ * SortMethods - Sort methods for '$sort' for the index
+ */
+const struct Mapping SortMethods[] = {
+ { "date", SORT_DATE },
+ { "date-received", SORT_RECEIVED },
+ { "date-sent", SORT_DATE },
+ { "from", SORT_FROM },
+ { "label", SORT_LABEL },
+ { "mailbox-order", SORT_ORDER },
+ { "score", SORT_SCORE },
+ { "size", SORT_SIZE },
+ { "spam", SORT_SPAM },
+ { "subject", SORT_SUBJECT },
+ { "threads", SORT_THREADS },
+ { "to", SORT_TO },
+ { NULL, 0 },
+};
+
+/**
+ * SortSidebarMethods - Sort methods for the sidebar
+ */
+const struct Mapping SortSidebarMethods[] = {
+ { "alpha", SORT_PATH },
+ { "count", SORT_COUNT },
+ { "desc", SORT_DESC },
+ { "flagged", SORT_FLAGGED },
+ { "mailbox-order", SORT_ORDER },
+ { "name", SORT_PATH },
+ { "new", SORT_UNREAD },
+ { "path", SORT_PATH },
+ { "unread", SORT_UNREAD },
+ { "unsorted", SORT_ORDER },
+ { NULL, 0 },
+};
+// clang-format on
+
+/**
+ * sort_string_set - Set a Sort by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int sort_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef || !value)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ intptr_t id = -1;
+ int flags = 0;
+
+ if (mutt_str_strncmp("reverse-", value, 8) == 0)
+ {
+ flags |= SORT_REVERSE;
+ value += 8;
+ }
+
+ if (mutt_str_strncmp("last-", value, 5) == 0)
+ {
+ flags |= SORT_LAST;
+ value += 5;
+ }
+
+ switch (cdef->type & DT_SUBTYPE_MASK)
+ {
+ case DT_SORT_INDEX:
+ id = mutt_map_get_value(value, SortMethods);
+ break;
+ case DT_SORT_ALIAS:
+ id = mutt_map_get_value(value, SortAliasMethods);
+ break;
+ case DT_SORT_AUX:
+ id = mutt_map_get_value(value, SortAuxMethods);
+ break;
+ case DT_SORT_BROWSER:
+ id = mutt_map_get_value(value, SortBrowserMethods);
+ break;
+ case DT_SORT_KEYS:
+ id = mutt_map_get_value(value, SortKeyMethods);
+ break;
+ case DT_SORT_SIDEBAR:
+ id = mutt_map_get_value(value, SortSidebarMethods);
+ break;
+ default:
+ mutt_debug(1, "Invalid sort type: %u\n", cdef->type & DT_SUBTYPE_MASK);
+ return CSR_ERR_CODE;
+ break;
+ }
+
+ if (id < 0)
+ {
+ mutt_buffer_printf(err, "Invalid sort name: %s", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ id |= flags;
+
+ if (var)
+ {
+ if (id == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, (intptr_t) id, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = id;
+ }
+ else
+ {
+ cdef->initial = id;
+ }
+
+ return CSR_SUCCESS;
+}
+
+/**
+ * sort_string_get - Get a Sort as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int sort_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int sort;
+
+ if (var)
+ sort = *(short *) var;
+ else
+ sort = (int) cdef->initial;
+
+ if (sort & SORT_REVERSE)
+ mutt_buffer_addstr(result, "reverse-");
+ if (sort & SORT_LAST)
+ mutt_buffer_addstr(result, "last-");
+
+ sort &= SORT_MASK;
+
+ const char *str = NULL;
+
+ switch (cdef->type & DT_SUBTYPE_MASK)
+ {
+ case DT_SORT_INDEX:
+ str = mutt_map_get_name(sort, SortMethods);
+ break;
+ case DT_SORT_ALIAS:
+ str = mutt_map_get_name(sort, SortAliasMethods);
+ break;
+ case DT_SORT_AUX:
+ str = mutt_map_get_name(sort, SortAuxMethods);
+ break;
+ case DT_SORT_BROWSER:
+ str = mutt_map_get_name(sort, SortBrowserMethods);
+ break;
+ case DT_SORT_KEYS:
+ str = mutt_map_get_name(sort, SortKeyMethods);
+ break;
+ case DT_SORT_SIDEBAR:
+ str = mutt_map_get_name(sort, SortSidebarMethods);
+ break;
+ default:
+ mutt_debug(1, "Invalid sort type: %u\n", cdef->type & DT_SUBTYPE_MASK);
+ return CSR_ERR_CODE;
+ break;
+ }
+
+ if (!str)
+ {
+ mutt_debug(1, "Variable has an invalid value: %d/%d\n",
+ cdef->type & DT_SUBTYPE_MASK, sort);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * sort_native_set - Set a Sort config item by int
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Sort value
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int sort_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ switch (cdef->type & DT_SUBTYPE_MASK)
+ {
+ case DT_SORT_INDEX:
+ str = mutt_map_get_name((value & SORT_MASK), SortMethods);
+ break;
+ case DT_SORT_ALIAS:
+ str = mutt_map_get_name((value & SORT_MASK), SortAliasMethods);
+ break;
+ case DT_SORT_AUX:
+ str = mutt_map_get_name((value & SORT_MASK), SortAuxMethods);
+ break;
+ case DT_SORT_BROWSER:
+ str = mutt_map_get_name((value & SORT_MASK), SortBrowserMethods);
+ break;
+ case DT_SORT_KEYS:
+ str = mutt_map_get_name((value & SORT_MASK), SortKeyMethods);
+ break;
+ case DT_SORT_SIDEBAR:
+ str = mutt_map_get_name((value & SORT_MASK), SortSidebarMethods);
+ break;
+ default:
+ mutt_debug(1, "Invalid sort type: %u\n", cdef->type & DT_SUBTYPE_MASK);
+ return CSR_ERR_CODE;
+ break;
+ }
+
+ if (!str)
+ {
+ mutt_buffer_printf(err, "Invalid sort type: %ld", value);
+ return (CSR_ERR_INVALID | CSR_INV_TYPE);
+ }
+
+ if (value == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = value;
+ return CSR_SUCCESS;
+}
+
+/**
+ * sort_native_get - Get an int from a Sort config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t Sort ID
+ */
+static intptr_t sort_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ return *(short *) var;
+}
+
+/**
+ * sort_reset - Reset a Sort to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int sort_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ if (cdef->initial == (*(short *) var))
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ int rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ *(short *) var = cdef->initial;
+ return CSR_SUCCESS;
+}
+
+/**
+ * sort_init - Register the Sort config type
+ * @param cs Config items
+ */
+void sort_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_sort = {
+ "sort",
+ sort_string_set,
+ sort_string_get,
+ sort_native_set,
+ sort_native_get,
+ sort_reset,
+ NULL,
+ };
+ cs_register_type(cs, DT_SORT, &cst_sort);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a sort option
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_SORT_H
+#define _CONFIG_SORT_H
+
+#include "mutt/mapping.h"
+
+struct ConfigSet;
+
+extern const struct Mapping SortAliasMethods[];
+extern const struct Mapping SortAuxMethods[];
+extern const struct Mapping SortBrowserMethods[];
+extern const struct Mapping SortKeyMethods[];
+extern const struct Mapping SortMethods[];
+extern const struct Mapping SortSidebarMethods[];
+
+/* ... DT_SORT */
+#define DT_SORT_INDEX 0x000 /**< Sort id for #SortMethods */
+#define DT_SORT_ALIAS 0x040 /**< Sort id for #SortAliasMethods */
+#define DT_SORT_BROWSER 0x080 /**< Sort id for #SortBrowserMethods */
+#define DT_SORT_KEYS 0x100 /**< Sort id for #SortKeyMethods */
+#define DT_SORT_AUX 0x200 /**< Sort id for #SortAliasMethods */
+#define DT_SORT_SIDEBAR 0x400 /**< Sort id for #SortSidebarMethods */
+
+#define SORT_DATE 1 /**< Sort by the date the email was sent. */
+#define SORT_SIZE 2 /**< Sort by the size of the email */
+#define SORT_ALPHA 3 /**< Required by makedoc.c */
+#define SORT_SUBJECT 3 /**< Sort by the email's subject */
+#define SORT_FROM 4 /**< Sort by the email's From field */
+#define SORT_ORDER 5 /**< Sort by the order the messages appear in the mailbox */
+#define SORT_THREADS 6 /**< Sort by email threads */
+#define SORT_RECEIVED 7 /**< Sort by when the message were delivered locally */
+#define SORT_TO 8 /**< Sort by the email's To field */
+#define SORT_SCORE 9 /**< Sort by the email's score */
+#define SORT_ALIAS 10 /**< Sort by email alias */
+#define SORT_ADDRESS 11 /**< Sort by email address */
+#define SORT_KEYID 12 /**< Sort by the encryption key's ID */
+#define SORT_TRUST 13 /**< Sort by encryption key's trust level */
+#define SORT_SPAM 14 /**< Sort by the email's spam score */
+#define SORT_COUNT 15 /**< Sort by number of emails in a folder */
+#define SORT_UNREAD 16 /**< Sort by the number of unread emails */
+#define SORT_FLAGGED 17 /**< Sort by the number of flagged emails */
+#define SORT_PATH 18 /**< Sort by the folder's path */
+#define SORT_LABEL 19 /**< Sort by the emails label */
+#define SORT_DESC 20 /**< Sort by the folder's description */
+
+/* Sort and sort_aux are shorts, and are a composite of a constant sort
+ * operation number and a set of compounded bitflags.
+ *
+ * Everything below SORT_MASK is a constant. There's room for SORT_MASK
+ * constant SORT_ values.
+ *
+ * Everything above is a bitflag. It's OK to move SORT_MASK down by powers of 2
+ * if we need more, so long as we don't collide with the constants above. (Or
+ * we can just expand sort and sort_aux to uint32_t.)
+ */
+#define SORT_MASK ((1 << 8) - 1) /**< Mask for the sort id */
+#define SORT_REVERSE (1 << 8) /**< Reverse the order of the sort */
+#define SORT_LAST (1 << 9) /**< Sort thread by last-X, e.g. received date */
+
+void sort_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_SORT_H */
--- /dev/null
+/**
+ * @file
+ * Type representing a string
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+/**
+ * @page config-string Type: String
+ *
+ * Type representing a string.
+ */
+
+#include "config.h"
+#include <stddef.h>
+#include <limits.h>
+#include <stdint.h>
+#include "mutt/buffer.h"
+#include "mutt/memory.h"
+#include "mutt/string2.h"
+#include "set.h"
+#include "types.h"
+
+/**
+ * string_destroy - Destroy a String
+ * @param cs Config items
+ * @param var Variable to destroy
+ * @param cdef Variable definition
+ */
+static void string_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
+{
+ if (!cs || !var || !cdef)
+ return; /* LCOV_EXCL_LINE */
+
+ const char **str = (const char **) var;
+ if (!*str)
+ return;
+
+ /* Don't free strings from the var definition */
+ if (*(char **) var == (char *) cdef->initial)
+ {
+ *(char **) var = NULL;
+ return;
+ }
+
+ FREE(var);
+}
+
+/**
+ * string_string_set - Set a String by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be set.
+ */
+static int string_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
+ const char *value, struct Buffer *err)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ /* Store empty strings as NULL */
+ if (value && (value[0] == '\0'))
+ value = NULL;
+
+ if (!value && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ int rc = CSR_SUCCESS;
+
+ if (var)
+ {
+ if (mutt_str_strcmp(value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, (intptr_t) value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ string_destroy(cs, var, cdef);
+
+ const char *str = mutt_str_strdup(value);
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ }
+ else
+ {
+ /* we're already using the initial value */
+ if (*(char **) cdef->var == (char *) cdef->initial)
+ *(char **) cdef->var = mutt_str_strdup((char *) cdef->initial);
+
+ if (cdef->type & DT_INITIAL_SET)
+ FREE(&cdef->initial);
+
+ cdef->type |= DT_INITIAL_SET;
+ cdef->initial = IP mutt_str_strdup(value);
+ }
+
+ return rc;
+}
+
+/**
+ * string_string_get - Get a String as a string
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param result Buffer for results or error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ *
+ * If var is NULL, then the config item's initial value will be returned.
+ */
+static int string_string_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *result)
+{
+ if (!cs || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = NULL;
+
+ if (var)
+ str = *(const char **) var;
+ else
+ str = (char *) cdef->initial;
+
+ if (!str)
+ return (CSR_SUCCESS | CSR_SUC_EMPTY); /* empty string */
+
+ mutt_buffer_addstr(result, str);
+ return CSR_SUCCESS;
+}
+
+/**
+ * string_native_set - Set a String config item by string
+ * @param cs Config items
+ * @param var Variable to set
+ * @param cdef Variable definition
+ * @param value Native pointer/value to set
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int string_native_set(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, intptr_t value,
+ struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ const char *str = (const char *) value;
+
+ /* Store empty strings as NULL */
+ if (str && (str[0] == '\0'))
+ value = 0;
+
+ if ((value == 0) && (cdef->type & DT_NOT_EMPTY))
+ {
+ mutt_buffer_printf(err, "Option %s may not be empty", cdef->name);
+ return (CSR_ERR_INVALID | CSR_INV_VALIDATOR);
+ }
+
+ if (mutt_str_strcmp((const char *) value, (*(char **) var)) == 0)
+ return (CSR_SUCCESS | CSR_SUC_NO_CHANGE);
+
+ int rc;
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, value, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ string_destroy(cs, var, cdef);
+
+ str = mutt_str_strdup(str);
+ rc = CSR_SUCCESS;
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ return rc;
+}
+
+/**
+ * string_native_get - Get a string from a String config item
+ * @param cs Config items
+ * @param var Variable to get
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval intptr_t String pointer
+ */
+static intptr_t string_native_get(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return INT_MIN; /* LCOV_EXCL_LINE */
+
+ const char *str = *(const char **) var;
+
+ return (intptr_t) str;
+}
+
+/**
+ * string_reset - Reset a String to its initial value
+ * @param cs Config items
+ * @param var Variable to reset
+ * @param cdef Variable definition
+ * @param err Buffer for error messages
+ * @retval int Result, e.g. #CSR_SUCCESS
+ */
+static int string_reset(const struct ConfigSet *cs, void *var,
+ const struct ConfigDef *cdef, struct Buffer *err)
+{
+ if (!cs || !var || !cdef)
+ return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
+
+ int rc = CSR_SUCCESS;
+
+ const char *str = (const char *) cdef->initial;
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ if (mutt_str_strcmp(str, (*(char **) var)) == 0)
+ return (rc | CSR_SUC_NO_CHANGE);
+
+ if (cdef->validator)
+ {
+ rc = cdef->validator(cs, cdef, cdef->initial, err);
+
+ if (CSR_RESULT(rc) != CSR_SUCCESS)
+ return (rc | CSR_INV_VALIDATOR);
+ }
+
+ string_destroy(cs, var, cdef);
+
+ if (!str)
+ rc |= CSR_SUC_EMPTY;
+
+ *(const char **) var = str;
+ return rc;
+}
+
+/**
+ * string_init - Register the String config type
+ * @param cs Config items
+ */
+void string_init(struct ConfigSet *cs)
+{
+ const struct ConfigSetType cst_string = {
+ "string", string_string_set, string_string_get, string_native_set,
+ string_native_get, string_reset, string_destroy,
+ };
+ cs_register_type(cs, DT_STRING, &cst_string);
+}
--- /dev/null
+/**
+ * @file
+ * Type representing a string
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_STRING_H
+#define _CONFIG_STRING_H
+
+struct ConfigSet;
+
+void string_init(struct ConfigSet *cs);
+
+#endif /* _CONFIG_STRING_H */
--- /dev/null
+/**
+ * @file
+ * Constants for all the config types
+ *
+ * @authors
+ * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program 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, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef _CONFIG_TYPES_H
+#define _CONFIG_TYPES_H
+
+/* Data Types */
+#define DT_ADDRESS 1 /**< e-mail address */
+#define DT_BOOL 2 /**< boolean option */
+#define DT_COMMAND 3 /**< a command */
+#define DT_HCACHE 4 /**< header cache backend */
+#define DT_LONG 5 /**< a number (long) */
+#define DT_MAGIC 6 /**< mailbox type */
+#define DT_MBTABLE 7 /**< multibyte char table */
+#define DT_NUMBER 8 /**< a number */
+#define DT_PATH 9 /**< a pathname */
+#define DT_QUAD 10 /**< quad-option (no/yes/ask-no/ask-yes) */
+#define DT_REGEX 11 /**< regular expressions */
+#define DT_SORT 12 /**< sorting methods */
+#define DT_STRING 13 /**< a string */
+#define DT_SYNONYM 14 /**< synonym for another variable */
+
+#define DTYPE(x) ((x) & 0x1f) /**< Mask for the Data Type */
+
+#define DT_NOT_EMPTY 0x40 /**< Empty strings are not allowed */
+#define DT_NOT_NEGATIVE 0x80 /**< Negative numbers are not allowed */
+
+/* subtypes for... */
+#define DT_SUBTYPE_MASK 0xfe0 /**< Mask for the Data Subtype */
+
+/* Private config item flags */
+#define DT_INHERITED 0x1000 /**< Config item is inherited */
+#define DT_INITIAL_SET 0x2000 /**< Config item must have its initial value freed */
+#define DT_DISABLED 0x4000 /**< Config item is disabled */
+#define DT_MY_CONFIG 0x8000 /**< Config item is a "my_" variable */
+
+/* forced redraw/resort types + other flags */
+#define R_NONE 0 /**< No refresh/resort flags */
+#define R_INDEX (1 << 0) /**< redraw the index menu (MENU_MAIN) */
+#define R_PAGER (1 << 1) /**< redraw the pager menu */
+#define R_PAGER_FLOW (1 << 2) /**< reflow line_info and redraw the pager menu */
+#define R_RESORT (1 << 3) /**< resort the mailbox */
+#define R_RESORT_SUB (1 << 4) /**< resort subthreads */
+#define R_RESORT_INIT (1 << 5) /**< resort from scratch */
+#define R_TREE (1 << 6) /**< redraw the thread tree */
+#define R_REFLOW (1 << 7) /**< reflow window layout and full redraw */
+#define R_SIDEBAR (1 << 8) /**< redraw the sidebar */
+#define R_MENU (1 << 9) /**< redraw all menus */
+#define R_BOTH (R_INDEX | R_PAGER)
+#define R_RESORT_BOTH (R_RESORT | R_RESORT_SUB)
+
+/* general flags, to be OR'd with the R_ flags above (so keep shifting..) */
+#define F_SENSITIVE (1 << 10) /**< Config item contains sensitive value */
+#define IS_SENSITIVE(x) (((x).flags & F_SENSITIVE) == F_SENSITIVE)
+
+#endif /* _CONFIG_TYPES_H */
/* ... DT_REGEX */
#define DT_REGEX_MATCH_CASE 0x010 /**< Case-sensitive matching */
#define DT_REGEX_ALLOW_NOT 0x020 /**< Regex can begin with '!' */
+#define DT_REGEX_NOSUB 0x100 /**< Do not report what was matched (REG_NOSUB) */
/* This is a non-standard option supported by Solaris 2.5.x
* which allows patterns of the form \<...\> */
complete.c
compose.c
compress.c
+config/account.c
+config/address.c
+config/bool.c
+config/command.c
+config/dump.c
+config/enum.c
+config/long.c
+config/magic.c
+config/mbtable.c
+config/number.c
+config/path.c
+config/quad.c
+config/regex.c
+config/set.c
+config/slist.c
+config/sort.c
+config/string.c
conn/conn_globals.c
conn/conn_raw.c
conn/getdomain.c