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