61 {
62 std::stringstream out;
63 std::string commentLead;
64 commentLead.push_back(commentChar);
65 commentLead.push_back(' ');
66
67 std::vector<std::string> groups = app->get_groups();
68 bool defaultUsed = false;
69 groups.insert(groups.begin(), std::string("Options"));
70 for (const auto& group : groups) {
71 if (group == "Options" || group.empty()) {
72 if (defaultUsed) {
73 continue;
74 }
75 defaultUsed = true;
76 }
77 if (write_description && group != "Options" && !group.empty() &&
78 !app->get_options([group](const Option* opt) -> bool {
79 return opt->get_group() == group && opt->get_configurable();
80 })
81 .empty()) {
82 out << '\n' << commentChar << commentLead << group << "\n";
83 }
84 for (const Option* opt : app->get_options({})) {
85
86 if (opt->get_configurable()) {
87 if (opt->get_group() != group) {
88 if (!(group == "Options" && opt->get_group().empty())) {
89 continue;
90 }
91 }
92 const std::string name = prefix + opt->get_single_name();
93 std::string value =
94 detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
95
96 std::string defaultValue{};
97 if (default_also) {
98 static_assert(std::string::npos + static_cast<std::string::size_type>(1) == 0,
99 "std::string::npos + static_cast<std::string::size_type>(1) != 0");
100 if (!value.empty() && detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, literalQuote) == value) {
101 value.clear();
102 }
103 if (!opt->get_default_str().empty()) {
104 defaultValue = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, literalQuote);
105 if (defaultValue == "'\"\"'") {
106 defaultValue = "";
107 }
108 } else if (opt->get_run_callback_for_default()) {
109 defaultValue = "\"\"";
110 } else if (opt->get_required()) {
111 defaultValue = "\"<REQUIRED>\"";
112 } else if (opt->get_expected_min() == 0) {
113 defaultValue = "false";
114 } else {
115 defaultValue = "\"\"";
116 }
117 }
118 if (write_description && opt->has_description() && (default_also || !value.empty())) {
119 out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
120 }
121 if (default_also && !defaultValue.empty()) {
122 out << commentChar << name << valueDelimiter << defaultValue << "\n";
123 }
124 if (!value.empty()) {
125 if (!opt->get_fnames().empty()) {
126 value = opt->get_flag_value(name, value);
127 }
128 if (value == "\"default\"") {
129 value = "default";
130 }
131 out << name << valueDelimiter << value << "\n\n";
132 } else {
133 out << '\n';
134 }
135 }
136 }
137 }
138 auto subcommands = app->get_subcommands({});
139 for (const App* subcom : subcommands) {
140 if (subcom->get_name().empty()) {
141 if (write_description && !subcom->get_group().empty()) {
142 out << '\n' << commentLead << subcom->get_group() << " Options\n";
143 }
144 out <<
to_config(subcom, default_also, write_description, prefix);
145 }
146 }
147
148 for (const App* subcom : subcommands) {
149 if (!subcom->get_name().empty()) {
150 if (subcom->get_configurable() && app->got_subcommand(subcom)) {
151 if (!prefix.empty() || app->get_parent() == nullptr) {
152 out << '[' << prefix << subcom->get_name() << "]\n";
153 } else {
154 std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
155 const auto* p = app->get_parent();
156 while (p->get_parent() != nullptr) {
157 subname = p->get_name() + parentSeparatorChar + subname;
158 p = p->get_parent();
159 }
160 out << '[' << subname << "]\n";
161 }
162 out <<
to_config(subcom, default_also, write_description,
"");
163 } else {
164 out <<
to_config(subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
165 }
166 }
167 }
168
169 std::string outString;
170 if (write_description && !out.str().empty()) {
171 outString =
172 commentChar + std::string("#") + commentLead + detail::fix_newlines(commentChar + commentLead, app->get_description());
173 }
174
175 return outString + out.str();
176 }