Line data Source code
1 : // Copyright (c) 2006-2024 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/advgetopt
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : #pragma once
20 :
21 : /** \file
22 : * \brief Definitions of the options class a initialization functions.
23 : *
24 : * The advgetopt library offers an advanced way to manage your command
25 : * line tools options on the command line, in environment variables, and
26 : * in configuration files.
27 : */
28 :
29 :
30 : // self
31 : //
32 : #include <advgetopt/option_info.h>
33 :
34 : #include <advgetopt/conf_file.h>
35 :
36 :
37 : // snapdev
38 : //
39 : #include <snapdev/not_used.h>
40 :
41 :
42 :
43 : namespace advgetopt
44 : {
45 :
46 :
47 :
48 :
49 :
50 :
51 : // this structure is used to declare your command line options in a
52 : // constexpr array
53 : //
54 : // TODO: plan to transform all the strings in an array with a scheme such
55 : // as:
56 : //
57 : // "a:<alias>",
58 : // "d:<default>",
59 : // "h:<help>",
60 : // "n:<name>",
61 : // "s:<separator>",
62 : // "v:<validator>(<param>, ...)"
63 : //
64 : // our templates should be able to create that array automatically.
65 : // This way we avoid many nullptr in so many definitions (i.e. most of
66 : // our definitions do not have a default, separators, or a validator)
67 : // We would also avoid the alias/help overload.
68 : //
69 : struct option
70 : {
71 : short_name_t f_short_name = NO_SHORT_NAME; // letter option (or NO_SHORT_NAME == U'\0')
72 : flag_t f_flags = GETOPT_FLAG_NONE; // set of flags
73 : char const * f_name = nullptr; // name of the option (i.e. "test" for --test, or nullptr)
74 : char const * f_environment_variable_name = nullptr; // name of an environment variable to read for the value from
75 : char const * f_default = nullptr; // a default value if not nullptr
76 : char const * f_help = nullptr; // help for this option, if nullptr it's a hidden option; if ALIAS then this is the actual alias
77 : char const * f_validator = nullptr; // the name of a validator and optional parameters between parenthesis
78 : char const * const *f_multiple_separators = nullptr; // nullptr terminated list of strings used as separators when GETOPT_FLAG_MULTIPLE is set
79 : };
80 :
81 :
82 :
83 : template<typename T>
84 : class OptionValue
85 : {
86 : public:
87 : typedef T value_t;
88 :
89 360 : constexpr OptionValue<T>(T const v)
90 360 : : f_value(v)
91 : {
92 360 : }
93 :
94 264 : constexpr value_t get() const
95 : {
96 264 : return f_value;
97 : }
98 :
99 : private:
100 : value_t f_value;
101 : };
102 :
103 :
104 : class ShortName
105 : : public OptionValue<short_name_t>
106 : {
107 : public:
108 24 : constexpr ShortName()
109 24 : : OptionValue<short_name_t>(NO_SHORT_NAME)
110 : {
111 24 : }
112 :
113 17 : constexpr ShortName(short_name_t name)
114 17 : : OptionValue<short_name_t>(name)
115 : {
116 17 : }
117 : };
118 :
119 : class Flags
120 : : public OptionValue<flag_t>
121 : {
122 : public:
123 24 : constexpr Flags()
124 24 : : OptionValue<flag_t>(GETOPT_FLAG_NONE)
125 : {
126 24 : }
127 :
128 24 : constexpr Flags(flag_t flags)
129 24 : : OptionValue<flag_t>(flags)
130 : {
131 24 : }
132 : };
133 :
134 : class Name
135 : : public OptionValue<char const *>
136 : {
137 : public:
138 : constexpr Name()
139 : : OptionValue<char const *>(nullptr)
140 : {
141 : }
142 :
143 24 : constexpr Name(char const * name)
144 24 : : OptionValue<char const *>(name)
145 : {
146 24 : }
147 : };
148 :
149 : class EnvironmentVariableName
150 : : public OptionValue<char const *>
151 : {
152 : public:
153 48 : constexpr EnvironmentVariableName()
154 48 : : OptionValue<char const *>(nullptr)
155 : {
156 48 : }
157 :
158 : constexpr EnvironmentVariableName(char const * name)
159 : : OptionValue<char const *>(name)
160 : {
161 : }
162 : };
163 :
164 : class DefaultValue
165 : : public OptionValue<char const *>
166 : {
167 : public:
168 24 : constexpr DefaultValue()
169 24 : : OptionValue<char const *>(nullptr)
170 : {
171 24 : }
172 :
173 15 : constexpr DefaultValue(char const * default_value)
174 15 : : OptionValue<char const *>(default_value)
175 : {
176 15 : }
177 : };
178 :
179 : class Alias
180 : : public OptionValue<char const *>
181 : {
182 : public:
183 48 : constexpr Alias()
184 48 : : OptionValue<char const *>(nullptr)
185 : {
186 48 : }
187 :
188 : constexpr Alias(char const * alias)
189 : : OptionValue<char const *>(alias)
190 : {
191 : }
192 : };
193 :
194 : class Help
195 : : public OptionValue<char const *>
196 : {
197 : public:
198 24 : constexpr Help()
199 24 : : OptionValue<char const *>(nullptr)
200 : {
201 24 : }
202 :
203 24 : constexpr Help(char const * help)
204 24 : : OptionValue<char const *>(help)
205 : {
206 24 : }
207 : };
208 :
209 : class Validator
210 : : public OptionValue<char const *>
211 : {
212 : public:
213 24 : constexpr Validator()
214 24 : : OptionValue<char const *>(nullptr)
215 : {
216 24 : }
217 :
218 : constexpr Validator(char const * validator)
219 : : OptionValue<char const *>(validator)
220 : {
221 : }
222 : };
223 :
224 : class Separators
225 : : public OptionValue<char const * const *>
226 : {
227 : public:
228 24 : constexpr Separators()
229 24 : : OptionValue<char const * const *>(nullptr)
230 : {
231 24 : }
232 :
233 16 : constexpr Separators(char const * const * separators)
234 16 : : OptionValue<char const * const *>(separators)
235 : {
236 16 : }
237 : };
238 :
239 :
240 :
241 : template<typename T, typename F, class ...ARGS>
242 264 : constexpr typename std::enable_if<std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
243 : {
244 264 : snapdev::NOT_USED(args...);
245 264 : return first.get();
246 : }
247 :
248 :
249 : template<typename T, typename F, class ...ARGS>
250 960 : constexpr typename std::enable_if<!std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
251 : {
252 960 : snapdev::NOT_USED(first);
253 960 : return find_option<T>(args...);
254 : }
255 :
256 :
257 :
258 : template<class ...ARGS>
259 24 : constexpr option define_option(ARGS ...args)
260 : {
261 : #pragma GCC diagnostic push
262 : #pragma GCC diagnostic ignored "-Wpedantic"
263 120 : option opt =
264 : {
265 24 : .f_short_name = find_option<ShortName >(args..., ShortName()),
266 48 : .f_flags = find_option<Flags >(args..., Flags())
267 48 : | (find_option<Alias>(args..., Alias()) != nullptr
268 48 : ? GETOPT_FLAG_ALIAS
269 : : GETOPT_FLAG_NONE)
270 48 : | (find_option<EnvironmentVariableName>(args..., EnvironmentVariableName()) != nullptr
271 48 : ? GETOPT_FLAG_ENVIRONMENT_VARIABLE
272 : : GETOPT_FLAG_NONE),
273 24 : .f_name = find_option<Name >(args...), // no default, must be defined
274 : .f_environment_variable_name =
275 24 : find_option<EnvironmentVariableName>(args..., EnvironmentVariableName()),
276 24 : .f_default = find_option<DefaultValue>(args..., DefaultValue()),
277 48 : .f_help = find_option<Alias >(args..., Alias()) != nullptr
278 72 : ? find_option<Alias >(args..., Alias())
279 24 : : find_option<Help >(args..., Help()),
280 24 : .f_validator = find_option<Validator >(args..., Validator()),
281 24 : .f_multiple_separators = find_option<Separators >(args..., Separators()),
282 : };
283 : #pragma GCC diagnostic pop
284 :
285 : // TODO: once possible (C++17/20?) add verification tests here
286 :
287 24 : return opt;
288 : }
289 :
290 :
291 :
292 :
293 :
294 : constexpr option end_options()
295 : {
296 : return define_option(
297 : advgetopt::Name(nullptr)
298 : , advgetopt::Flags(advgetopt::end_flags())
299 : );
300 : }
301 :
302 :
303 :
304 :
305 :
306 :
307 :
308 :
309 : struct group_description
310 : {
311 : flag_t f_group = GETOPT_FLAG_GROUP_NONE; // the default is used to mark the end of the list
312 : char const * f_name = nullptr; // for --<name>-help
313 : char const * f_description = nullptr; // for usage() output
314 : };
315 :
316 :
317 :
318 :
319 : template<typename T>
320 : class GroupValue
321 : {
322 : public:
323 : typedef T value_t;
324 :
325 : constexpr GroupValue<T>(T const v)
326 : : f_value(v)
327 : {
328 : }
329 :
330 : constexpr value_t get() const
331 : {
332 : return f_value;
333 : }
334 :
335 : private:
336 : value_t f_value;
337 : };
338 :
339 : class GroupNumber
340 : : public OptionValue<flag_t>
341 : {
342 : public:
343 : constexpr GroupNumber()
344 : : OptionValue<flag_t>(GETOPT_FLAG_GROUP_NONE)
345 : {
346 : }
347 :
348 : constexpr GroupNumber(flag_t group)
349 : : OptionValue<flag_t>(group)
350 : {
351 : }
352 : };
353 :
354 : class GroupName
355 : : public GroupValue<char const *>
356 : {
357 : public:
358 : constexpr GroupName()
359 : : GroupValue<char const *>(nullptr)
360 : {
361 : }
362 :
363 : constexpr GroupName(char const * name)
364 : : GroupValue<char const *>(name)
365 : {
366 : }
367 : };
368 :
369 : class GroupDescription
370 : : public GroupValue<char const *>
371 : {
372 : public:
373 : constexpr GroupDescription()
374 : : GroupValue<char const *>(nullptr)
375 : {
376 : }
377 :
378 : constexpr GroupDescription(char const * description)
379 : : GroupValue<char const *>(description)
380 : {
381 : }
382 : };
383 :
384 :
385 : template<typename T, typename F, class ...ARGS>
386 : constexpr typename std::enable_if<std::is_same<T, F>::value, typename T::value_t>::type find_group(F first, ARGS ...args)
387 : {
388 : snapdev::NOT_USED(args...);
389 : return first.get();
390 : }
391 :
392 :
393 : template<typename T, typename F, class ...ARGS>
394 : constexpr typename std::enable_if<!std::is_same<T, F>::value, typename T::value_t>::type find_group(F first, ARGS ...args)
395 : {
396 : snapdev::NOT_USED(first);
397 : return find_group<T>(args...);
398 : }
399 :
400 :
401 :
402 : template<class ...ARGS>
403 : constexpr group_description define_group(ARGS ...args)
404 : {
405 : #pragma GCC diagnostic push
406 : #pragma GCC diagnostic ignored "-Wpedantic"
407 : group_description grp =
408 : {
409 : .f_group = find_group<GroupNumber >(args..., GroupNumber()),
410 : .f_name = find_group<GroupName >(args..., GroupName()),
411 : .f_description = find_group<GroupDescription>(args..., GroupDescription()),
412 : };
413 : #pragma GCC diagnostic pop
414 :
415 : return grp;
416 : }
417 :
418 :
419 : constexpr group_description end_groups()
420 : {
421 : // the defaults are what we expect to end the list of groups
422 : return define_group();
423 : }
424 :
425 :
426 :
427 :
428 :
429 :
430 :
431 :
432 : constexpr flag_t GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS = 0x0001; // accept parameters that are not declared
433 :
434 : constexpr flag_t GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS = 0x0002; // add system parameters (i.e. --help, --version, etc.)
435 : constexpr flag_t GETOPT_ENVIRONMENT_FLAG_PROCESS_SYSTEM_PARAMETERS = 0x0004; // add & process system parameters
436 : constexpr flag_t GETOPT_ENVIRONMENT_FLAG_DEBUG_SOURCE = 0x0008; // debug source for each option
437 : constexpr flag_t GETOPT_ENVIRONMENT_FLAG_AUTO_DONE = 0x0010; // if you want a valid getopt structure without parsing arguments, set this flag
438 :
439 :
440 : struct options_environment
441 : {
442 : char const * f_project_name = nullptr; // project/application name--used as filename for the .conf files (%a)
443 : char const * f_group_name = nullptr; // sub-folder name (i.e. "snapwebsites")--if nullptr, use f_project_name
444 : option const * f_options = nullptr; // raw options
445 : char const * f_options_files_directory = nullptr; // directory to check for option files (default "/usr/shared/advgetopt")
446 : char const * f_environment_variable_name = nullptr; // environment variable with additional options (%e)
447 : char const * f_environment_variable_intro = nullptr; // introducer for option specific environment variable names (%E)
448 : char const * f_section_variables_name = nullptr; // the name of a section representing variables (%m)
449 : char const * const * f_configuration_files = nullptr; // nullptr terminated array of full paths to configuration files (%f)
450 : char const * f_configuration_filename = nullptr; // the configuration filename to search in f_configuration_directories (%g)
451 : char const * const * f_configuration_directories = nullptr; // nullptr terminated array of paths only to configuration files (%d)
452 : flag_t f_environment_flags = 0; // GETOPT_ENVIRONMENT_FLAG_...
453 : char const * f_help_header = nullptr; // show on --help
454 : char const * f_help_footer = nullptr; // show on --help
455 : char const * f_version = nullptr; // show on --version and %v
456 : char const * f_license = nullptr; // show on --license and %l
457 : char const * f_copyright = nullptr; // show on --copyright and %c
458 : char const * f_build_date = UTC_BUILD_DATE; // available to parameter %b
459 : char const * f_build_time = UTC_BUILD_TIME; // available to parameter %t
460 : group_description const * f_groups = nullptr; // nullptr terminated array of group names %s
461 : conf_file_setup const * f_config_setup = nullptr; // default configuration file setup (except the filename)
462 : };
463 :
464 :
465 :
466 :
467 :
468 : } // namespace advgetopt
469 : // vim: ts=4 sw=4 et
|