From: Richard Russon Date: Mon, 30 Oct 2017 00:50:11 +0000 (+0000) Subject: config files X-Git-Tag: 2019-10-25~739^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=166235f252b844b02a46202eb4979bc436db4c4d;p=neomutt config files --- diff --git a/.clang-format b/.clang-format index c41d0a208..fbb3509cd 100644 --- a/.clang-format +++ b/.clang-format @@ -17,6 +17,8 @@ IncludeCategories: - Regex: '".*_private\.h"' Priority: -6 - Regex: '"mutt/.*\.h"' + Priority: -5 + - Regex: '"config/.*\.h"' Priority: -4 - Regex: '"email/.*\.h"' Priority: -3 diff --git a/Makefile.autosetup b/Makefile.autosetup index e03cbaa2f..4cfb62b74 100644 --- a/Makefile.autosetup +++ b/Makefile.autosetup @@ -207,6 +207,18 @@ CLEANFILES+= $(LIBCONN) $(LIBCONNOBJS) 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 @@ -333,6 +345,13 @@ $(LIBCONN): $(PWD)/conn $(LIBCONNOBJS) $(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) diff --git a/config/Makefile.am b/config/Makefile.am new file mode 100644 index 000000000..5fdd1a2a3 --- /dev/null +++ b/config/Makefile.am @@ -0,0 +1,13 @@ +## 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 + diff --git a/config/address.c b/config/address.c new file mode 100644 index 000000000..2a39e25df --- /dev/null +++ b/config/address.c @@ -0,0 +1,316 @@ +/** + * @file + * Type representing an email address + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-address Type: Email address + * + * Type representing an email address. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/address.h b/config/address.h new file mode 100644 index 000000000..bbfbc64f3 --- /dev/null +++ b/config/address.h @@ -0,0 +1,34 @@ +/** + * @file + * Type representing an email address + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_ADDRESS_H +#define _CONFIG_ADDRESS_H + +#include + +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 */ diff --git a/config/bool.c b/config/bool.c new file mode 100644 index 000000000..413191c58 --- /dev/null +++ b/config/bool.c @@ -0,0 +1,291 @@ +/** + * @file + * Type representing a boolean + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-bool Type: Boolean + * + * Type representing a boolean. + */ + +#include "config.h" +#include +#include +#include +#include +#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); +} diff --git a/config/bool.h b/config/bool.h new file mode 100644 index 000000000..acb937ec0 --- /dev/null +++ b/config/bool.h @@ -0,0 +1,36 @@ +/** + * @file + * Type representing a boolean + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/config/command.c b/config/command.c new file mode 100644 index 000000000..39ff34219 --- /dev/null +++ b/config/command.c @@ -0,0 +1,283 @@ +/** + * @file + * Type representing a command + * + * @authors + * Copyright (C) 2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-command Type: Command + * + * Type representing a command. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/command.h b/config/command.h new file mode 100644 index 000000000..fae068a6b --- /dev/null +++ b/config/command.h @@ -0,0 +1,30 @@ +/** + * @file + * Type representing a command + * + * @authors + * Copyright (C) 2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_COMMAND_H +#define _CONFIG_COMMAND_H + +struct ConfigSet; + +void command_init(struct ConfigSet *cs); + +#endif /* _CONFIG_COMMAND_H */ diff --git a/config/dump.c b/config/dump.c new file mode 100644 index 000000000..768d37bb4 --- /dev/null +++ b/config/dump.c @@ -0,0 +1,328 @@ +/** + * @file + * Dump all the config + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-dump Dump all the config + * + * Dump all the config items in various formats. + */ + +#include "config.h" +#include +#include +#include +#include +#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; +} diff --git a/config/dump.h b/config/dump.h new file mode 100644 index 000000000..cc0e76048 --- /dev/null +++ b/config/dump.h @@ -0,0 +1,50 @@ +/** + * @file + * Dump all the config + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_DUMP_H +#define _CONFIG_DUMP_H + +#include + +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 */ diff --git a/config/inheritance.h b/config/inheritance.h new file mode 100644 index 000000000..0199556fa --- /dev/null +++ b/config/inheritance.h @@ -0,0 +1,39 @@ +/** + * @file + * An inherited config item + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_INHERITANCE_H +#define _CONFIG_INHERITANCE_H + +#include + +/** + * 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 */ diff --git a/config/lib.h b/config/lib.h new file mode 100644 index 000000000..56052fd91 --- /dev/null +++ b/config/lib.h @@ -0,0 +1,62 @@ +/** + * @file + * Convenience wrapper for the config headers + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @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 */ diff --git a/config/long.c b/config/long.c new file mode 100644 index 000000000..73e7e3994 --- /dev/null +++ b/config/long.c @@ -0,0 +1,228 @@ +/** + * @file + * Type representing a long + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-long Type: Long + * + * Type representing a long. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/long.h b/config/long.h new file mode 100644 index 000000000..95ab884c8 --- /dev/null +++ b/config/long.h @@ -0,0 +1,30 @@ +/** + * @file + * Type representing a long + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_LONG_H +#define _CONFIG_LONG_H + +struct ConfigSet; + +void long_init(struct ConfigSet *cs); + +#endif /* _CONFIG_LONG_H */ diff --git a/config/magic.c b/config/magic.c new file mode 100644 index 000000000..9947c7d61 --- /dev/null +++ b/config/magic.c @@ -0,0 +1,234 @@ +/** + * @file + * Type representing a mailbox + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-magic Type: Mailbox types + * + * Type representing a mailbox. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/magic.h b/config/magic.h new file mode 100644 index 000000000..449ff6b1a --- /dev/null +++ b/config/magic.h @@ -0,0 +1,48 @@ +/** + * @file + * Type representing a mailbox + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/config/mbtable.c b/config/mbtable.c new file mode 100644 index 000000000..aa4f41c60 --- /dev/null +++ b/config/mbtable.c @@ -0,0 +1,352 @@ +/** + * @file + * Type representing a multibyte character table + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-mbtable Type: Multi-byte character table + * + * Type representing a multibyte character table. + */ + +#include "config.h" +#include +#include +#include +#include +#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); +} diff --git a/config/mbtable.h b/config/mbtable.h new file mode 100644 index 000000000..8af58778c --- /dev/null +++ b/config/mbtable.h @@ -0,0 +1,47 @@ +/** + * @file + * Type representing a multibyte character table + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/config/number.c b/config/number.c new file mode 100644 index 000000000..227def2c4 --- /dev/null +++ b/config/number.c @@ -0,0 +1,229 @@ +/** + * @file + * Type representing a number + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-number Type: Number + * + * Type representing a number. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/number.h b/config/number.h new file mode 100644 index 000000000..9e5378a5b --- /dev/null +++ b/config/number.h @@ -0,0 +1,30 @@ +/** + * @file + * Type representing a number + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_NUMBER_H +#define _CONFIG_NUMBER_H + +struct ConfigSet; + +void number_init(struct ConfigSet *cs); + +#endif /* _CONFIG_NUMBER_H */ diff --git a/config/path.c b/config/path.c new file mode 100644 index 000000000..66116f8e1 --- /dev/null +++ b/config/path.c @@ -0,0 +1,281 @@ +/** + * @file + * Type representing a path + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-path Type: Path + * + * Type representing a path. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/path.h b/config/path.h new file mode 100644 index 000000000..a82ddffe1 --- /dev/null +++ b/config/path.h @@ -0,0 +1,30 @@ +/** + * @file + * Type representing a path + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_PATH_H +#define _CONFIG_PATH_H + +struct ConfigSet; + +void path_init(struct ConfigSet *cs); + +#endif /* _CONFIG_PATH_H */ diff --git a/config/quad.c b/config/quad.c new file mode 100644 index 000000000..be5f011de --- /dev/null +++ b/config/quad.c @@ -0,0 +1,282 @@ +/** + * @file + * Type representing a quad-option + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-quad Type: Quad-option + * + * Type representing a quad-option. + */ + +#include "config.h" +#include +#include +#include +#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; +} diff --git a/config/quad.h b/config/quad.h new file mode 100644 index 000000000..61c437482 --- /dev/null +++ b/config/quad.h @@ -0,0 +1,47 @@ +/** + * @file + * Type representing a quad-option + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/config/regex.c b/config/regex.c new file mode 100644 index 000000000..411108374 --- /dev/null +++ b/config/regex.c @@ -0,0 +1,359 @@ +/** + * @file + * Type representing a regular expression + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-regex Type: Regular expression + * + * Type representing a regular expression. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#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); +} diff --git a/config/regex2.h b/config/regex2.h new file mode 100644 index 000000000..6b2d4c934 --- /dev/null +++ b/config/regex2.h @@ -0,0 +1,37 @@ +/** + * @file + * Type representing a regular expression + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_REGEX_H +#define _CONFIG_REGEX_H + +#include +#include +#include + +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 */ diff --git a/config/set.c b/config/set.c new file mode 100644 index 000000000..8fdca904b --- /dev/null +++ b/config/set.c @@ -0,0 +1,908 @@ +/** + * @file + * A collection of config items + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-set Config Set + * + * A collection of config items. + */ + +#include "config.h" +#include +#include +#include +#include +#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); +} diff --git a/config/set.h b/config/set.h new file mode 100644 index 000000000..04b7589d7 --- /dev/null +++ b/config/set.h @@ -0,0 +1,164 @@ +/** + * @file + * A collection of config items + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_SET_H +#define _CONFIG_SET_H + +#include +#include + +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 */ diff --git a/config/sort.c b/config/sort.c new file mode 100644 index 000000000..b16d9d4bd --- /dev/null +++ b/config/sort.c @@ -0,0 +1,418 @@ +/** + * @file + * Type representing a sort option + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-sort Type: Sorting + * + * Type representing a sort option. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/sort.h b/config/sort.h new file mode 100644 index 000000000..b04024d9c --- /dev/null +++ b/config/sort.h @@ -0,0 +1,83 @@ +/** + * @file + * Type representing a sort option + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/config/string.c b/config/string.c new file mode 100644 index 000000000..08df411ed --- /dev/null +++ b/config/string.c @@ -0,0 +1,282 @@ +/** + * @file + * Type representing a string + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +/** + * @page config-string Type: String + * + * Type representing a string. + */ + +#include "config.h" +#include +#include +#include +#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); +} diff --git a/config/string3.h b/config/string3.h new file mode 100644 index 000000000..ab24f49ca --- /dev/null +++ b/config/string3.h @@ -0,0 +1,30 @@ +/** + * @file + * Type representing a string + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#ifndef _CONFIG_STRING_H +#define _CONFIG_STRING_H + +struct ConfigSet; + +void string_init(struct ConfigSet *cs); + +#endif /* _CONFIG_STRING_H */ diff --git a/config/types.h b/config/types.h new file mode 100644 index 000000000..3fc0edb61 --- /dev/null +++ b/config/types.h @@ -0,0 +1,75 @@ +/** + * @file + * Constants for all the config types + * + * @authors + * Copyright (C) 2017-2018 Richard Russon + * + * @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 . + */ + +#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 */ diff --git a/mutt/regex3.h b/mutt/regex3.h index 50b601f7f..bb943842d 100644 --- a/mutt/regex3.h +++ b/mutt/regex3.h @@ -32,6 +32,7 @@ struct Buffer; /* ... 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 \<...\> */ diff --git a/po/POTFILES.in b/po/POTFILES.in index a699b96bd..64903c352 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -8,6 +8,23 @@ commands.c 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