summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Kettlewell <rjk@greenend.org.uk>2015-12-19 18:58:32 +0000
committerRichard Kettlewell <rjk@greenend.org.uk>2015-12-19 18:58:32 +0000
commitf575b72b8ad7980304c32ed62e2f4b317cc3862b (patch)
tree73315ae2f77ae369dfb7d742abfc2864a63238dd
parent931506ede6608313f3be43e11dfd24883044b455 (diff)
Split up Conf.cc a bit
It remains the two largest files...
-rw-r--r--src/Conf.cc697
-rw-r--r--src/ConfDirective.cc588
-rw-r--r--src/ConfDirective.h186
-rw-r--r--src/Makefile.am2
4 files changed, 778 insertions, 695 deletions
diff --git a/src/Conf.cc b/src/Conf.cc
index c2e8103..c62c6eb 100644
--- a/src/Conf.cc
+++ b/src/Conf.cc
@@ -22,701 +22,11 @@
#include "Utils.h"
#include "Database.h"
#include "Prune.h"
-#include <cctype>
-#include <sstream>
+#include "ConfDirective.h"
#include <cerrno>
-#include <cstring>
-#include <cstdlib>
-#include <glob.h>
#include <regex>
#include <boost/filesystem.hpp>
-/** @brief Context for configuration file parsing */
-struct ConfContext {
- /** @brief Constructor
- *
- * @param conf_ Root configuration node
- */
- ConfContext(Conf *conf_):
- conf(conf_), context(conf_) {}
-
- /** @brief Root of configuration */
- Conf *conf;
-
- /** @brief Current configuration node
- *
- * Could be a @ref Conf, @ref Host or @ref Volume.
- */
- ConfBase *context;
-
- /** @brief Current host or null */
- Host *host = nullptr;
-
- /** @brief Current volume or null */
- Volume *volume = nullptr;
-
- /** @brief Parsed directive */
- std::vector<std::string> bits;
-
- /** @brief Containing filename */
- std::string path;
-
- /** @brief Line number */
- int line = -1;
-};
-
-struct Directive;
-
-/** @brief Type of name-to-directive map */
-typedef std::map<std::string, const Directive *> directives_type;
-
-/** @brief Map names to directives */
-static directives_type *directives;
-
-/** @brief Base class for configuration file directives */
-struct Directive {
- /** @brief Constructor
- *
- * @param name_ Name of directive
- * @param min_ Minimum number of arguments
- * @param max_ Maximum number of arguments
- *
- * Directives are automatically registered in this constructor.
- */
- Directive(const char *name_, int min_=0, int max_=INT_MAX):
- name(name_), min(min_), max(max_) {
- if(!directives)
- directives = new directives_type();
- assert((*directives).find(name) == (*directives).end());
- (*directives)[name] = this;
- }
-
- /** @brief Name of directive */
- const std::string name;
-
- /** @brief Minimum number of arguments */
- int min;
-
- /** @brief Maximum number of arguments */
- int max;
-
- /** @brief Check directive syntax
- * @param cc Context containing directive
- *
- * The base class implementation just checks the minimum and maximum number
- * of arguments.
- */
- virtual void check(const ConfContext &cc) const {
- int args = cc.bits.size() - 1;
- if(args < min)
- throw SyntaxError("too few arguments to '" + name + "'");
- if(args > max)
- throw SyntaxError("too many arguments to '" + name + "'");
- }
-
- /** @brief Get a boolean parameter
- * @param cc Context containing directive
- * @return @c true or @c false
- *
- * Use in Directive::set implementations for boolean-sense directives.
- */
- bool get_boolean(const ConfContext &cc) const {
- if(cc.bits.size() == 1) {
- warning("%s:%d: use '%s true' instead of '%s'",
- cc.path.c_str(), cc.line,
- name.c_str(), name.c_str());
- return true;
- } else if(cc.bits[1] == "true")
- return true;
- else if(cc.bits[1] == "false")
- return false;
- else
- throw SyntaxError("invalid argument to '" + name
- + "' - only 'true' or 'false' allowed");
- }
-
- /** @brief Act on a directive
- * @param cc Context containing directive
- */
- virtual void set(ConfContext &cc) const = 0;
-};
-
-/** @brief Base class for directives that can only appear in a host context */
-struct HostOnlyDirective: public Directive {
- /** @brief Constructor
- *
- * @param name_ Name of directive
- * @param min_ Minimum number of arguments
- * @param max_ Maximum number of arguments
- * @param inheritable_ If @c true, also allowed in volume context
- */
- HostOnlyDirective(const char *name_, int min_=0, int max_=INT_MAX,
- bool inheritable_ = false):
- Directive(name_, min_, max_),
- inheritable(inheritable_) {}
- void check(const ConfContext &cc) const override {
- if(cc.host == nullptr)
- throw SyntaxError("'" + name + "' command without 'host'");
- if(cc.volume != nullptr && !inheritable)
- throw SyntaxError("'" + name + "' inside 'volume'");
- Directive::check(cc);
- }
-
- /** @brief If @c true, also allowed in volume context */
- bool inheritable;
-};
-
-/** @brief Base class for directives that can only appear in a volume context */
-struct VolumeOnlyDirective: public Directive {
- /** @brief Constructor
- *
- * @param name_ Name of directive
- * @param min_ Minimum number of arguments
- * @param max_ Maximum number of arguments
- */
- VolumeOnlyDirective(const char *name_, int min_=0, int max_=INT_MAX):
- Directive(name_, min_, max_) {}
- void check(const ConfContext &cc) const override {
- if(cc.volume == nullptr)
- throw SyntaxError("'" + name + "' command without 'volume'");
- Directive::check(cc);
- }
-};
-
-/** @brief Base class for color-setting directives */
-struct ColorDirective: public Directive {
- /** @brief Constructor
- * @param name Name of directive
- */
- ColorDirective(const char *name): Directive(name, 1, 4) {}
-
- void check(const ConfContext &cc) const override {
- int args = cc.bits.size() - 1;
- Directive::check(cc);
- if(args > 1 && args < 4)
- throw SyntaxError("wrong number of arguments to '" + name + "'");
- if(args == 4) {
- if(cc.bits[1] == "rgb"
- || cc.bits[1] == "hsv")
- ; // OK
- else
- throw SyntaxError("invalid color representation '" + cc.bits[1] + "'");
- }
- }
-
- void set(ConfContext &cc) const override {
- int args = cc.bits.size() - 1;
- if(args == 4) {
- if(cc.bits[1] == "rgb")
- set_rgb(cc, 2);
- else if(cc.bits[1] == "hsv")
- set_hsv(cc, 2);
- } else
- set_packed(cc, 1, 0);
- }
-
- /** @brief Parse and set an RGB color representation
- * @param cc Configuration context
- * @param n Index for first element
- */
- void set_rgb(ConfContext &cc, size_t n) const {
- set(cc, Color(parseFloat(cc.bits[n], 0, 1),
- parseFloat(cc.bits[n+1], 0, 1),
- parseFloat(cc.bits[n+2], 0, 1)));
- }
-
- /** @brief Parse and set an HSV color representation
- * @param cc Configuration context
- * @param n Index for first element
- */
- void set_hsv(ConfContext &cc, size_t n) const {
- set(cc, Color::HSV(parseFloat(cc.bits[n]),
- parseFloat(cc.bits[n+1], 0, 1),
- parseFloat(cc.bits[n+2], 0, 1)));
- }
-
- /** @brief Parse and set a packed integer color representation
- * @param cc Configuration context
- * @param n Index for first element
- * @param radix Radix or 0 to pick as per strtol(3)
- */
- void set_packed(ConfContext &cc, size_t n, int radix = 0) const {
- set(cc, Color(parseInteger(cc.bits[n], 0, 0xFFFFFF, radix)));
- }
-
- /** @brief Set a color
- * @param cc Configuration context
- * @param c Color to set
- */
- virtual void set(ConfContext &cc, const Color &c) const = 0;
-
-};
-
-// Global directives ----------------------------------------------------------
-
-/** @brief The @c store directive */
-static const struct StoreDirective: public Directive {
- StoreDirective(): Directive("store", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->stores[cc.bits[1]] = new Store(cc.bits[1]);
- }
-} store_directive;
-
-/** @brief The @c store-pattern directive */
-static const struct StorePatternDirective: public Directive {
- StorePatternDirective(): Directive("store-pattern", 1, 1) {}
- void set(ConfContext &cc) const override {
- std::vector<std::string> files;
- globFiles(files, cc.bits[1], GLOB_NOCHECK);
- for(auto &file: files)
- cc.conf->stores[file] = new Store(file);
- }
-} store_pattern_directive;
-
-/** @brief The @c stylesheet directive */
-static const struct StyleSheetDirective: public Directive {
- StyleSheetDirective(): Directive("stylesheet", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->stylesheet = cc.bits[1];
- }
-} stylesheet_directive;
-
-/** @brief The @c colors directive */
-static const struct ColorsDirective: public Directive {
- ColorsDirective(): Directive("colors", 2, 2) {}
- void set(ConfContext &cc) const override {
- warning("%s:%d: the 'colors' directive is deprecated, use 'color-good' and 'color-bad' instead",
- cc.path.c_str(), cc.line);
- cc.conf->colorGood = parseInteger(cc.bits[1], 0, 0xFFFFFF, 0);
- cc.conf->colorBad = parseInteger(cc.bits[2], 0, 0xFFFFFF, 0);
- }
-} colors_directive;
-
-/** @brief The @c color-good directive */
-static const struct ColorGoodDirective: public ColorDirective {
- ColorGoodDirective(): ColorDirective("color-good") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorGood = c;
- }
-} color_good_directive;
-
-/** @brief The @c color-bad directive */
-static const struct ColorBadDirective: public ColorDirective {
- ColorBadDirective(): ColorDirective("color-bad") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorBad = c;
- }
-} color_bad_directive;
-
-/** @brief The @c device directive */
-static const struct DeviceDirective: public Directive {
- DeviceDirective(): Directive("device", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->devices[cc.bits[1]] = new Device(cc.bits[1]);
- }
-} device_directive;
-
-/** @brief The @c max-usage directive */
-static const struct MaxUsageDirective: public Directive {
- MaxUsageDirective(): Directive("max-usage", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->maxUsage = parseInteger(cc.bits[1], 0, 100);
- }
-} max_usage_directive;
-
-/** @brief The @c max-file-usage directive */
-static const struct MaxFileUsageDirective: public Directive {
- MaxFileUsageDirective(): Directive("max-file-usage", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->maxFileUsage = parseInteger(cc.bits[1], 0, 100);
- }
-} max_file_usage_directive;
-
-/** @brief The @c public directive */
-static const struct PublicDirective: public Directive {
- PublicDirective(): Directive("public", 0, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->publicStores = get_boolean(cc);
- }
-} public_directive;
-
-/** @brief The @c logs directive */
-static const struct LogsDirective: public Directive {
- LogsDirective(): Directive("logs", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->logs = cc.bits[1];
- }
-} logs_directive;
-
-/** @brief The @c lock directive */
-static const struct LockDirective: public Directive {
- LockDirective(): Directive("lock", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->lock = cc.bits[1];
- }
-} lock_directive;
-
-/** @brief The @c sendmail directive */
-static const struct SendmailDirective: public Directive {
- SendmailDirective(): Directive("sendmail", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->sendmail = cc.bits[1];
- }
-} sendmail_directive;
-
-/** @brief The @c pre-access-hook directive */
-static const struct PreAccessHookDirective: public Directive {
- PreAccessHookDirective(): Directive("pre-access-hook", 1, INT_MAX) {}
- void set(ConfContext &cc) const override {
- cc.conf->preAccess.assign(cc.bits.begin() + 1, cc.bits.end());
- }
-} pre_access_hook_directive;
-
-/** @brief The @c post-access-hook directive */
-static const struct PostAccessHookDirective: public Directive {
- PostAccessHookDirective(): Directive("post-access-hook", 1, INT_MAX) {}
- void set(ConfContext &cc) const override {
- cc.conf->postAccess.assign(cc.bits.begin() + 1, cc.bits.end());
- }
-} post_access_hook_directive;
-
-/** @brief The @c keep-prune-logs directive */
-static const struct KeepPruneLogsDirective: public Directive {
- KeepPruneLogsDirective(): Directive("keep-prune-logs", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->keepPruneLogs = parseInteger(cc.bits[1], 1);
- }
-} keep_prune_logs_directive;
-
-/** @brief The @c report-prune-logs directive */
-static const struct ReportPruneLogsDirective: public Directive {
- ReportPruneLogsDirective(): Directive("report-prune-logs", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->reportPruneLogs = parseInteger(cc.bits[1], 1);
- }
-} report_prune_logs_directive;
-
-/** @brief The @c include directive */
-static const struct IncludeDirective: public Directive {
- IncludeDirective(): Directive("include", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.conf->includeFile(cc.bits[1]);
- }
-} include_directive;
-
-/** @brief The color-graph-background directive */
-static const struct ColorGraphBackgroundDirective: public ColorDirective {
- ColorGraphBackgroundDirective(): ColorDirective("color-graph-background") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorGraphBackground = c;
- }
-} color_graph_background_directive;
-
-/** @brief The color-graph-foreground directive */
-static const struct ColorGraphForegroundDirective: public ColorDirective {
- ColorGraphForegroundDirective(): ColorDirective("color-graph-foreground") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorGraphForeground = c;
- }
-} color_graph_foreground_directive;
-
-/** @brief The color-month-guide directive */
-static const struct ColorMonthGuideDirective: public ColorDirective {
- ColorMonthGuideDirective(): ColorDirective("color-month-guide") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorMonthGuide = c;
- }
-} color_month_guide_directive;
-
-/** @brief The color-host-guide directive */
-static const struct ColorHostGuideDirective: public ColorDirective {
- ColorHostGuideDirective(): ColorDirective("color-host-guide") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorHostGuide = c;
- }
-} color_host_guide_directive;
-
-/** @brief The color-volume-guide directive */
-static const struct ColorVolumeGuideDirective: public ColorDirective {
- ColorVolumeGuideDirective(): ColorDirective("color-volume-guide") {}
- void set(ConfContext &cc, const Color &c) const override {
- cc.conf->colorVolumeGuide = c;
- }
-} color_volume_guide_directive;
-
-/** @brief The device-color-strategy directive */
-static const struct DeviceColorStrategyDirective: public Directive {
- DeviceColorStrategyDirective(): Directive("device-color-strategy",
- 1, INT_MAX) {}
- void set(ConfContext &cc) const override {
- ColorStrategy *nc = ColorStrategy::create(cc.bits[1], cc.bits, 2);
- delete cc.conf->deviceColorStrategy;
- cc.conf->deviceColorStrategy = nc;
- }
-} device_color_strategy_directive;
-
-/** @brief The horizontal-padding directive */
-static const struct HorizontalPaddingDirective: public Directive {
- HorizontalPaddingDirective(): Directive("horizontal-padding") {}
- void set(ConfContext &cc) const override {
- cc.conf->horizontalPadding = parseFloat(cc.bits[1],
- 0,
- std::numeric_limits<double>::max());
- }
-} horizontal_padding_directive;
-
-/** @brief The vertical-padding directive */
-static const struct VerticalPaddingDirective: public Directive {
- VerticalPaddingDirective(): Directive("vertical-padding") {}
- void set(ConfContext &cc) const override {
- cc.conf->verticalPadding = parseFloat(cc.bits[1],
- 0,
- std::numeric_limits<double>::max());
- }
-} vertical_padding_directive;
-
-/** @brief The backup-indicator-width directive */
-static const struct BackupIndicatorWidthDirective: public Directive {
- BackupIndicatorWidthDirective(): Directive("backup-indicator-width") {}
- void set(ConfContext &cc) const override {
- cc.conf->backupIndicatorWidth
- = parseFloat(cc.bits[1],
- std::numeric_limits<double>::min(),
- std::numeric_limits<double>::max());
- }
-} backup_indicator_width_directive;
-
-/** @brief The backup-indicator-height directive */
-static const struct BackupIndicatorHeightDirective: public Directive {
- BackupIndicatorHeightDirective(): Directive("backup-indicator-height") {}
- void set(ConfContext &cc) const override {
- cc.conf->backupIndicatorHeight
- = parseFloat(cc.bits[1],
- std::numeric_limits<double>::min(),
- std::numeric_limits<double>::max());
- }
-} backup_indicator_height_directive;
-
-/** @brief The backup-indicator-key-width directive */
-static const struct BackupIndicatorKeyWidthDirective: public Directive {
- BackupIndicatorKeyWidthDirective(): Directive("backup-indicator-key-width") {}
- void set(ConfContext &cc) const override {
- cc.conf->backupIndicatorKeyWidth
- = parseFloat(cc.bits[1],
- std::numeric_limits<double>::min(),
- std::numeric_limits<double>::max());
- }
-} backup_indicator_key_width_directive;
-
-// Inheritable directives -----------------------------------------------------
-
-/** @brief The @c max-age directive */
-static const struct MaxAgeDirective: public Directive {
- MaxAgeDirective(): Directive("max-age", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.context->maxAge = parseInteger(cc.bits[1], 1);
- }
-} max_age_directive;
-
-/** @brief The @c min-backups directive */
-static const struct MinBackupsDirective: public Directive {
- MinBackupsDirective(): Directive("min-backups", 1, 1) {}
- void set(ConfContext &cc) const override {
- warning("%s:%d: the 'min-backups' directive is deprecated, use 'prune-parameter min-backups' instead",
- cc.path.c_str(), cc.line);
- parseInteger(cc.bits[1], 1);
- cc.context->pruneParameters["min-backups"] = cc.bits[1];
- }
-} min_backups_directive;
-
-/** @brief The @c prune-age directive */
-static const struct PruneAgeDirective: public Directive {
- PruneAgeDirective(): Directive("prune-age", 1, 1) {}
- void set(ConfContext &cc) const override {
- warning("%s:%d: the 'prune-age' directive is deprecated, use 'prune-parameter prune-age' instead",
- cc.path.c_str(), cc.line);
- parseInteger(cc.bits[1], 1);
- cc.context->pruneParameters["prune-age"] = cc.bits[1];
- }
-} prune_age_directive;
-
-/** @brief The @c prune-policy directive */
-static const struct PrunePolicyDirective: public Directive {
- PrunePolicyDirective(): Directive("prune-policy", 1, 1) {}
- void set(ConfContext &cc) const override {
- if(cc.bits[1].size() > 0 && cc.bits[1].at(0) == '/') {
- cc.context->prunePolicy = "exec";
- cc.context->pruneParameters["path"] = cc.bits[1];
- } else
- cc.context->prunePolicy = cc.bits[1];
- }
-} prune_policy_directive;
-
-/** @brief The @c prune-parameter directive */
-static const struct PruneParameterDirective: public Directive {
- PruneParameterDirective(): Directive("prune-parameter", 2, 2) {}
- void check(const ConfContext &cc) const override {
- Directive::check(cc);
- const std::string &name = (cc.bits[1] != "--remove" ?
- cc.bits[1] : cc.bits[2]);
- if(!valid(name))
- throw SyntaxError("invalid prune-parameter name");
- }
- void set(ConfContext &cc) const override {
- if(cc.bits[1] != "--remove")
- cc.context->pruneParameters[cc.bits[1]] = cc.bits[2];
- else
- cc.context->pruneParameters.erase(cc.bits[2]);
- }
- static bool valid(const std::string &name) {
- return name.size() > 0
- && name.at(0) != '-'
- && name.find_first_not_of(PRUNE_PARAMETER_VALID) == std::string::npos;
- }
-} prune_parameter_directive;
-
-/** @brief The @c pre-backup-hook directive */
-static const struct PreBackupHookDirective: public Directive {
- PreBackupHookDirective(): Directive("pre-backup-hook", 1, INT_MAX) {}
- void set(ConfContext &cc) const override {
- cc.context->preBackup.assign(cc.bits.begin() + 1, cc.bits.end());
- }
-} pre_backup_hook_directive;
-
-/** @brief The @c post-backup-hook directive */
-static const struct PostBackupHookDirective: public Directive {
- PostBackupHookDirective(): Directive("post-backup-hook", 1, INT_MAX) {}
- void set(ConfContext &cc) const override {
- cc.context->postBackup.assign(cc.bits.begin() + 1, cc.bits.end());
- }
-} post_backup_hook_directive;
-
-/** @brief The @c rsync-timeout directive */
-static const struct RsyncTimeoutDirective: public Directive {
- RsyncTimeoutDirective(): Directive("rsync-timeout", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.context->rsyncTimeout = parseInteger(cc.bits[1], 1);
- }
-} rsync_timeout_directive;
-
-/** @brief The @c hook-timeout directive */
-static const struct HookTimeoutDirective: public Directive {
- HookTimeoutDirective(): Directive("hook-timeout", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.context->hookTimeout = parseInteger(cc.bits[1], 1);
- }
-} hook_timeout_directive;
-
-/** @brief The @c ssh-timeout directive */
-static const struct SshTimeoutDirective: public Directive {
- SshTimeoutDirective(): Directive("ssh-timeout", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.context->sshTimeout = parseInteger(cc.bits[1], 1);
- }
-} ssh_timeout_directive;
-
-// Host directives ------------------------------------------------------------
-
-/** @brief The @c host directive */
-static const struct HostDirective: public Directive {
- HostDirective(): Directive("host", 1, 1) {}
- void set(ConfContext &cc) const override {
- if(!Host::valid(cc.bits[1]))
- throw SyntaxError("invalid host name");
- if(contains(cc.conf->hosts, cc.bits[1]))
- throw SyntaxError("duplicate host");
- cc.context = cc.host = new Host(cc.conf, cc.bits[1]);
- cc.volume = nullptr;
- cc.host->hostname = cc.bits[1];
- }
-} host_directive;
-
-/** @brief The @c hostname directive */
-static const struct HostnameDirective: public HostOnlyDirective {
- HostnameDirective(): HostOnlyDirective("hostname", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.host->hostname = cc.bits[1];
- }
-} hostname_directive;
-
-/** @brief The @c always-up directive */
-static const struct AlwaysUpDirective: public HostOnlyDirective {
- AlwaysUpDirective(): HostOnlyDirective("always-up", 0, 1) {}
- void set(ConfContext &cc) const override {
- cc.host->alwaysUp = get_boolean(cc);
- }
-} always_up_directive;
-
-/** @brief The @c priority directive */
-static const struct PriorityDirective: public HostOnlyDirective {
- PriorityDirective(): HostOnlyDirective("priority", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.host->priority = parseInteger(cc.bits[1]);
- }
-} priority_directive;
-
-/** @brief The @c user directive */
-static const struct UserDirective: public HostOnlyDirective {
- UserDirective(): HostOnlyDirective("user", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.host->user = cc.bits[1];
- }
-} user_directive;
-
-/** @brief The @c devices directive */
-static const struct DevicesDirective: public HostOnlyDirective {
- DevicesDirective(): HostOnlyDirective("devices", 1, 1, true) {}
- void set(ConfContext &cc) const override {
- cc.context->devicePattern = cc.bits[1];
- }
-} devices_directive;
-
-// Volume directives ----------------------------------------------------------
-
-/** @brief The @c volume directive */
-static const struct VolumeDirective: public HostOnlyDirective {
- VolumeDirective(): HostOnlyDirective("volume", 2, 2, true/*hacky*/) {}
- void set(ConfContext &cc) const override {
- if(!Volume::valid(cc.bits[1]))
- throw SyntaxError("invalid volume name");
- if(contains(cc.host->volumes, cc.bits[1]))
- throw SyntaxError("duplicate volume");
- cc.context = cc.volume = new Volume(cc.host, cc.bits[1], cc.bits[2]);
- }
-} volume_directive;
-
-/** @brief The @c exclude directive */
-static const struct ExcludeDirective: public VolumeOnlyDirective {
- ExcludeDirective(): VolumeOnlyDirective("exclude", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.volume->exclude.push_back(cc.bits[1]);
- }
-} exclude_directive;
-
-/** @brief The @c traverse directive */
-static const struct TraverseDirective: public VolumeOnlyDirective {
- TraverseDirective(): VolumeOnlyDirective("traverse", 0, 1) {}
- void set(ConfContext &cc) const override {
- cc.volume->traverse = get_boolean(cc);
- }
-} traverse_directive;
-
-/** @brief The @c check-file directive */
-static const struct CheckFileDirective: public VolumeOnlyDirective {
- CheckFileDirective(): VolumeOnlyDirective("check-file", 1, 1) {}
- void set(ConfContext &cc) const override {
- cc.volume->checkFile = cc.bits[1];
- }
-} check_file_directive;
-
-/** @brief The @c check-mounted directive */
-static const struct CheckMountedDirective: public VolumeOnlyDirective {
- CheckMountedDirective(): VolumeOnlyDirective("check-mounted", 0, 1) {}
- void set(ConfContext &cc) const override {
- cc.volume->checkMounted = get_boolean(cc);
- }
-} check_mounted_directive;
-
Conf::Conf() {
std::vector<std::string> args;
args.push_back("120");
@@ -892,9 +202,8 @@ void Conf::readOneFile(const std::string &path) {
if(!cc.bits.size()) // skip blank lines
continue;
// Consider all the possible commands
- auto it = (*directives).find(cc.bits[0]);
- if(it != (*directives).end()) {
- const Directive *d = it->second;
+ const ConfDirective *d = ConfDirective::find(cc.bits[0]);
+ if(d) {
d->check(cc);
d->set(cc);
} else {
diff --git a/src/ConfDirective.cc b/src/ConfDirective.cc
new file mode 100644
index 0000000..dcc68d1
--- /dev/null
+++ b/src/ConfDirective.cc
@@ -0,0 +1,588 @@
+// Copyright © 2011, 2012, 2014, 2015 Richard Kettlewell.
+//
+// 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#include <config.h>
+#include "rsbackup.h"
+#include "Conf.h"
+#include "Store.h"
+#include "Errors.h"
+#include "Utils.h"
+#include "ConfDirective.h"
+#include <glob.h>
+
+// ConfDirective --------------------------------------------------------------
+
+ConfDirective::ConfDirective(const char *name_, int min_, int max_):
+ name(name_), min(min_), max(max_) {
+ if(!directives)
+ directives = new directives_type();
+ assert((*directives).find(name) == (*directives).end());
+ (*directives)[name] = this;
+}
+
+const ConfDirective *ConfDirective::find(const std::string &name) {
+ auto it = directives->find(name);
+ return it == directives->end() ? nullptr : it->second;
+}
+
+void ConfDirective::check(const ConfContext &cc) const {
+ int args = cc.bits.size() - 1;
+ if(args < min)
+ throw SyntaxError("too few arguments to '" + name + "'");
+ if(args > max)
+ throw SyntaxError("too many arguments to '" + name + "'");
+}
+
+bool ConfDirective::get_boolean(const ConfContext &cc) const {
+ if(cc.bits.size() == 1) {
+ warning("%s:%d: use '%s true' instead of '%s'",
+ cc.path.c_str(), cc.line,
+ name.c_str(), name.c_str());
+ return true;
+ } else if(cc.bits[1] == "true")
+ return true;
+ else if(cc.bits[1] == "false")
+ return false;
+ else
+ throw SyntaxError("invalid argument to '" + name
+ + "' - only 'true' or 'false' allowed");
+}
+
+directives_type *ConfDirective::directives;
+
+// HostOnlyDirective ----------------------------------------------------------
+
+void HostOnlyDirective::check(const ConfContext &cc) const {
+ if(cc.host == nullptr)
+ throw SyntaxError("'" + name + "' command without 'host'");
+ if(cc.volume != nullptr && !inheritable)
+ throw SyntaxError("'" + name + "' inside 'volume'");
+ ConfDirective::check(cc);
+}
+
+// VolumeOnlyDirective --------------------------------------------------------
+
+void VolumeOnlyDirective::check(const ConfContext &cc) const {
+ if(cc.volume == nullptr)
+ throw SyntaxError("'" + name + "' command without 'volume'");
+ ConfDirective::check(cc);
+}
+
+// ColorDirective -------------------------------------------------------------
+
+void ColorDirective::check(const ConfContext &cc) const {
+ int args = cc.bits.size() - 1;
+ ConfDirective::check(cc);
+ if(args > 1 && args < 4)
+ throw SyntaxError("wrong number of arguments to '" + name + "'");
+ if(args == 4) {
+ if(cc.bits[1] == "rgb"
+ || cc.bits[1] == "hsv")
+ ; // OK
+ else
+ throw SyntaxError("invalid color representation '" + cc.bits[1] + "'");
+ }
+}
+
+void ColorDirective::set(ConfContext &cc) const {
+ int args = cc.bits.size() - 1;
+ if(args == 4) {
+ if(cc.bits[1] == "rgb")
+ set_rgb(cc, 2);
+ else if(cc.bits[1] == "hsv")
+ set_hsv(cc, 2);
+ } else
+ set_packed(cc, 1, 0);
+}
+
+void ColorDirective::set_rgb(ConfContext &cc, size_t n) const {
+ set(cc, Color(parseFloat(cc.bits[n], 0, 1),
+ parseFloat(cc.bits[n+1], 0, 1),
+ parseFloat(cc.bits[n+2], 0, 1)));
+}
+
+void ColorDirective::set_hsv(ConfContext &cc, size_t n) const {
+ set(cc, Color::HSV(parseFloat(cc.bits[n]),
+ parseFloat(cc.bits[n+1], 0, 1),
+ parseFloat(cc.bits[n+2], 0, 1)));
+}
+
+void ColorDirective::set_packed(ConfContext &cc, size_t n, int radix) const {
+ set(cc, Color(parseInteger(cc.bits[n], 0, 0xFFFFFF, radix)));
+}
+
+// Global directives ----------------------------------------------------------
+
+/** @brief The @c store directive */
+static const struct StoreDirective: public ConfDirective {
+ StoreDirective(): ConfDirective("store", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->stores[cc.bits[1]] = new Store(cc.bits[1]);
+ }
+} store_directive;
+
+/** @brief The @c store-pattern directive */
+static const struct StorePatternDirective: public ConfDirective {
+ StorePatternDirective(): ConfDirective("store-pattern", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ std::vector<std::string> files;
+ globFiles(files, cc.bits[1], GLOB_NOCHECK);
+ for(auto &file: files)
+ cc.conf->stores[file] = new Store(file);
+ }
+} store_pattern_directive;
+
+/** @brief The @c stylesheet directive */
+static const struct StyleSheetDirective: public ConfDirective {
+ StyleSheetDirective(): ConfDirective("stylesheet", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->stylesheet = cc.bits[1];
+ }
+} stylesheet_directive;
+
+/** @brief The @c colors directive */
+static const struct ColorsDirective: public ConfDirective {
+ ColorsDirective(): ConfDirective("colors", 2, 2) {}
+ void set(ConfContext &cc) const override {
+ warning("%s:%d: the 'colors' directive is deprecated, use 'color-good' and 'color-bad' instead",
+ cc.path.c_str(), cc.line);
+ cc.conf->colorGood = parseInteger(cc.bits[1], 0, 0xFFFFFF, 0);
+ cc.conf->colorBad = parseInteger(cc.bits[2], 0, 0xFFFFFF, 0);
+ }
+} colors_directive;
+
+/** @brief The @c color-good directive */
+static const struct ColorGoodDirective: public ColorDirective {
+ ColorGoodDirective(): ColorDirective("color-good") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorGood = c;
+ }
+} color_good_directive;
+
+/** @brief The @c color-bad directive */
+static const struct ColorBadDirective: public ColorDirective {
+ ColorBadDirective(): ColorDirective("color-bad") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorBad = c;
+ }
+} color_bad_directive;
+
+/** @brief The @c device directive */
+static const struct DeviceDirective: public ConfDirective {
+ DeviceDirective(): ConfDirective("device", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->devices[cc.bits[1]] = new Device(cc.bits[1]);
+ }
+} device_directive;
+
+/** @brief The @c max-usage directive */
+static const struct MaxUsageDirective: public ConfDirective {
+ MaxUsageDirective(): ConfDirective("max-usage", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->maxUsage = parseInteger(cc.bits[1], 0, 100);
+ }
+} max_usage_directive;
+
+/** @brief The @c max-file-usage directive */
+static const struct MaxFileUsageDirective: public ConfDirective {
+ MaxFileUsageDirective(): ConfDirective("max-file-usage", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->maxFileUsage = parseInteger(cc.bits[1], 0, 100);
+ }
+} max_file_usage_directive;
+
+/** @brief The @c public directive */
+static const struct PublicDirective: public ConfDirective {
+ PublicDirective(): ConfDirective("public", 0, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->publicStores = get_boolean(cc);
+ }
+} public_directive;
+
+/** @brief The @c logs directive */
+static const struct LogsDirective: public ConfDirective {
+ LogsDirective(): ConfDirective("logs", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->logs = cc.bits[1];
+ }
+} logs_directive;
+
+/** @brief The @c lock directive */
+static const struct LockDirective: public ConfDirective {
+ LockDirective(): ConfDirective("lock", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->lock = cc.bits[1];
+ }
+} lock_directive;
+
+/** @brief The @c sendmail directive */
+static const struct SendmailDirective: public ConfDirective {
+ SendmailDirective(): ConfDirective("sendmail", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->sendmail = cc.bits[1];
+ }
+} sendmail_directive;
+
+/** @brief The @c pre-access-hook directive */
+static const struct PreAccessHookDirective: public ConfDirective {
+ PreAccessHookDirective(): ConfDirective("pre-access-hook", 1, INT_MAX) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->preAccess.assign(cc.bits.begin() + 1, cc.bits.end());
+ }
+} pre_access_hook_directive;
+
+/** @brief The @c post-access-hook directive */
+static const struct PostAccessHookDirective: public ConfDirective {
+ PostAccessHookDirective(): ConfDirective("post-access-hook", 1, INT_MAX) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->postAccess.assign(cc.bits.begin() + 1, cc.bits.end());
+ }
+} post_access_hook_directive;
+
+/** @brief The @c keep-prune-logs directive */
+static const struct KeepPruneLogsDirective: public ConfDirective {
+ KeepPruneLogsDirective(): ConfDirective("keep-prune-logs", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->keepPruneLogs = parseInteger(cc.bits[1], 1);
+ }
+} keep_prune_logs_directive;
+
+/** @brief The @c report-prune-logs directive */
+static const struct ReportPruneLogsDirective: public ConfDirective {
+ ReportPruneLogsDirective(): ConfDirective("report-prune-logs", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->reportPruneLogs = parseInteger(cc.bits[1], 1);
+ }
+} report_prune_logs_directive;
+
+/** @brief The @c include directive */
+static const struct IncludeDirective: public ConfDirective {
+ IncludeDirective(): ConfDirective("include", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.conf->includeFile(cc.bits[1]);
+ }
+} include_directive;
+
+/** @brief The color-graph-background directive */
+static const struct ColorGraphBackgroundDirective: public ColorDirective {
+ ColorGraphBackgroundDirective(): ColorDirective("color-graph-background") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorGraphBackground = c;
+ }
+} color_graph_background_directive;
+
+/** @brief The color-graph-foreground directive */
+static const struct ColorGraphForegroundDirective: public ColorDirective {
+ ColorGraphForegroundDirective(): ColorDirective("color-graph-foreground") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorGraphForeground = c;
+ }
+} color_graph_foreground_directive;
+
+/** @brief The color-month-guide directive */
+static const struct ColorMonthGuideDirective: public ColorDirective {
+ ColorMonthGuideDirective(): ColorDirective("color-month-guide") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorMonthGuide = c;
+ }
+} color_month_guide_directive;
+
+/** @brief The color-host-guide directive */
+static const struct ColorHostGuideDirective: public ColorDirective {
+ ColorHostGuideDirective(): ColorDirective("color-host-guide") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorHostGuide = c;
+ }
+} color_host_guide_directive;
+
+/** @brief The color-volume-guide directive */
+static const struct ColorVolumeGuideDirective: public ColorDirective {
+ ColorVolumeGuideDirective(): ColorDirective("color-volume-guide") {}
+ void set(ConfContext &cc, const Color &c) const override {
+ cc.conf->colorVolumeGuide = c;
+ }
+} color_volume_guide_directive;
+
+/** @brief The device-color-strategy directive */
+static const struct DeviceColorStrategyDirective: public ConfDirective {
+ DeviceColorStrategyDirective(): ConfDirective("device-color-strategy",
+ 1, INT_MAX) {}
+ void set(ConfContext &cc) const override {
+ ColorStrategy *nc = ColorStrategy::create(cc.bits[1], cc.bits, 2);
+ delete cc.conf->deviceColorStrategy;
+ cc.conf->deviceColorStrategy = nc;
+ }
+} device_color_strategy_directive;
+
+/** @brief The horizontal-padding directive */
+static const struct HorizontalPaddingDirective: public ConfDirective {
+ HorizontalPaddingDirective(): ConfDirective("horizontal-padding") {}
+ void set(ConfContext &cc) const override {
+ cc.conf->horizontalPadding = parseFloat(cc.bits[1],
+ 0,
+ std::numeric_limits<double>::max());
+ }
+} horizontal_padding_directive;
+
+/** @brief The vertical-padding directive */
+static const struct VerticalPaddingDirective: public ConfDirective {
+ VerticalPaddingDirective(): ConfDirective("vertical-padding") {}
+ void set(ConfContext &cc) const override {
+ cc.conf->verticalPadding = parseFloat(cc.bits[1],
+ 0,
+ std::numeric_limits<double>::max());
+ }
+} vertical_padding_directive;
+
+/** @brief The backup-indicator-width directive */
+static const struct BackupIndicatorWidthDirective: public ConfDirective {
+ BackupIndicatorWidthDirective(): ConfDirective("backup-indicator-width") {}
+ void set(ConfContext &cc) const override {
+ cc.conf->backupIndicatorWidth
+ = parseFloat(cc.bits[1],
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max());
+ }
+} backup_indicator_width_directive;
+
+/** @brief The backup-indicator-height directive */
+static const struct BackupIndicatorHeightDirective: public ConfDirective {
+ BackupIndicatorHeightDirective(): ConfDirective("backup-indicator-height") {}
+ void set(ConfContext &cc) const override {
+ cc.conf->backupIndicatorHeight
+ = parseFloat(cc.bits[1],
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max());
+ }
+} backup_indicator_height_directive;
+
+/** @brief The backup-indicator-key-width directive */
+static const struct BackupIndicatorKeyWidthDirective: public ConfDirective {
+ BackupIndicatorKeyWidthDirective(): ConfDirective("backup-indicator-key-width") {}
+ void set(ConfContext &cc) const override {
+ cc.conf->backupIndicatorKeyWidth
+ = parseFloat(cc.bits[1],
+ std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max());
+ }
+} backup_indicator_key_width_directive;
+
+// Inheritable directives -----------------------------------------------------
+
+/** @brief The @c max-age directive */
+static const struct MaxAgeDirective: public ConfDirective {
+ MaxAgeDirective(): ConfDirective("max-age", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.context->maxAge = parseInteger(cc.bits[1], 1);
+ }
+} max_age_directive;
+
+/** @brief The @c min-backups directive */
+static const struct MinBackupsDirective: public ConfDirective {
+ MinBackupsDirective(): ConfDirective("min-backups", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ warning("%s:%d: the 'min-backups' directive is deprecated, use 'prune-parameter min-backups' instead",
+ cc.path.c_str(), cc.line);
+ parseInteger(cc.bits[1], 1);
+ cc.context->pruneParameters["min-backups"] = cc.bits[1];
+ }
+} min_backups_directive;
+
+/** @brief The @c prune-age directive */
+static const struct PruneAgeDirective: public ConfDirective {
+ PruneAgeDirective(): ConfDirective("prune-age", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ warning("%s:%d: the 'prune-age' directive is deprecated, use 'prune-parameter prune-age' instead",
+ cc.path.c_str(), cc.line);
+ parseInteger(cc.bits[1], 1);
+ cc.context->pruneParameters["prune-age"] = cc.bits[1];
+ }
+} prune_age_directive;
+
+/** @brief The @c prune-policy directive */
+static const struct PrunePolicyDirective: public ConfDirective {
+ PrunePolicyDirective(): ConfDirective("prune-policy", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ if(cc.bits[1].size() > 0 && cc.bits[1].at(0) == '/') {
+ cc.context->prunePolicy = "exec";
+ cc.context->pruneParameters["path"] = cc.bits[1];
+ } else
+ cc.context->prunePolicy = cc.bits[1];
+ }
+} prune_policy_directive;
+
+/** @brief The @c prune-parameter directive */
+static const struct PruneParameterDirective: public ConfDirective {
+ PruneParameterDirective(): ConfDirective("prune-parameter", 2, 2) {}
+ void check(const ConfContext &cc) const override {
+ ConfDirective::check(cc);
+ const std::string &name = (cc.bits[1] != "--remove" ?
+ cc.bits[1] : cc.bits[2]);
+ if(!valid(name))
+ throw SyntaxError("invalid prune-parameter name");
+ }
+ void set(ConfContext &cc) const override {
+ if(cc.bits[1] != "--remove")
+ cc.context->pruneParameters[cc.bits[1]] = cc.bits[2];
+ else
+ cc.context->pruneParameters.erase(cc.bits[2]);
+ }
+ static bool valid(const std::string &name) {
+ return name.size() > 0
+ && name.at(0) != '-'
+ && name.find_first_not_of(PRUNE_PARAMETER_VALID) == std::string::npos;
+ }
+} prune_parameter_directive;
+
+/** @brief The @c pre-backup-hook directive */
+static const struct PreBackupHookDirective: public ConfDirective {
+ PreBackupHookDirective(): ConfDirective("pre-backup-hook", 1, INT_MAX) {}
+ void set(ConfContext &cc) const override {
+ cc.context->preBackup.assign(cc.bits.begin() + 1, cc.bits.end());
+ }
+} pre_backup_hook_directive;
+
+/** @brief The @c post-backup-hook directive */
+static const struct PostBackupHookDirective: public ConfDirective {
+ PostBackupHookDirective(): ConfDirective("post-backup-hook", 1, INT_MAX) {}
+ void set(ConfContext &cc) const override {
+ cc.context->postBackup.assign(cc.bits.begin() + 1, cc.bits.end());
+ }
+} post_backup_hook_directive;
+
+/** @brief The @c rsync-timeout directive */
+static const struct RsyncTimeoutDirective: public ConfDirective {
+ RsyncTimeoutDirective(): ConfDirective("rsync-timeout", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.context->rsyncTimeout = parseInteger(cc.bits[1], 1);
+ }
+} rsync_timeout_directive;
+
+/** @brief The @c hook-timeout directive */
+static const struct HookTimeoutDirective: public ConfDirective {
+ HookTimeoutDirective(): ConfDirective("hook-timeout", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.context->hookTimeout = parseInteger(cc.bits[1], 1);
+ }
+} hook_timeout_directive;
+
+/** @brief The @c ssh-timeout directive */
+static const struct SshTimeoutDirective: public ConfDirective {
+ SshTimeoutDirective(): ConfDirective("ssh-timeout", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.context->sshTimeout = parseInteger(cc.bits[1], 1);
+ }
+} ssh_timeout_directive;
+
+// Host directives ------------------------------------------------------------
+
+/** @brief The @c host directive */
+static const struct HostDirective: public ConfDirective {
+ HostDirective(): ConfDirective("host", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ if(!Host::valid(cc.bits[1]))
+ throw SyntaxError("invalid host name");
+ if(contains(cc.conf->hosts, cc.bits[1]))
+ throw SyntaxError("duplicate host");
+ cc.context = cc.host = new Host(cc.conf, cc.bits[1]);
+ cc.volume = nullptr;
+ cc.host->hostname = cc.bits[1];
+ }
+} host_directive;
+
+/** @brief The @c hostname directive */
+static const struct HostnameDirective: public HostOnlyDirective {
+ HostnameDirective(): HostOnlyDirective("hostname", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.host->hostname = cc.bits[1];
+ }
+} hostname_directive;
+
+/** @brief The @c always-up directive */
+static const struct AlwaysUpDirective: public HostOnlyDirective {
+ AlwaysUpDirective(): HostOnlyDirective("always-up", 0, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.host->alwaysUp = get_boolean(cc);
+ }
+} always_up_directive;
+
+/** @brief The @c priority directive */
+static const struct PriorityDirective: public HostOnlyDirective {
+ PriorityDirective(): HostOnlyDirective("priority", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.host->priority = parseInteger(cc.bits[1]);
+ }
+} priority_directive;
+
+/** @brief The @c user directive */
+static const struct UserDirective: public HostOnlyDirective {
+ UserDirective(): HostOnlyDirective("user", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.host->user = cc.bits[1];
+ }
+} user_directive;
+
+/** @brief The @c devices directive */
+static const struct DevicesDirective: public HostOnlyDirective {
+ DevicesDirective(): HostOnlyDirective("devices", 1, 1, true) {}
+ void set(ConfContext &cc) const override {
+ cc.context->devicePattern = cc.bits[1];
+ }
+} devices_directive;
+
+// Volume directives ----------------------------------------------------------
+
+/** @brief The @c volume directive */
+static const struct VolumeDirective: public HostOnlyDirective {
+ VolumeDirective(): HostOnlyDirective("volume", 2, 2, true/*hacky*/) {}
+ void set(ConfContext &cc) const override {
+ if(!Volume::valid(cc.bits[1]))
+ throw SyntaxError("invalid volume name");
+ if(contains(cc.host->volumes, cc.bits[1]))
+ throw SyntaxError("duplicate volume");
+ cc.context = cc.volume = new Volume(cc.host, cc.bits[1], cc.bits[2]);
+ }
+} volume_directive;
+
+/** @brief The @c exclude directive */
+static const struct ExcludeDirective: public VolumeOnlyDirective {
+ ExcludeDirective(): VolumeOnlyDirective("exclude", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.volume->exclude.push_back(cc.bits[1]);
+ }
+} exclude_directive;
+
+/** @brief The @c traverse directive */
+static const struct TraverseDirective: public VolumeOnlyDirective {
+ TraverseDirective(): VolumeOnlyDirective("traverse", 0, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.volume->traverse = get_boolean(cc);
+ }
+} traverse_directive;
+
+/** @brief The @c check-file directive */
+static const struct CheckFileDirective: public VolumeOnlyDirective {
+ CheckFileDirective(): VolumeOnlyDirective("check-file", 1, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.volume->checkFile = cc.bits[1];
+ }
+} check_file_directive;
+
+/** @brief The @c check-mounted directive */
+static const struct CheckMountedDirective: public VolumeOnlyDirective {
+ CheckMountedDirective(): VolumeOnlyDirective("check-mounted", 0, 1) {}
+ void set(ConfContext &cc) const override {
+ cc.volume->checkMounted = get_boolean(cc);
+ }
+} check_mounted_directive;
diff --git a/src/ConfDirective.h b/src/ConfDirective.h
new file mode 100644
index 0000000..8323437
--- /dev/null
+++ b/src/ConfDirective.h
@@ -0,0 +1,186 @@
+//-*-C++-*-
+// Copyright © 2011, 2012, 2014, 2015 Richard Kettlewell.
+//
+// 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#ifndef CONFDIRECTIVE_H
+#define CONFDIRECTIVE_H
+
+/** @brief Context for configuration file parsing */
+struct ConfContext {
+ /** @brief Constructor
+ *
+ * @param conf_ Root configuration node
+ */
+ ConfContext(Conf *conf_):
+ conf(conf_), context(conf_) {}
+
+ /** @brief Root of configuration */
+ Conf *conf;
+
+ /** @brief Current configuration node
+ *
+ * Could be a @ref Conf, @ref Host or @ref Volume.
+ */
+ ConfBase *context;
+
+ /** @brief Current host or null */
+ Host *host = nullptr;
+
+ /** @brief Current volume or null */
+ Volume *volume = nullptr;
+
+ /** @brief Parsed directive */
+ std::vector<std::string> bits;
+
+ /** @brief Containing filename */
+ std::string path;
+
+ /** @brief Line number */
+ int line = -1;
+};
+
+struct ConfDirective;
+
+/** @brief Type of name-to-directive map */
+typedef std::map<std::string, const ConfDirective *> directives_type;
+
+/** @brief Base class for configuration file directives */
+class ConfDirective {
+public:
+ /** @brief Constructor
+ *
+ * @param name_ Name of directive
+ * @param min_ Minimum number of arguments
+ * @param max_ Maximum number of arguments
+ *
+ * Directives are automatically registered in this constructor.
+ */
+ ConfDirective(const char *name_, int min_=0, int max_=INT_MAX);
+
+ /** @brief Name of directive */
+ const std::string name;
+
+ /** @brief Check directive syntax
+ * @param cc Context containing directive
+ *
+ * The base class implementation just checks the minimum and maximum number
+ * of arguments.
+ */
+ virtual void check(const ConfContext &cc) const;
+
+ /** @brief Get a boolean parameter
+ * @param cc Context containing directive
+ * @return @c true or @c false
+ *
+ * Use in ConfDirective::set implementations for boolean-sense directives.
+ */
+ bool get_boolean(const ConfContext &cc) const;
+
+ /** @brief Act on a directive
+ * @param cc Context containing directive
+ */
+ virtual void set(ConfContext &cc) const = 0;
+
+ /** @brief Find a directive
+ * @param name Name of directive
+ * @return Pointer to implementation or null pointer if not found
+ */
+ static const ConfDirective *find(const std::string &name);
+
+private:
+ /** @brief Minimum number of arguments */
+ int min;
+
+ /** @brief Maximum number of arguments */
+ int max;
+
+ /** @brief Map names to directives */
+ static directives_type *directives;
+};
+
+/** @brief Base class for directives that can only appear in a host context */
+class HostOnlyDirective: public ConfDirective {
+public:
+ /** @brief Constructor
+ *
+ * @param name_ Name of directive
+ * @param min_ Minimum number of arguments
+ * @param max_ Maximum number of arguments
+ * @param inheritable_ If @c true, also allowed in volume context
+ */
+ HostOnlyDirective(const char *name_, int min_=0, int max_=INT_MAX,
+ bool inheritable_ = false):
+ ConfDirective(name_, min_, max_),
+ inheritable(inheritable_) {}
+ void check(const ConfContext &cc) const override;
+
+ /** @brief If @c true, also allowed in volume context */
+ bool inheritable;
+};
+
+/** @brief Base class for directives that can only appear in a volume context */
+class VolumeOnlyDirective: public ConfDirective {
+public:
+ /** @brief Constructor
+ *
+ * @param name_ Name of directive
+ * @param min_ Minimum number of arguments
+ * @param max_ Maximum number of arguments
+ */
+ VolumeOnlyDirective(const char *name_, int min_=0, int max_=INT_MAX):
+ ConfDirective(name_, min_, max_) {}
+ void check(const ConfContext &cc) const override;
+};
+
+/** @brief Base class for color-setting directives */
+class ColorDirective: public ConfDirective {
+public:
+ /** @brief Constructor
+ * @param name Name of directive
+ */
+ ColorDirective(const char *name): ConfDirective(name, 1, 4) {}
+
+ void check(const ConfContext &cc) const override;
+
+ void set(ConfContext &cc) const override;
+
+ /** @brief Set a color
+ * @param cc Configuration context
+ * @param c Color to set
+ */
+ virtual void set(ConfContext &cc, const Color &c) const = 0;
+
+private:
+ /** @brief Parse and set an RGB color representation
+ * @param cc Configuration context
+ * @param n Index for first element
+ */
+ void set_rgb(ConfContext &cc, size_t n) const;
+
+ /** @brief Parse and set an HSV color representation
+ * @param cc Configuration context
+ * @param n Index for first element
+ */
+ void set_hsv(ConfContext &cc, size_t n) const;
+
+ /** @brief Parse and set a packed integer color representation
+ * @param cc Configuration context
+ * @param n Index for first element
+ * @param radix Radix or 0 to pick as per strtol(3)
+ */
+ void set_packed(ConfContext &cc, size_t n, int radix = 0) const;
+
+};
+
+#endif /* CONFDIRECTIVE_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 35b2279..3a51d8b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,7 +37,7 @@ toLines.cc globFiles.cc Database.h Database.cc Report.h \
parseInteger.cc split.cc EventLoop.cc EventLoop.h nonblock.cc \
Action.cc Action.h BulkRemove.h Selection.h Selection.cc Color.h \
Color.cc parseFloat.cc Render.h Render.cc HistoryGraph.h \
-HistoryGraph.cc ColorStrategy.cc
+HistoryGraph.cc ColorStrategy.cc ConfDirective.h ConfDirective.cc
rsbackup_SOURCES=rsbackup.cc PruneAge.cc PruneNever.cc PruneExec.cc \
PruneDecay.cc