advgetopt 2.0.50
Parse complex command line arguments and configuration files in C++.
advgetopt_usage.cpp
Go to the documentation of this file.
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
27// self
28//
29#include "advgetopt/advgetopt.h"
30
31#include "advgetopt/exception.h"
32
33
34// snapdev
35//
36#include <snapdev/join_strings.h>
37#include <snapdev/tokenize_format.h>
38
39
40// C++
41//
42#include <iomanip>
43#include <iostream>
44
45
46// last include
47//
48#include <snapdev/poison.h>
49
50
51
52
53namespace advgetopt
54{
55
56
57
64{
65 // add the --long-help if at least one option uses the GROUP1, GROUP2,
66 // or SYSTEM
67 //
68 // add the --system-help if at least one option uses SYSTEM
69 //
70 bool add_long_help(false);
71 bool add_system_help(false);
72
73 for(auto it(f_options_by_name.begin())
74 ; it != f_options_by_name.end()
75 ; ++it)
76 {
78 {
79 add_long_help = true;
80 }
81
82 if(it->second->has_flag(GETOPT_FLAG_SHOW_SYSTEM))
83 {
84 add_system_help = true;
85 }
86 }
87
88 if(add_long_help)
89 {
90 option_info::pointer_t opt(std::make_shared<option_info>("long-help"));
91 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
94 opt->set_help("show all the help from all the available options.");
95 f_options_by_name["long-help"] = opt;
97 {
98 opt->set_short_name(L'?');
99 f_options_by_short_name[L'?'] = opt;
100 }
101 }
102
103 if(add_system_help)
104 {
105 option_info::pointer_t opt(std::make_shared<option_info>("system-help"));
106 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
109 opt->set_help("show commands and options added by libraries.");
110 f_options_by_name["system-help"] = opt;
112 {
113 opt->set_short_name(L'S');
114 f_options_by_short_name[L'S'] = opt;
115 }
116 }
117
118 if(f_options_environment.f_groups == nullptr)
119 {
120 // no groups, ignore following loop
121 //
122 return;
123 }
124
126 ; grp->f_group != GETOPT_FLAG_GROUP_NONE
127 ; ++grp)
128 {
129 // the name is not mandatory, without it you do not get the command
130 // line option but still get the group description
131 //
132 if(grp->f_name != nullptr
133 && *grp->f_name != '\0')
134 {
135 std::string const name(grp->f_name);
136 std::string const option_name(name + "-help");
137 option_info::pointer_t opt(std::make_shared<option_info>(option_name));
138 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
141 opt->set_help("show help from the \""
142 + name
143 + "\" group of options.");
144 f_options_by_name[option_name] = opt;
145 }
146 }
147}
148
149
166{
167 if(f_options_environment.f_groups == nullptr)
168 {
169 return nullptr;
170 }
171
172 if((group & ~GETOPT_FLAG_GROUP_MASK) != 0)
173 {
174 throw getopt_logic_error("group parameter must represent a valid group.");
175 }
176 if(group == GETOPT_FLAG_GROUP_NONE)
177 {
178 throw getopt_logic_error("group NONE cannot be assigned a name so you cannot search for it.");
179 }
180
182 ; grp->f_group != GETOPT_FLAG_GROUP_NONE
183 ; ++grp)
184 {
185 if(group == grp->f_group)
186 {
187 if((grp->f_name == nullptr || *grp->f_name == '\0')
188 && (grp->f_description == nullptr || *grp->f_description == '\0'))
189 {
190 throw getopt_logic_error("at least one of a group name or description must be defined (a non-empty string).");
191 }
192 return grp;
193 }
194 }
195
196 // group not defined
197 //
198 return nullptr;
199}
200
201
237std::string getopt::usage(flag_t show) const
238{
239 std::stringstream ss;
240
241 flag_t specific_group(show & GETOPT_FLAG_GROUP_MASK);
242
243 // ignore all the non-show flags
244 //
250
251 size_t const line_width(get_screen_width());
253
254 std::string save_default;
255 std::string save_help;
256
259 if(f_options_environment.f_groups == nullptr)
260 {
261 group_max = GETOPT_FLAG_GROUP_MINIMUM;
262 specific_group = GETOPT_FLAG_GROUP_NONE;
263 }
264 else if(specific_group != GETOPT_FLAG_GROUP_NONE)
265 {
266 // only display that specific group if asked to do so
267 //
268 pos = specific_group >> GETOPT_FLAG_GROUP_SHIFT;
269 group_max = pos;
270 }
271
272 std::multimap<advgetopt::option_info::pointer_t, advgetopt::option_info::pointer_t> alias_reverse_references;
273 for(auto const & opt : f_options_by_name)
274 {
275 if(!opt.second->has_flag(GETOPT_FLAG_ALIAS))
276 {
277 continue;
278 }
279 alias_reverse_references.insert(std::make_pair(opt.second->get_alias_destination(), opt.second));
280 }
281
282 for(; pos <= group_max; ++pos)
283 {
284 bool group_name_shown(false);
285 flag_t const group(pos << GETOPT_FLAG_GROUP_SHIFT);
286 for(auto const & opt : f_options_by_name)
287 {
288 if((opt.second->get_flags() & GETOPT_FLAG_GROUP_MASK) != group
289 && f_options_environment.f_groups != nullptr)
290 {
291 // this could be optimized but we'd probably not see much
292 // difference overall and it's just for the usage() call
293 //
294 continue;
295 }
296
297 std::string const help(opt.second->get_help());
298 if(help.empty())
299 {
300 // ignore entries without help
301 //
302 continue;
303 }
304
305 if(opt.second->has_flag(GETOPT_FLAG_ALIAS))
306 {
307 // ignore entries representing an alias
308 //
309 continue;
310 }
311
312 if((show & GETOPT_FLAG_SHOW_ALL) == 0)
313 {
314 if(show != 0)
315 {
316 if(!opt.second->has_flag(show))
317 {
318 // usage selected group is not present in this option, ignore
319 //
320 continue;
321 }
322 }
323 else if(opt.second->has_flag(GETOPT_FLAG_SHOW_GROUP1
326 {
327 // do not show specialized groups
328 //
329 continue;
330 }
331 }
332
333 if(!group_name_shown)
334 {
335 group_name_shown = true;
336
337 if(group != GETOPT_FLAG_GROUP_NONE)
338 {
339 group_description const * grp(find_group(group));
340 if(grp != nullptr)
341 {
342 ss << std::endl
343 << breakup_line(process_help_string(grp->f_description), 0, line_width);
344 }
345 }
346 }
347
348 std::stringstream argument;
349
350 if(opt.second->is_default_option())
351 {
352 switch(opt.second->get_flags() & (GETOPT_FLAG_REQUIRED | GETOPT_FLAG_MULTIPLE))
353 {
354 case 0:
355 argument << "[default argument]";
356 break;
357
359 argument << "<default argument>";
360 break;
361
363 argument << "[default arguments]";
364 break;
365
367 argument << "<default arguments>";
368 break;
369
370 }
371 }
372 else
373 {
374 argument << "--" << opt.second->get_name();
375 auto aliases(alias_reverse_references.lower_bound(opt.second));
376 if(!opt.second->has_flag(GETOPT_FLAG_REMOVE_NAMESPACE))
377 {
378 if(aliases->first == opt.second)
379 {
380 auto end(alias_reverse_references.upper_bound(opt.second));
381 for(auto a(aliases); a != end; ++a)
382 {
383 argument << " or --" << a->second->get_name();
384 }
385 }
386 }
387 if(opt.second->get_short_name() != NO_SHORT_NAME)
388 {
389 argument << " or -" << short_name_to_string(opt.second->get_short_name());
390 }
391 if(!opt.second->has_flag(GETOPT_FLAG_REMOVE_NAMESPACE))
392 {
393 if(aliases->first == opt.second)
394 {
395 auto end(alias_reverse_references.upper_bound(opt.second));
396 for(; aliases != end; ++aliases)
397 {
398 if(aliases->second->get_short_name() != NO_SHORT_NAME)
399 {
400 argument << " or -" << short_name_to_string(aliases->second->get_short_name());
401 }
402 }
403 }
404 }
405
406 switch(opt.second->get_flags() & (GETOPT_FLAG_FLAG | GETOPT_FLAG_REQUIRED | GETOPT_FLAG_MULTIPLE))
407 {
408 case 0:
409 argument << " [<arg>]";
410 break;
411
413 argument << " <arg>";
414 break;
415
417 argument << " {<arg>}";
418 break;
419
421 argument << " <arg> {<arg>}";
422 break;
423
424 }
425 }
426
427 if(opt.second->has_flag(GETOPT_FLAG_DYNAMIC_CONFIGURATION))
428 {
429 argument << "*";
430 }
431
432 if(opt.second->has_default())
433 {
434 argument << " (default is \""
435 << opt.second->get_default()
436 << "\")";
437 }
438
439 // Output argument string with help
440 //
441 if(opt.second->is_default_option())
442 {
443 save_default = argument.str();
444 save_help = help;
445 }
446 else
447 {
448 std::string variable_name;
449 if(!opt.second->get_environment_variable_name().empty())
450 {
451 variable_name += "\nEnvironment Variable Name: \"";
453 {
455 }
456 variable_name += opt.second->get_environment_variable_name();
457 variable_name += '"';
458 }
459 ss << format_usage_string(argument.str()
460 , process_help_string((help + variable_name).c_str())
461 , 30
462 , line_width);
463 }
464 }
465 }
466
467 if(!save_default.empty())
468 {
469 ss << format_usage_string(save_default
470 , process_help_string(save_help.c_str())
471 , 30
472 , line_width);
473 }
474
477 {
478 ss << std::endl;
480 }
481
482 return ss.str();
483}
484
485
487{
488public:
489 static constexpr snapdev::format_flag_t const FORMAT_FLAG_EXTENDED = 0x01; // '*'
490
491 static bool is_flag(char c, snapdev::format_item<char> & f)
492 {
493 switch(c)
494 {
495 case '*':
496 if(f.has_flags(FORMAT_FLAG_EXTENDED))
497 {
498 f.add_error(snapdev::format_error_t::FORMAT_ERROR_DUPLICATE);
499 }
500 f.add_flags(FORMAT_FLAG_EXTENDED);
501 return true;
502
503 default:
504 return false;
505
506 }
507 }
508};
509
510
512{
513public:
514 static std::string::size_type is_format(char const * s, snapdev::format_item<char> & f)
515 {
516 switch(s[0])
517 {
518 case 'a':
519 case 'b':
520 case 'c':
521 case 'd':
522 case 'e':
523 case 'E':
524 case 'f':
525 case 'g':
526 case 'i':
527 case 'l':
528 case 'm':
529 case 'o':
530 case 'p':
531 case 's':
532 case 't':
533 case 'v':
534 case 'w':
535 f.format(s[0]);
536 return 1UL;
537
538 }
539
540 f.add_error(snapdev::format_error_t::FORMAT_ERROR_UNKNOWN);
541 return 0;
542 }
543};
544
545
599std::string getopt::process_help_string(char const * help) const
600{
601 if(help == nullptr)
602 {
603 return std::string();
604 }
605
606 snapdev::format_item<char>::list_t items(snapdev::tokenize_format<
607 char
609 , usage_flag_traits>(help));
610
611 for(auto it(items.begin()); it != items.end(); ++it)
612 {
613 switch(it->format())
614 {
615 case 'a':
617 {
619 }
620 break;
621
622 case 'b':
624 {
626 }
627 break;
628
629 case 'c':
631 {
633 }
634 break;
635
636 case 'd':
639 {
641 {
642 std::string joined;
643 for(char const * const * directories(f_options_environment.f_configuration_directories);
644 *directories != nullptr;
645 ++directories)
646 {
647 if(!joined.empty())
648 {
649 joined += ", ";
650 }
651 joined += *directories;
652 }
653 it->string(joined);
654 }
655 else
656 {
658 }
659 }
660 break;
661
662 case 'e':
665 {
667 {
669 char const * env(getenv(f_options_environment.f_environment_variable_name));
670 if(env != nullptr)
671 {
672 var += '=';
673 var += env;
674 }
675 else
676 {
677 var += " (not set)";
678 }
679 it->string(var);
680 }
681 else
682 {
684 }
685 }
686 break;
687
688 case 'E':
690 {
692 }
693 break;
694
695 case 'f':
698 {
700 {
701 std::string joined;
702 for(char const * const * filenames(f_options_environment.f_configuration_files);
703 *filenames != nullptr;
704 ++filenames)
705 {
706 if(!joined.empty())
707 {
708 joined += ", ";
709 }
710 joined += *filenames;
711 }
712 it->string(joined);
713 }
714 else
715 {
717 }
718 }
719 break;
720
721 case 'g':
722 {
724 it->string(snapdev::join_strings(list, ", "));
725 }
726 break;
727
728 case 'i':
730 {
731 it->string(get_path_to_option_files());
732 }
733 else
734 {
736 it->string(snapdev::join_strings(list, ", "));
737 }
738 break;
739
740 case 'l':
741 if(f_options_environment.f_license != nullptr)
742 {
744 }
745 break;
746
747 case 'm':
749 {
751 }
752 break;
753
754 case 'o':
755 it->string(get_output_filename());
756 break;
757
758 case 'p':
760 {
761 it->string(f_program_fullname);
762 }
763 else
764 {
765 it->string(f_program_name);
766 }
767 break;
768
769 case 's':
771 {
773 }
774 break;
775
776 case 't':
778 {
780 }
781 break;
782
783 case 'v':
784 if(f_options_environment.f_version != nullptr)
785 {
787 }
788 break;
789
790 case 'w':
791 {
792 string_list_t const list(get_configuration_filenames(true, true));
793 it->string(snapdev::join_strings(list, ", "));
794 }
795 break;
796
797 }
798 }
799
800 snapdev::format_item<char> empty_item;
801 return snapdev::join_strings(items, empty_item);
802}
803
804
805
806
807} // namespace advgetopt
808// vim: ts=4 sw=4 et
Definitions of the advanced getopt class.
string_list_t get_configuration_filenames(bool exists, bool writable, int argc=0, char *argv[]=nullptr) const
Generate a list of configuration filenames.
options_environment f_options_environment
Definition advgetopt.h:226
option_info::map_by_name_t f_options_by_name
Definition advgetopt.h:227
std::string f_program_name
Definition advgetopt.h:224
std::string usage(flag_t show=GETOPT_FLAG_SHOW_MOST) const
Create a string of the command line arguments.
option_info::map_by_short_name_t f_options_by_short_name
Definition advgetopt.h:228
std::string get_path_to_option_files() const
std::string f_program_fullname
Definition advgetopt.h:223
string_list_t get_filenames_of_option_definitions() const
Get the path and filename to options.
group_description const * find_group(flag_t group) const
Search for group in the list of group names.
std::string get_output_filename() const
Determine the best suited file for updates.
void parse_options_from_group_names()
Transform group names in –<name>-help commands.
std::string process_help_string(char const *help) const
Change the % flags in help strings.
std::shared_ptr< option_info > pointer_t
Definition option_info.h:78
static constexpr snapdev::format_flag_t const FORMAT_FLAG_EXTENDED
static bool is_flag(char c, snapdev::format_item< char > &f)
static std::string::size_type is_format(char const *s, snapdev::format_item< char > &f)
Definitions of the advanced getopt exceptions.
The advgetopt environment to parse command line options.
Definition version.h:37
size_t get_screen_width()
Retrieve the width of one line in your console.
Definition utils.cpp:753
static constexpr flag_t GETOPT_FLAG_GROUP_MINIMUM
Definition flags.h:68
static constexpr flag_t GETOPT_FLAG_GROUP_MASK
Definition flags.h:67
static constexpr flag_t GETOPT_FLAG_REMOVE_NAMESPACE
Definition flags.h:80
static constexpr flag_t GETOPT_FLAG_SHOW_USAGE_ON_ERROR
Definition flags.h:61
std::string breakup_line(std::string line, size_t const option_width, size_t const line_width)
Breakup a string on multiple lines.
Definition utils.cpp:798
static constexpr flag_t GETOPT_FLAG_SHOW_GROUP1
Definition flags.h:63
static constexpr flag_t GETOPT_FLAG_SHOW_SYSTEM
Definition flags.h:65
static constexpr flag_t GETOPT_FLAG_GROUP_MAXIMUM
Definition flags.h:69
static constexpr flag_t GETOPT_FLAG_DYNAMIC_CONFIGURATION
Definition flags.h:49
static constexpr flag_t GETOPT_FLAG_COMMAND_LINE
Definition flags.h:46
constexpr short_name_t NO_SHORT_NAME
Definition option_info.h:51
static constexpr flag_t GETOPT_FLAG_SHOW_GROUP2
Definition flags.h:64
std::uint32_t flag_t
Definition flags.h:42
std::string format_usage_string(std::string const &argument, std::string const &help, size_t const option_width, size_t const line_width)
Format a help string to make it fit on a given width.
Definition utils.cpp:906
static constexpr flag_t GETOPT_FLAG_GROUP_SHIFT
Definition flags.h:70
static constexpr flag_t GETOPT_FLAG_FLAG
Definition flags.h:52
static constexpr flag_t GETOPT_FLAG_GROUP_COMMANDS
Definition flags.h:72
static constexpr flag_t GETOPT_FLAG_GROUP_NONE
Definition flags.h:71
std::string short_name_to_string(short_name_t short_name)
Convert a short name to a UTF-8 string.
static constexpr flag_t GETOPT_FLAG_MULTIPLE
Definition flags.h:54
static constexpr flag_t GETOPT_FLAG_REQUIRED
Definition flags.h:53
std::vector< std::string > string_list_t
Definition utils.h:41
static constexpr flag_t GETOPT_FLAG_SHOW_ALL
Definition flags.h:62
static constexpr flag_t GETOPT_FLAG_ALIAS
Definition flags.h:51
char const *const * f_configuration_directories
Definition options.h:451
char const * f_environment_variable_intro
Definition options.h:447
char const * f_section_variables_name
Definition options.h:448
char const * f_environment_variable_name
Definition options.h:446
char const *const * f_configuration_files
Definition options.h:449
group_description const * f_groups
Definition options.h:460

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.