diff options
author | Richard Kettlewell <rjk@greenend.org.uk> | 2015-12-19 18:58:32 +0000 |
---|---|---|
committer | Richard Kettlewell <rjk@greenend.org.uk> | 2015-12-19 18:58:32 +0000 |
commit | f575b72b8ad7980304c32ed62e2f4b317cc3862b (patch) | |
tree | 73315ae2f77ae369dfb7d742abfc2864a63238dd | |
parent | 931506ede6608313f3be43e11dfd24883044b455 (diff) |
Split up Conf.cc a bit
It remains the two largest files...
-rw-r--r-- | src/Conf.cc | 697 | ||||
-rw-r--r-- | src/ConfDirective.cc | 588 | ||||
-rw-r--r-- | src/ConfDirective.h | 186 | ||||
-rw-r--r-- | src/Makefile.am | 2 |
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 |