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 :
20 : /** \file
21 : * \brief Advanced getopt data access implementation.
22 : *
23 : * The advgetopt class has many function used to access the data in the
24 : * class. These functions are gathered here.
25 : *
26 : * This file is covered by the following tests:
27 : *
28 : * \li options_parser
29 : * \li invalid_options_parser
30 : * \li valid_options_files
31 : * \li invalid_options_files
32 : */
33 :
34 : // self
35 : //
36 : #include "advgetopt/advgetopt.h"
37 :
38 : #include "advgetopt/conf_file.h"
39 : #include "advgetopt/exception.h"
40 :
41 :
42 : // cppthread
43 : //
44 : #include <cppthread/log.h>
45 :
46 :
47 : // snapdev
48 : //
49 : #include <snapdev/join_strings.h>
50 : #include <snapdev/tokenize_string.h>
51 :
52 :
53 : // C++
54 : //
55 : #include <list>
56 :
57 :
58 : // last include
59 : //
60 : #include <snapdev/poison.h>
61 :
62 :
63 :
64 :
65 : namespace advgetopt
66 : {
67 :
68 :
69 :
70 :
71 :
72 :
73 :
74 : /** \brief Reset all the options.
75 : *
76 : * This function goes through the list of options and mark them all as
77 : * undefined. This is useful if you want to reuse a getopt object.
78 : *
79 : * The effect is that all calls to is_defined() made afterward return false
80 : * until new arguments get parsed.
81 : */
82 2 : void getopt::reset()
83 : {
84 18 : for(auto & opt : f_options_by_name)
85 : {
86 16 : opt.second->reset();
87 : }
88 2 : }
89 :
90 :
91 : /** \brief Parse the options to option_info objects.
92 : *
93 : * This function transforms an array of options in a vector of option_info
94 : * objects.
95 : *
96 : * \param[in] opts An array of options to be parsed.
97 : * \param[in] ignore_duplicates Whether to ignore potential duplicates.
98 : */
99 482 : void getopt::parse_options_info(option const * opts, bool ignore_duplicates)
100 : {
101 482 : if(opts == nullptr)
102 : {
103 66 : return;
104 : }
105 :
106 2059 : for(
107 2059 : ; (opts->f_flags & GETOPT_FLAG_END) == 0
108 : ; ++opts)
109 : {
110 1651 : if(opts->f_name == nullptr
111 1650 : || opts->f_name[0] == '\0')
112 : {
113 2 : throw getopt_logic_error("option long name missing or empty.");
114 : }
115 1649 : short_name_t const one_char(string_to_short_name(opts->f_name));
116 1649 : if(one_char != NO_SHORT_NAME)
117 : {
118 1 : throw getopt_logic_error("a long name option must be at least 2 characters.");
119 : }
120 :
121 1648 : short_name_t short_name(opts->f_short_name);
122 :
123 1648 : option_info::pointer_t o(std::make_shared<option_info>(
124 1648 : opts->f_name
125 1648 : , short_name));
126 1647 : o->set_variables(f_variables);
127 :
128 1647 : o->set_environment_variable_name(opts->f_environment_variable_name);
129 1647 : o->add_flag(opts->f_flags);
130 1647 : o->set_default(opts->f_default);
131 1647 : o->set_help(opts->f_help);
132 1647 : o->set_multiple_separators(opts->f_multiple_separators);
133 :
134 1647 : if(opts->f_validator != nullptr)
135 : {
136 6 : o->set_validator(opts->f_validator);
137 : }
138 :
139 1651 : add_option(o, ignore_duplicates);
140 1647 : }
141 : }
142 :
143 :
144 : /** \brief Add one option to the advgetopt object.
145 : *
146 : * This function is used to dynamically add one option to the advgetopt
147 : * object.
148 : *
149 : * This is often used in a library which wants to dynamically add support
150 : * for library specific parameters to the command line.
151 : *
152 : * \note
153 : * The \p ignore_duplicates option still gets the option added if only
154 : * the short-name is a duplicate. In that case, we set the option's
155 : * short-name to NO_SHORT_NAME before adding the option to the tables.
156 : *
157 : * \param[in] opt The option to be added.
158 : * \param[in] ignore_duplicate If option is a duplicate, do not add it.
159 : */
160 1680 : void getopt::add_option(option_info::pointer_t opt, bool ignore_duplicates)
161 : {
162 1680 : if(get_option(opt->get_name(), true) != nullptr)
163 : {
164 2 : if(ignore_duplicates)
165 : {
166 1 : return;
167 : }
168 2 : throw getopt_defined_twice(
169 2 : std::string("option named \"")
170 3 : + opt->get_name()
171 5 : + "\" found twice.");
172 : }
173 :
174 1678 : short_name_t short_name(opt->get_short_name());
175 1678 : if(get_option(short_name, true) != nullptr)
176 : {
177 3 : if(ignore_duplicates)
178 : {
179 2 : short_name = NO_SHORT_NAME;
180 2 : opt->set_short_name(NO_SHORT_NAME);
181 : }
182 : else
183 : {
184 2 : throw getopt_defined_twice(
185 : "option with short name \""
186 2 : + short_name_to_string(short_name)
187 5 : + "\" found twice.");
188 : }
189 : }
190 :
191 1677 : if(opt->is_default_option())
192 : {
193 48 : if(f_default_option != nullptr)
194 : {
195 1 : throw getopt_logic_error("two default options found.");
196 : }
197 47 : if(opt->has_flag(GETOPT_FLAG_FLAG))
198 : {
199 1 : throw getopt_logic_error("a default option must accept parameters, it can't be a GETOPT_FLAG_FLAG.");
200 : }
201 :
202 46 : f_default_option = opt;
203 : }
204 :
205 1675 : f_options_by_name[opt->get_name()] = opt;
206 :
207 1675 : if(short_name != NO_SHORT_NAME)
208 : {
209 729 : f_options_by_short_name[short_name] = opt;
210 : }
211 : }
212 :
213 :
214 : /** \brief Get the path and filename to options.
215 : *
216 : * The programmer can define a path to options that the tool will load.
217 : * By default, that path is expected to be `/usr/share/advgetopt`.
218 : *
219 : * In order to allow debugging as a programmer, we also support changing
220 : * the source through an environment variable named
221 : * `ADVGETOPT_OPTIONS_FILES_DIRECTORY`. This variable is checked
222 : * first and any other path is ignored if it is defined and not just an
223 : * empty string.
224 : *
225 : * \note
226 : * If somehow you did not define a group or a project name, then the
227 : * function will return an empty string. Otherwise, this path always
228 : * exists.
229 : *
230 : * \return The path or an empty string.
231 : */
232 376 : std::string getopt::get_options_filename() const
233 : {
234 376 : std::string const filename(get_group_or_project_name());
235 376 : if(filename.empty())
236 : {
237 6 : return std::string();
238 : }
239 :
240 370 : std::string path;
241 370 : char const * const options_files_directory(getenv("ADVGETOPT_OPTIONS_FILES_DIRECTORY"));
242 370 : if(options_files_directory != nullptr
243 0 : && *options_files_directory != '\0')
244 : {
245 0 : path = options_files_directory;
246 : }
247 370 : else if(f_options_environment.f_options_files_directory != nullptr
248 91 : && f_options_environment.f_options_files_directory[0] != '\0')
249 : {
250 86 : path = f_options_environment.f_options_files_directory;
251 : }
252 : else
253 : {
254 284 : path = "/usr/share/advgetopt/options/";
255 : }
256 370 : if(path.back() != '/')
257 : {
258 86 : path += '/';
259 : }
260 :
261 740 : return path + filename + ".ini";
262 376 : }
263 :
264 :
265 : /** \brief Check for a file with option definitions.
266 : *
267 : * This function tries to read the default option file for this process.
268 : * This filename is generated using the the option environment files
269 : * directory and the project name.
270 : *
271 : * If the directory is not defined, the function uses this default path:
272 : * `"/usr/share/advgetopt/options/"`. See the other
273 : * parse_options_from_file(std::string const & filename, int min_sections, int max_sections)
274 : * function for additional details.
275 : *
276 : * \sa parse_options_from_file(std::string const & filename, int min_sections, int max_sections)
277 : */
278 357 : void getopt::parse_options_from_file()
279 : {
280 359 : parse_options_from_file(get_options_filename(), 1, 1);
281 355 : }
282 :
283 :
284 : /** \brief Check for a file with option definitions.
285 : *
286 : * This function tries to read the specified file for command line options
287 : * for this application. These are similar to the option structure, only it
288 : * is defined in a file.
289 : *
290 : * The format of the file is like so:
291 : *
292 : * \li Option names are defined on a line by themselves between square brackets.
293 : * \li Parameters of that option are defined below as a `name=<value>`.
294 : *
295 : * Example:
296 : *
297 : * \code
298 : * [<command-name>]
299 : * short_name=<character>
300 : * default=<default value>
301 : * help=<help sentence>
302 : * validator=<validator name>[(<param>[,<param>...])]|/<regex>/<flags>
303 : * alias=<name of aliased option>
304 : * allowed=command-line,environment-variable,configuration-file
305 : * show-usage-on-error
306 : * no-arguments|multiple
307 : * required
308 : * \endcode
309 : *
310 : * The number of namespaces in `<command-name>` can be limited using the
311 : * \p min_sections and \p max_sections parameters.
312 : *
313 : * The function can be called multiple times. The first time, it verifies
314 : * that there are not duplicated settings. On following loads, that test
315 : * is ignored.
316 : *
317 : * \todo
318 : * Test that options get 100% updated on a reload.
319 : *
320 : * \note
321 : * By default, this function is called with one specific filename based
322 : * on the f_project_name field and the f_options_files_directory as
323 : * defined in the options environment.
324 : *
325 : * \param[in] filename The filename to load.
326 : * \param[in] min_sections The minimum number of namespaces.
327 : * \param[in] max_sections The maximum number of namespaces.
328 : * \param[in] ignore_duplicates Whether duplicates are okay or not.
329 : *
330 : * \sa parse_options_from_file()
331 : */
332 359 : void getopt::parse_options_from_file(
333 : std::string const & filename
334 : , int min_sections
335 : , int max_sections
336 : , bool ignore_duplicates)
337 : {
338 359 : if(filename.empty())
339 : {
340 6 : return;
341 : }
342 :
343 353 : section_operator_t operators(SECTION_OPERATOR_INI_FILE);
344 353 : if(min_sections == 1
345 351 : && max_sections == 1)
346 : {
347 351 : operators |= SECTION_OPERATOR_ONE_SECTION;
348 : }
349 :
350 353 : conf_file_setup conf_setup(
351 : filename
352 : , line_continuation_t::line_continuation_unix
353 : , ASSIGNMENT_OPERATOR_EQUAL
354 : , COMMENT_INI | COMMENT_SHELL
355 353 : , operators);
356 353 : if(!conf_setup.is_valid())
357 : {
358 : return; // LCOV_EXCL_LINE
359 : }
360 :
361 : // if the file includes a section named after the group or project
362 : // we can remove it completely (this helps with sharing fluid settings)
363 : //
364 353 : std::string const section_to_ignore(get_group_or_project_name());
365 353 : conf_setup.set_section_to_ignore(section_to_ignore);
366 :
367 353 : conf_file::pointer_t conf(conf_file::get_conf_file(conf_setup));
368 353 : conf_file::sections_t const & sections(conf->get_sections());
369 388 : for(auto & section_names : sections)
370 : {
371 37 : string_list_t names;
372 74 : split_string(section_names, names, {"::"});
373 37 : std::string option_name;
374 51 : if(names.size() > 1
375 51 : && *names.begin() == section_to_ignore)
376 : {
377 0 : names.erase(names.begin());
378 0 : option_name = snapdev::join_strings(names, "::");
379 : }
380 : else
381 : {
382 37 : option_name = section_names;
383 : }
384 :
385 37 : if(names.size() < static_cast<std::size_t>(min_sections)
386 37 : || names.size() > static_cast<std::size_t>(max_sections))
387 : {
388 2 : if(min_sections == 1
389 : && max_sections == 1) // LCOV_EXCL_LINE
390 : {
391 : // right now this case cannot happen because we set the
392 : // SECTION_OPERATOR_ONE_SECTION flag so errors are caught
393 : // directly inside the conf_file::get_conf_file() call
394 : //
395 : cppthread::log << cppthread::log_level_t::error // LCOV_EXCL_LINE
396 : << "the name of a settings definition must include one namespace; \"" // LCOV_EXCL_LINE
397 : << section_names // LCOV_EXCL_LINE
398 : << "\" is not considered valid." // LCOV_EXCL_LINE
399 : << cppthread::end; // LCOV_EXCL_LINE
400 : }
401 : else
402 : {
403 4 : cppthread::log << cppthread::log_level_t::error
404 2 : << "the name of a settings definition must include between "
405 2 : << min_sections
406 2 : << " and "
407 2 : << max_sections
408 2 : << " namespaces; \""
409 2 : << section_names
410 2 : << "\" is not considered valid."
411 4 : << cppthread::end;
412 : }
413 2 : continue;
414 : }
415 :
416 35 : std::string const parameter_name(option_name);
417 175 : std::string const short_name(unquote(conf->get_parameter(parameter_name + "::shortname")));
418 35 : if(short_name.length() > 1)
419 : {
420 2 : throw getopt_logic_error(
421 : "option \""
422 2 : + section_names
423 3 : + "\" has an invalid short name in \""
424 3 : + filename
425 5 : + "\", it can't be more than one character.");
426 : }
427 34 : short_name_t const sn(short_name.length() == 1
428 34 : ? short_name[0]
429 34 : : NO_SHORT_NAME);
430 :
431 34 : option_info::pointer_t opt(std::make_shared<option_info>(parameter_name, sn));
432 34 : opt->set_variables(f_variables);
433 :
434 34 : std::string const environment_variable_name(parameter_name + "::environment_variable_name");
435 34 : if(conf->has_parameter(environment_variable_name))
436 : {
437 8 : opt->set_environment_variable_name(unquote(conf->get_parameter(environment_variable_name)));
438 : }
439 :
440 34 : std::string const default_name(parameter_name + "::default");
441 34 : if(conf->has_parameter(default_name))
442 : {
443 17 : opt->set_default(unquote(conf->get_parameter(default_name)));
444 : }
445 :
446 34 : opt->set_help(unquote(conf->get_parameter(parameter_name + "::help")));
447 :
448 68 : std::string const validator_name_and_params(conf->get_parameter(parameter_name + "::validator"));
449 34 : opt->set_validator(validator_name_and_params);
450 :
451 34 : std::string const alias_name(parameter_name + "::alias");
452 34 : if(conf->has_parameter(alias_name))
453 : {
454 8 : if(!opt->get_help().empty())
455 : {
456 2 : throw getopt_logic_error(
457 : "option \""
458 2 : + section_names
459 3 : + "\" is an alias and as such it can't include a help=... parameter in \""
460 3 : + filename
461 5 : + "\".");
462 : }
463 7 : opt->set_help(unquote(conf->get_parameter(alias_name)));
464 7 : opt->add_flag(GETOPT_FLAG_ALIAS);
465 : }
466 :
467 33 : std::string const allowed_name(parameter_name + "::allowed");
468 33 : if(conf->has_parameter(allowed_name))
469 : {
470 66 : std::string const allowed_list(conf->get_parameter(allowed_name));
471 33 : string_list_t allowed;
472 66 : split_string(allowed_list, allowed, {","});
473 92 : for(auto const & a : allowed)
474 : {
475 59 : if(a == "command-line")
476 : {
477 24 : opt->add_flag(GETOPT_FLAG_COMMAND_LINE);
478 : }
479 35 : else if(a == "environment-variable")
480 : {
481 22 : opt->add_flag(GETOPT_FLAG_ENVIRONMENT_VARIABLE);
482 : }
483 13 : else if(a == "configuration-file")
484 : {
485 13 : opt->add_flag(GETOPT_FLAG_CONFIGURATION_FILE);
486 : }
487 0 : else if(a == "dynamic-configuration")
488 : {
489 0 : opt->add_flag(GETOPT_FLAG_DYNAMIC_CONFIGURATION);
490 : }
491 : }
492 33 : }
493 :
494 33 : std::string const group_name(parameter_name + "::group");
495 33 : if(conf->has_parameter(group_name))
496 : {
497 0 : std::string const group(conf->get_parameter(group_name));
498 0 : if(group == "commands")
499 : {
500 0 : opt->add_flag(GETOPT_FLAG_GROUP_COMMANDS);
501 : }
502 0 : else if(group == "options")
503 : {
504 0 : opt->add_flag(GETOPT_FLAG_GROUP_OPTIONS);
505 : }
506 0 : else if(group == "three")
507 : {
508 0 : opt->add_flag(GETOPT_FLAG_GROUP_THREE);
509 : }
510 0 : else if(group == "four")
511 : {
512 0 : opt->add_flag(GETOPT_FLAG_GROUP_FOUR);
513 : }
514 0 : else if(group == "five")
515 : {
516 0 : opt->add_flag(GETOPT_FLAG_GROUP_FIVE);
517 : }
518 0 : else if(group == "six")
519 : {
520 0 : opt->add_flag(GETOPT_FLAG_GROUP_SIX);
521 : }
522 0 : else if(group == "seven")
523 : {
524 0 : opt->add_flag(GETOPT_FLAG_GROUP_SEVEN);
525 : }
526 0 : }
527 :
528 33 : if(conf->has_parameter(parameter_name + "::show-usage-on-error"))
529 : {
530 4 : opt->add_flag(GETOPT_FLAG_SHOW_USAGE_ON_ERROR);
531 : }
532 :
533 33 : if(conf->has_parameter(parameter_name + "::no-arguments"))
534 : {
535 8 : opt->add_flag(GETOPT_FLAG_FLAG);
536 : }
537 :
538 33 : if(conf->has_parameter(parameter_name + "::multiple"))
539 : {
540 6 : opt->add_flag(GETOPT_FLAG_MULTIPLE);
541 : }
542 :
543 33 : if(conf->has_parameter(parameter_name + "::required"))
544 : {
545 17 : opt->add_flag(GETOPT_FLAG_REQUIRED);
546 : }
547 :
548 33 : add_option(opt, ignore_duplicates);
549 50 : }
550 359 : }
551 :
552 :
553 : /** \brief Link options marked as a GETOPT_FLAG_ALIAS.
554 : *
555 : * After we defined all the options, go through the list again to find
556 : * aliases and link them with their corresponding alias option.
557 : *
558 : * \exception getopt_exception_invalid
559 : * All aliases must exist or this exception is raised.
560 : */
561 275 : void getopt::link_aliases()
562 : {
563 1603 : for(auto & c : f_options_by_name)
564 : {
565 1332 : if(c.second->has_flag(GETOPT_FLAG_ALIAS))
566 : {
567 23 : std::string const & alias_name(c.second->get_help());
568 23 : if(alias_name.empty())
569 : {
570 4 : throw getopt_logic_error(
571 : "the default value of your alias cannot be an empty string for \""
572 4 : + c.first
573 10 : + "\".");
574 : }
575 :
576 : // we have to use the `true` flag in this get_option() because
577 : // aliases may not yet be defined
578 : //
579 21 : option_info::pointer_t alias(get_option(alias_name, true));
580 21 : if(alias == nullptr)
581 : {
582 2 : throw getopt_logic_error(
583 : "no option named \""
584 2 : + alias_name
585 3 : + "\" to satisfy the alias of \""
586 3 : + c.first
587 5 : + "\".");
588 : }
589 :
590 20 : flag_t const expected_flags(c.second->get_flags() & ~GETOPT_FLAG_ALIAS);
591 20 : if(alias->get_flags() != expected_flags)
592 : {
593 1 : std::stringstream ss;
594 1 : ss << std::hex
595 : << "the flags of alias \""
596 1 : << c.first
597 1 : << "\" (0x"
598 1 : << expected_flags
599 : << ") are different than the flags of \""
600 : << alias_name
601 1 : << "\" (0x"
602 1 : << alias->get_flags()
603 1 : << ").";
604 1 : throw getopt_logic_error(ss.str());
605 1 : }
606 :
607 19 : c.second->set_alias_destination(alias);
608 21 : }
609 : }
610 271 : }
611 :
612 :
613 : /** \brief Assign a short name to an option.
614 : *
615 : * This function allows for dynamically assigning a short name to an option.
616 : * This is useful for cases where a certain number of options may be added
617 : * dynamically and may share the same short name or similar situation.
618 : *
619 : * On our end we like to add `-c` as the short name of the `--config-dir`
620 : * command line or environment variable option. However, some of our tools
621 : * use `-c` for other reason (i.e. our `cxpath` tool uses `-c` for its
622 : * `--compile` option.) So we do not want to have it as a default in
623 : * `--config-dir`. Instead we assign it afterward if possible.
624 : *
625 : * **IMPORTANT:** It is possible to change the short-name at any time.
626 : * However, note that you can't have duplicates. It is also possible
627 : * to remove a short-name by setting it to the advgetopt::NO_SHORT_NAME
628 : * special value.
629 : *
630 : * \note
631 : * This function requires you to make use of the constructor without the
632 : * `argc` and `argv` parameters, add the short name, then run all the
633 : * parsing.
634 : *
635 : * \exception getopt_exception_logic
636 : * The same short name cannot be used more than once. This exception is
637 : * raised if it is discovered that another option already makes use of
638 : * this short name. This exception is also raised if \p name does not
639 : * reference an existing option.
640 : *
641 : * \param[in] name The name of the option which is to receive a short name.
642 : * \param[in] short_name The short name to assigned to the \p name option.
643 : */
644 9 : void getopt::set_short_name(std::string const & name, short_name_t short_name)
645 : {
646 9 : auto opt(f_options_by_name.find(name));
647 9 : if(opt == f_options_by_name.end())
648 : {
649 2 : throw getopt_logic_error(
650 : "option with name \""
651 2 : + name
652 5 : + "\" not found.");
653 : }
654 :
655 8 : if(short_name != NO_SHORT_NAME)
656 : {
657 6 : auto it(f_options_by_short_name.find(short_name));
658 6 : if(it != f_options_by_short_name.end())
659 : {
660 2 : if(it->second == opt->second)
661 : {
662 : // same option, already named 'short_name'
663 : //
664 1 : return;
665 : }
666 :
667 2 : throw getopt_logic_error(
668 : "found another option (\""
669 2 : + it->second->get_name()
670 3 : + "\") with short name '"
671 4 : + short_name_to_string(short_name)
672 5 : + "'.");
673 : }
674 : }
675 :
676 6 : short_name_t const old_short_name(opt->second->get_short_name());
677 6 : if(old_short_name != NO_SHORT_NAME)
678 : {
679 2 : auto it(f_options_by_short_name.find(old_short_name));
680 2 : if(it != f_options_by_short_name.end())
681 : {
682 2 : f_options_by_short_name.erase(it);
683 : }
684 : }
685 :
686 6 : opt->second->set_short_name(short_name);
687 :
688 6 : if(short_name != NO_SHORT_NAME)
689 : {
690 4 : f_options_by_short_name[short_name] = opt->second;
691 : }
692 : }
693 :
694 :
695 : /** \brief Output the source of each option.
696 : *
697 : * This function goes through the list of options by name ("alphabetically")
698 : * and prints out the sources or "(undefined)" if not defined anywhere.
699 : *
700 : * This function gets called when using the `--show-option-sources`
701 : * system command line option at the time the process_system_options()
702 : * function gets called.
703 : *
704 : * \param[in] out The output streaming where the info is written.
705 : */
706 3 : void getopt::show_option_sources(std::basic_ostream<char> & out)
707 : {
708 3 : int idx(1);
709 3 : out << "Option Sources:\n";
710 68 : for(auto const & opt : f_options_by_name)
711 : {
712 65 : out << " " << idx << ". option \"" << opt.second->get_name() << "\"";
713 65 : string_list_t sources(opt.second->trace_sources());
714 65 : if(sources.empty())
715 : {
716 33 : out << " (undefined)\n";
717 : }
718 : else
719 : {
720 32 : out << "\n";
721 84 : for(auto const & src : sources)
722 : {
723 52 : out << " " << src << "\n";
724 : }
725 : }
726 65 : out << "\n";
727 :
728 65 : ++idx;
729 65 : }
730 3 : out << std::flush;
731 3 : }
732 :
733 :
734 :
735 : } // namespace advgetopt
736 : // vim: ts=4 sw=4 et
|