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 :
27 : // self
28 : //
29 : #include "advgetopt/advgetopt.h"
30 :
31 : #include "advgetopt/conf_file.h"
32 : #include "advgetopt/exception.h"
33 :
34 :
35 : // cppthread
36 : //
37 : #include <cppthread/log.h>
38 :
39 :
40 : // C
41 : //
42 : #include <string.h>
43 : #include <unistd.h>
44 :
45 :
46 : // last include
47 : //
48 : #include <snapdev/poison.h>
49 :
50 :
51 :
52 : namespace advgetopt
53 : {
54 :
55 :
56 :
57 :
58 : /** \brief Generate a list of configuration filenames.
59 : *
60 : * This function goes through the list of filenames and directories and
61 : * generates a complete list of all the configuration files that the
62 : * system will load when you call the parse_configuration_files()
63 : * function.
64 : *
65 : * Set the flag \p exists to true if you only want the name of files
66 : * that currently exists.
67 : *
68 : * The \p writable file means that we only want files under the
69 : * \<project-name>.d folder and the user configuration folder.
70 : *
71 : * \note
72 : * The argc/argv and environment variable parameters are used whenever the
73 : * function is called early and we can't call is_defined(). These are
74 : * ignored otherwise.
75 : *
76 : * \param[in] exists Remove files that do not exist from the list.
77 : * \param[in] writable Only return files we consider writable.
78 : * \param[in] argc The number of arguments in argv.
79 : * \param[in] argv The arguments passed to the finish_parsing() function or
80 : * nullptr.
81 : * \param[in] environment_variable The environment variable or an empty string.
82 : *
83 : * \return The list of configuration filenames.
84 : */
85 345 : string_list_t getopt::get_configuration_filenames(
86 : bool exists
87 : , bool writable
88 : , int argc
89 : , char * argv[]) const
90 : {
91 345 : string_list_t result;
92 :
93 345 : get_managed_configuration_filenames(result, writable, argc, argv);
94 345 : get_direct_configuration_filenames(result, writable);
95 :
96 345 : if(!exists)
97 : {
98 297 : return result;
99 : }
100 :
101 48 : string_list_t existing_files;
102 48 : int const mode(R_OK | (writable ? W_OK : 0));
103 512 : for(auto r : result)
104 : {
105 464 : if(access(r.c_str(), mode) == 0)
106 : {
107 26 : existing_files.push_back(r);
108 : }
109 464 : }
110 :
111 48 : return existing_files;
112 345 : }
113 :
114 :
115 : /** \brief Add one configuration filename to our list.
116 : *
117 : * This function adds the specified \p add name to the \p names list unless
118 : * already present in the list.
119 : *
120 : * Several of the functions computing configuration filenames can end up
121 : * attempting to add the same filename multiple times. This function
122 : * prevents the duplication. This also means the order may be slightly
123 : * different than expected (i.e. the filenames don't get reordered when
124 : * a duplicate is found).
125 : *
126 : * \param[in,out] names The list of configuration names.
127 : * \param[in] add The new configuration filename to add.
128 : */
129 1321 : void getopt::add_configuration_filename(string_list_t & names, std::string const & add)
130 : {
131 1321 : if(std::find(names.begin(), names.end(), add) == names.end())
132 : {
133 1318 : names.push_back(add);
134 : }
135 1321 : }
136 :
137 :
138 : /** \brief Generate the list of managed configuration filenames.
139 : *
140 : * As the programmer, you can define a configuration filename and a set
141 : * of directory names. This function uses that information to generate
142 : * a list of full configuration filenames that is then used to load
143 : * those configurations.
144 : *
145 : * If a filename is defined, but no directories, the this function
146 : * defines three default paths like so:
147 : *
148 : * \li `/usr/share/advgetopt/options/\<name>`
149 : * \li `/usr/share/\<name>/options`
150 : * \li `/etc/\<name>`
151 : *
152 : * \param[in,out] names The list of names are added to this list.
153 : * \param[in] writable Whether the destination has to be writable.
154 : * \param[in] argc The number of arguments in argv.
155 : * \param[in] argv The command line arguments.
156 : */
157 345 : void getopt::get_managed_configuration_filenames(
158 : string_list_t & names
159 : , bool writable
160 : , int argc
161 : , char * argv[]) const
162 : {
163 345 : if(f_options_environment.f_configuration_filename == nullptr
164 142 : || *f_options_environment.f_configuration_filename == '\0')
165 : {
166 204 : return;
167 : }
168 :
169 141 : string_list_t directories;
170 141 : if(has_flag(GETOPT_ENVIRONMENT_FLAG_SYSTEM_PARAMETERS))
171 : {
172 12 : if(f_parsed)
173 : {
174 : // WARNING: at this point the command line and environment
175 : // variable may not be parsed in full if at all
176 : //
177 : //if(has_flag(SYSTEM_OPTION_CONFIGURATION_FILENAMES))
178 3 : if(is_defined("config-dir"))
179 : {
180 2 : std::size_t const max(size("config-dir"));
181 2 : directories.reserve(max);
182 6 : for(std::size_t idx(0); idx < max; ++idx)
183 : {
184 4 : directories.push_back(get_string("config-dir", idx));
185 : }
186 : }
187 : }
188 : else
189 : {
190 : // we've got to do some manual parsing (argh!)
191 : //
192 9 : directories = find_config_dir(argc, argv);
193 9 : if(directories.empty())
194 : {
195 5 : string_list_t args(split_environment(f_environment_variable));
196 :
197 5 : std::vector<char *> sub_argv;
198 5 : sub_argv.resize(args.size() + 2);
199 5 : sub_argv[0] = const_cast<char *>(f_program_fullname.c_str());
200 16 : for(std::size_t idx(0); idx < args.size(); ++idx)
201 : {
202 11 : sub_argv[idx + 1] = const_cast<char *>(args[idx].c_str());
203 : }
204 5 : sub_argv[args.size() + 1] = nullptr;
205 :
206 5 : directories = find_config_dir(sub_argv.size() - 1, sub_argv.data());
207 5 : }
208 : }
209 : }
210 :
211 141 : if(f_options_environment.f_configuration_directories != nullptr)
212 : {
213 210 : for(char const * const * configuration_directories(f_options_environment.f_configuration_directories);
214 210 : *configuration_directories != nullptr;
215 : ++configuration_directories)
216 : {
217 164 : directories.push_back(*configuration_directories);
218 : }
219 : }
220 :
221 141 : if(directories.empty())
222 : {
223 94 : std::string const name(get_group_or_project_name());
224 :
225 94 : if(!name.empty())
226 : {
227 184 : std::string directory_name("/usr/share/advgetopt/options/");
228 92 : directory_name += name;
229 92 : directories.push_back(directory_name);
230 :
231 92 : directory_name = "/usr/share/";
232 92 : directory_name += name;
233 92 : directory_name += "/options";
234 92 : directories.push_back(directory_name);
235 :
236 92 : directory_name = "/etc/";
237 92 : directory_name += name;
238 92 : directories.push_back(directory_name);
239 92 : }
240 94 : }
241 :
242 282 : std::string const filename(f_options_environment.f_configuration_filename);
243 :
244 592 : for(auto directory : directories)
245 : {
246 451 : if(!directory.empty())
247 : {
248 902 : std::string const full_filename(directory + ("/" + filename));
249 451 : std::string const user_filename(handle_user_directory(full_filename));
250 451 : if(user_filename == full_filename)
251 : {
252 414 : if(!writable)
253 : {
254 348 : add_configuration_filename(names, user_filename);
255 : }
256 :
257 414 : string_list_t const with_project_name(insert_group_name(
258 : user_filename
259 414 : , f_options_environment.f_group_name
260 414 : , f_options_environment.f_project_name));
261 414 : if(!with_project_name.empty())
262 : {
263 847 : for(auto const & n : with_project_name)
264 : {
265 433 : add_configuration_filename(names, n);
266 : }
267 : }
268 414 : }
269 : else
270 : {
271 37 : add_configuration_filename(names, user_filename);
272 : }
273 451 : }
274 451 : }
275 141 : }
276 :
277 :
278 : /** \brief Define the list of direct configuration filenames.
279 : *
280 : * We generate two lists of configurations: a managed list and a direct
281 : * configuration list. The managed list is created with the
282 : * get_managed_configuration_filenames(). The direct list is created with
283 : * this function and the list of filenames defined in the
284 : * f_configuration_files list of paths.
285 : *
286 : * In this case, the paths defined in that list are directly used. No
287 : * additional directory are added, except for the sub-directory to allow
288 : * for administrator files to be edited (i.e. `\<name>.d/??-filename.conf`).
289 : *
290 : * \param[in,out] names The list of configuration filenames.
291 : * \param[in] writable Whether only writable filenames get added.
292 : */
293 345 : void getopt::get_direct_configuration_filenames(
294 : string_list_t & names
295 : , bool writable) const
296 : {
297 345 : if(f_options_environment.f_configuration_files == nullptr)
298 : {
299 258 : return;
300 : }
301 :
302 : // load options from configuration files specified as is by the programmer
303 : //
304 387 : for(char const * const * configuration_files(f_options_environment.f_configuration_files);
305 387 : *configuration_files != nullptr;
306 : ++configuration_files)
307 : {
308 300 : char const * filename(*configuration_files);
309 300 : if(*filename == '\0')
310 : {
311 0 : continue;
312 : }
313 :
314 900 : std::string const user_filename(handle_user_directory(filename));
315 300 : if(user_filename == filename)
316 : {
317 293 : if(!writable)
318 : {
319 203 : add_configuration_filename(names, user_filename);
320 : }
321 :
322 293 : string_list_t const with_project_name(insert_group_name(
323 : user_filename
324 293 : , f_options_environment.f_group_name
325 293 : , f_options_environment.f_project_name));
326 293 : if(!with_project_name.empty())
327 : {
328 586 : for(auto const & n : with_project_name)
329 : {
330 293 : add_configuration_filename(names, n);
331 : }
332 : }
333 293 : }
334 : else
335 : {
336 7 : add_configuration_filename(names, user_filename);
337 : }
338 300 : }
339 : }
340 :
341 :
342 : /** \brief Determine the best suited file for updates.
343 : *
344 : * This function determines the best suited filename where an administrator
345 : * is expected to save his changes. For some tools, there may be many
346 : * choices. This function looks for the last entry since that last entry
347 : * will allow the administrator to override anything defined prior to
348 : * this last entry.
349 : *
350 : * The search first uses the direct configuration filenames if these are
351 : * defined. It uses the last directory which does not start with a tilde
352 : * (i.e. no user file).
353 : *
354 : * If the direct configuration is not defined in that process, we next
355 : * test with the managed configuration filenames. We again look for the
356 : * last path and that along the last configuration filename.
357 : *
358 : * If all of that fails, we build a name from "/etc/" the project name,
359 : * and use the project name plus ".conf" for the filename:
360 : *
361 : * \code
362 : * "/etc/" + project_name + "/" + project_name + ".conf"
363 : * \endcode
364 : *
365 : * then pass that file to the default_group_name() function. The result
366 : * is what gets returned.
367 : *
368 : * \return The file the administrator is expected to edit to make changes
369 : * to the configuration of the given project.
370 : */
371 19 : std::string getopt::get_output_filename() const
372 : {
373 19 : if(f_options_environment.f_configuration_files != nullptr)
374 : {
375 : // check the programmer defined paths as is
376 : //
377 16 : char const * found(nullptr);
378 68 : for(char const * const * configuration_files(f_options_environment.f_configuration_files);
379 68 : *configuration_files != nullptr;
380 : ++configuration_files)
381 : {
382 52 : char const * filename(*configuration_files);
383 52 : if(*filename == '\0')
384 : {
385 : // ignore empty filenames
386 : //
387 0 : continue;
388 : }
389 :
390 52 : if(filename[0] == '~'
391 0 : && (filename[1] == '/' || filename[1] == '\0'))
392 : {
393 : // ignore user directory entries
394 : //
395 0 : continue;
396 : }
397 :
398 : // we want the last one, so we are not done once we found one...
399 : //
400 52 : found = filename;
401 : }
402 :
403 16 : if(found != nullptr)
404 : {
405 : return default_group_name(
406 : found
407 13 : , f_options_environment.f_group_name
408 13 : , f_options_environment.f_project_name);
409 : }
410 : }
411 :
412 6 : if(f_options_environment.f_configuration_filename != nullptr
413 6 : && *f_options_environment.f_configuration_filename != '\0')
414 : {
415 : // check the directories either defined by the programmer or if
416 : // none defined by the programmer, as defined by advgetopt which
417 : // in this case simply means "/etc/"; we ignore the possible
418 : // use of the --config-dir because in that case the administrator
419 : // knows where to save his file
420 : //
421 6 : std::string const name(get_group_or_project_name());
422 :
423 6 : std::string directory;
424 6 : if(f_options_environment.f_configuration_directories != nullptr)
425 : {
426 0 : for(char const * const * configuration_directories(f_options_environment.f_configuration_directories);
427 0 : *configuration_directories != nullptr;
428 : ++configuration_directories)
429 : {
430 0 : char const * dir(*configuration_directories);
431 0 : if(dir[0] == '\0')
432 : {
433 0 : continue;
434 : }
435 :
436 0 : if(dir[0] == '~'
437 0 : && (dir[1] == '/' || dir[1] == '\0'))
438 : {
439 0 : continue;
440 : }
441 :
442 : // we want to keep the last entry unless it starts with ~/...
443 : //
444 0 : directory = dir;
445 : }
446 : }
447 :
448 6 : if(directory.empty())
449 : {
450 : // no programmer defined directory, use a system defined one
451 : // instead
452 : //
453 6 : directory = "/etc/" + name;
454 : }
455 :
456 6 : if(directory.back() != '/')
457 : {
458 6 : directory += '/';
459 : }
460 :
461 6 : std::string const filename(directory + f_options_environment.f_configuration_filename);
462 :
463 : return default_group_name(
464 : filename
465 6 : , f_options_environment.f_group_name
466 6 : , f_options_environment.f_project_name);
467 6 : }
468 :
469 : // the programmer did not define anything, it is likely that no files
470 : // will be loaded but we still generate a default name
471 : //
472 0 : std::string filename("/etc/");
473 0 : if(f_options_environment.f_group_name != nullptr
474 0 : && *f_options_environment.f_group_name != '\0')
475 : {
476 0 : filename += f_options_environment.f_group_name;
477 : }
478 0 : else if(f_options_environment.f_project_name != nullptr
479 0 : && *f_options_environment.f_project_name != '\0')
480 : {
481 0 : filename += f_options_environment.f_project_name;
482 : }
483 : else
484 : {
485 : // really nothing can be done in this case... we have no name
486 : // to generate a valid path/configuration filename
487 : //
488 0 : return std::string();
489 : }
490 :
491 0 : filename += '/';
492 :
493 0 : if(f_options_environment.f_project_name != nullptr
494 0 : && *f_options_environment.f_project_name != '\0')
495 : {
496 0 : filename += f_options_environment.f_project_name;
497 : }
498 0 : else if(f_options_environment.f_group_name != nullptr
499 0 : && *f_options_environment.f_group_name != '\0')
500 : {
501 0 : filename += f_options_environment.f_group_name;
502 : }
503 : else
504 : {
505 : throw getopt_logic_error("we just checked both of those names and at least one was valid."); // LCOV_EXCL_LINE
506 : }
507 :
508 0 : filename += ".conf";
509 :
510 0 : return filename;
511 0 : }
512 :
513 :
514 : /** \brief Search for the "--config-dir" option in a set of arguments.
515 : *
516 : * This function searches the given list of \p argv arguments for the
517 : * "--config-dir".
518 : *
519 : * This is done that way because we prematurely need that information
520 : * in order to properly search for the configuration file. This is because
521 : * the "--config-dir" is not yet defined when we attempt to read the
522 : * user specific configuration file.
523 : *
524 : * \param[in] argc The number of arguments.
525 : * \param[in] argv The list of arguments to be searched.
526 : */
527 14 : string_list_t getopt::find_config_dir(
528 : int argc
529 : , char * argv[])
530 : {
531 14 : if(argv == nullptr)
532 : {
533 2 : return string_list_t();
534 : }
535 :
536 12 : string_list_t result;
537 51 : for(int idx(1); idx < argc; ++idx)
538 : {
539 39 : if(strcmp(argv[idx], "--config-dir") == 0)
540 : {
541 10 : for(++idx; idx < argc; ++idx)
542 : {
543 7 : if(argv[idx][0] == '-')
544 : {
545 2 : --idx;
546 2 : break;
547 : }
548 5 : result.push_back(argv[idx]);
549 : }
550 : }
551 34 : else if(strncmp(argv[idx], "--config-dir=", 13) == 0)
552 : {
553 2 : result.push_back(argv[idx] + 13);
554 : }
555 : }
556 :
557 12 : return result;
558 12 : }
559 :
560 :
561 : /** \brief This function checks for arguments in configuration files.
562 : *
563 : * Each configuration file is checked one after another. Each file that is
564 : * defined is loaded and each line is viewed as an option. If valid, it is
565 : * added to the resulting getopt list of options.
566 : *
567 : * Note that it is an error to define a command in a configuration file. If
568 : * that happens, an error occurs and the process stops. Technically this is
569 : * defined with the GETOPT_FLAG_CONFIGURATION_FILE flag in your opt table.
570 : *
571 : * The list of files is checked from beginning to end. So if a later file
572 : * changes an option of an earlier file, it is the one effective.
573 : *
574 : * The configuration file loader supports a project name as defined in the
575 : * get_project_name() function. It allows for a sub-directory to
576 : * be inserted between the path and the basename of the configuration
577 : * file. This allows for a file to be search in an extra sub-directory
578 : * so one can avoid changing the original definitions and only use
579 : * configuration files in the sub-directory. The path looks like this
580 : * when a project name is specified:
581 : *
582 : * \code
583 : * <path>/<project name>.d/<basename>
584 : * \endcode
585 : *
586 : * Notice that we add a ".d" as usual in other projects under Linux.
587 : *
588 : * \exception getopt_exception_invalid
589 : * This function generates the getopt_exception_invalid exception whenever
590 : * something invalid is found in the list of options passed as the \p opts
591 : * parameter.
592 : *
593 : * \exception getopt_exception_default
594 : * The function detects whether two options are marked as the default
595 : * option (the one receiving parameters that are not used by another command
596 : * or match a command.) This exception is raised when such is detected.
597 : *
598 : * \param[in] argc The number of arguments in argv.
599 : * \param[in] argv The arguments passed to the finish_parsing() function.
600 : *
601 : * \sa process_configuration_file()
602 : * \sa get_configuration_filenames()
603 : * \sa finish_parsing()
604 : */
605 270 : void getopt::parse_configuration_files(int argc, char * argv[])
606 : {
607 270 : string_list_t const filenames(get_configuration_filenames(false, false, argc, argv));
608 :
609 842 : for(auto f : filenames)
610 : {
611 572 : process_configuration_file(f);
612 572 : f_parsed = false;
613 572 : }
614 :
615 270 : f_parsed = true;
616 540 : }
617 :
618 :
619 : /** \brief Parse one specific configuration file and process the results.
620 : *
621 : * This function reads one specific configuration file using a conf_file
622 : * object and then goes through the resulting arguments and add them to
623 : * the options of this getopt object.
624 : *
625 : * The options found in the configuration file must match an option by
626 : * its long name. In a configuration file, it is not allowed to have an
627 : * option which name is only one character.
628 : *
629 : * \note
630 : * If the filename points to a file which can't be read or does not exist,
631 : * then nothing happens and the function returns without an error.
632 : *
633 : * \todo
634 : * Extend the support by having the various flags that the conf_file
635 : * class supports appear in the list of configuration filenames.
636 : *
637 : * \param[in] filename The name of the configuration file to check out.
638 : *
639 : * \sa parse_configuration_files()
640 : */
641 583 : void getopt::process_configuration_file(std::string const & filename)
642 : {
643 583 : option_info::set_configuration_filename(filename);
644 :
645 583 : conf_file_setup::pointer_t conf_setup;
646 583 : if(f_options_environment.f_config_setup == nullptr)
647 : {
648 583 : conf_setup = std::make_shared<conf_file_setup>(filename);
649 : }
650 : else
651 : {
652 0 : conf_setup = std::make_shared<conf_file_setup>(
653 : filename
654 0 : , *f_options_environment.f_config_setup);
655 : }
656 583 : if(!conf_setup->is_valid())
657 : {
658 : // a non-existant file is considered valid now so this should never
659 : // happen; later we may use the flag if we find errors in the file
660 : //
661 : return; // LCOV_EXCL_LINE
662 : }
663 583 : conf_file::pointer_t conf(conf_file::get_conf_file(*conf_setup));
664 :
665 : // is there a variable section?
666 : //
667 583 : if(f_options_environment.f_section_variables_name != nullptr)
668 : {
669 34 : conf->section_to_variables(
670 : f_options_environment.f_section_variables_name
671 17 : , f_variables);
672 : }
673 :
674 583 : conf_file::sections_t sections(conf->get_sections());
675 583 : if(!sections.empty())
676 : {
677 12 : std::string const name(CONFIGURATION_SECTIONS);
678 6 : option_info::pointer_t configuration_sections(get_option(name));
679 6 : if(configuration_sections == nullptr)
680 : {
681 3 : configuration_sections = std::make_shared<option_info>(name);
682 3 : configuration_sections->add_flag(
683 : GETOPT_FLAG_MULTIPLE
684 : | GETOPT_FLAG_CONFIGURATION_FILE);
685 3 : f_options_by_name[configuration_sections->get_name()] = configuration_sections;
686 : }
687 3 : else if(!configuration_sections->has_flag(GETOPT_FLAG_MULTIPLE))
688 : {
689 2 : cppthread::log << cppthread::log_level_t::error
690 1 : << "option \""
691 1 : << name
692 1 : << "\" must have GETOPT_FLAG_MULTIPLE set."
693 2 : << cppthread::end;
694 1 : return;
695 : }
696 15 : for(auto s : sections)
697 : {
698 10 : if(!configuration_sections->has_value(s))
699 : {
700 12 : configuration_sections->add_value(
701 : s
702 12 : , string_list_t()
703 : , option_source_t::SOURCE_CONFIGURATION);
704 : }
705 10 : }
706 7 : }
707 :
708 670 : for(auto const & param : conf->get_parameters())
709 : {
710 : // in configuration files we only allow long arguments
711 : //
712 88 : option_info::pointer_t opt(get_option(param.first));
713 88 : if(opt == nullptr)
714 : {
715 5 : if(!has_flag(GETOPT_ENVIRONMENT_FLAG_DYNAMIC_PARAMETERS)
716 5 : || param.first.length() == 1)
717 : {
718 6 : cppthread::log << cppthread::log_level_t::error
719 3 : << "unknown option \""
720 6 : << option_with_underscores(param.first)
721 3 : << "\" found in configuration file \""
722 3 : << filename
723 3 : << "\" on line "
724 6 : << param.second.get_line()
725 3 : << "."
726 12 : << cppthread::end;
727 3 : continue;
728 : }
729 : else
730 : {
731 : // add a new parameter dynamically
732 : //
733 2 : opt = std::make_shared<option_info>(param.first);
734 2 : opt->set_variables(f_variables);
735 :
736 2 : opt->set_flags(GETOPT_FLAG_CONFIGURATION_FILE | GETOPT_FLAG_DYNAMIC);
737 :
738 : // consider the first definition as the default
739 : // (which is likely in our environment)
740 : //
741 2 : opt->set_default(param.second);
742 :
743 2 : f_options_by_name[opt->get_name()] = opt;
744 : }
745 : }
746 : else
747 : {
748 83 : if(!opt->has_flag(GETOPT_FLAG_CONFIGURATION_FILE))
749 : {
750 : // in configuration files we are expected to use '_' so
751 : // print an error with such
752 : //
753 2 : cppthread::log << cppthread::log_level_t::error
754 1 : << "option \""
755 2 : << option_with_underscores(param.first)
756 1 : << "\" is not supported in configuration files (found in \""
757 1 : << filename
758 1 : << "\")."
759 3 : << cppthread::end;
760 1 : continue;
761 : }
762 : }
763 :
764 84 : std::string value(param.second.get_value());
765 84 : switch(param.second.get_assignment_operator())
766 : {
767 84 : case advgetopt::assignment_t::ASSIGNMENT_SET:
768 : case advgetopt::assignment_t::ASSIGNMENT_NONE:
769 : // nothing special in this case, just overwrite if already defined
770 : //
771 84 : break;
772 :
773 0 : case advgetopt::assignment_t::ASSIGNMENT_OPTIONAL:
774 0 : if(opt->is_defined())
775 : {
776 : // already set, do not overwrite
777 : //
778 0 : continue;
779 : }
780 0 : break;
781 :
782 0 : case advgetopt::assignment_t::ASSIGNMENT_APPEND:
783 0 : if(opt->is_defined()
784 0 : && !opt->has_flag(GETOPT_FLAG_MULTIPLE))
785 : {
786 : // append the new value
787 : //
788 0 : value = opt->get_value() + value;
789 : }
790 0 : break;
791 :
792 0 : case advgetopt::assignment_t::ASSIGNMENT_NEW:
793 0 : if(opt->is_defined())
794 : {
795 : // prevent re-assignment
796 : //
797 0 : cppthread::log << cppthread::log_level_t::error
798 0 : << "option \""
799 0 : << option_with_underscores(param.first)
800 0 : << "\" found in configuration file \""
801 0 : << filename
802 0 : << "\" on line "
803 0 : << param.second.get_line()
804 0 : << " uses the := operator but the value is already defined."
805 0 : << cppthread::end;
806 0 : continue;
807 : }
808 0 : break;
809 :
810 : }
811 :
812 168 : add_option_from_string(
813 : opt
814 : , value
815 : , filename
816 168 : , string_list_t()
817 : , option_source_t::SOURCE_CONFIGURATION);
818 670 : }
819 :
820 582 : f_parsed = true;
821 585 : }
822 :
823 :
824 :
825 :
826 :
827 :
828 : } // namespace advgetopt
829 : // vim: ts=4 sw=4 et
|