summaryrefslogtreecommitdiff
path: root/src/ConfDirective.h
blob: b951ac4e175fa5ea418290e75bc594156e8539bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
//-*-C++-*-
// Copyright © 2011, 2012, 2014-17 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
/** @file ConfDirective.h
 * @brief Configuration file parser support
 *
 * This file contains classes used during configuration parsing, including the
 * classes that define directives (@ref ConfDirective).
 */

/** @brief Bit indicating the top level of the configuration file */
#define LEVEL_TOP 1

/** @brief Bit indicating the host level of the configuration file */
#define LEVEL_HOST 2

/** @brief Bit indicating the volume level of the configuration file */
#define LEVEL_VOLUME 4

/** @brief Context for configuration file parsing
 *
 * This class captures the state for a configuration file that we are reading.
 * If it includes further configuration files, there will be further
 * ConfContext objects.
 */
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;
};

class ConfDirective;

/** @brief Type of name-to-directive map */
typedef std::map<std::string, const ConfDirective *> directives_type;

/** @brief Base class for configuration file directives
 *
 * Each configuration file directive has a corresponding subclass and a single
 * instance.
 */
class ConfDirective {
public:
  /** @brief Constructor
   *
   * @param name_ Name of directive
   * @param min_ Minimum number of arguments
   * @param max_ Maximum number of arguments
   * @param acceptable_levels_ Acceptable indent levels
   * @param new_level_ New indent level introduced by this directive
   *
   * Directives are automatically registered in this constructor.
   *
   * The indent levels can contain @ref LEVEL_TOP, @ref LEVEL_HOST and @ref
   * LEVEL_VOLUME.
   */
  ConfDirective(const char *name_, int min_ = 0, int max_ = INT_MAX,
                unsigned acceptable_levels_ = LEVEL_TOP,
                unsigned new_level_ = 0);

  /** @brief Add an alias */
  void alias(const char *name_);

  /** @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 Set or extend a vector directive
   * @param cc Context containing directive
   * @param conf Configuration value to update
   */
  void extend(const ConfContext &cc, std::vector<std::string> &conf) 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);

  /** @brief Acceptable indent levels */
  const unsigned acceptable_levels;

  /** @brief Newly introduced level */
  const unsigned new_level;

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 Deprecated aliases */
  static std::set<std::string> *aliases;
};

/** @brief Base class for generally inheritable directives */
class InheritableDirective: public ConfDirective {
public:
  /** @brief Constructor
   *
   * @param name_ Name of directive
   * @param min_ Minimum number of arguments
   * @param max_ Maximum number of arguments
   * @param acceptable_levels_ Acceptable indent levels
   *
   * The indent level can contain @ref LEVEL_TOP, @ref LEVEL_HOST and @ref
   * LEVEL_VOLUME.
   */
  InheritableDirective(const char *name_, int min_ = 0, int max_ = INT_MAX,
                       unsigned acceptable_levels_ = LEVEL_TOP | LEVEL_HOST
                                                     | LEVEL_VOLUME):
      ConfDirective(name_, min_, max_, acceptable_levels_) {}
};

/** @brief Base class for directives that can only appear in host or host/volume
 * 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 acceptable_levels_ Acceptable indent levels
   * @param new_level_ New indent level introduced by this directive
   *
   * The indent levels can contain @ref LEVEL_TOP, @ref LEVEL_HOST and @ref
   * LEVEL_VOLUME.
   */
  HostOnlyDirective(const char *name_, int min_ = 0, int max_ = INT_MAX,
                    unsigned acceptable_levels_ = LEVEL_HOST,
                    unsigned new_level_ = 0):
      ConfDirective(name_, min_, max_, acceptable_levels_, new_level_) {}
  void check(const ConfContext &cc) const override;
};

/** @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_, LEVEL_VOLUME) {}
  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 */