| 1 | // | 
| 2 | // Copyright 2019 The Abseil Authors. | 
| 3 | // | 
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
| 5 | // you may not use this file except in compliance with the License. | 
| 6 | // You may obtain a copy of the License at | 
| 7 | // | 
| 8 | //      https://www.apache.org/licenses/LICENSE-2.0 | 
| 9 | // | 
| 10 | // Unless required by applicable law or agreed to in writing, software | 
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 13 | // See the License for the specific language governing permissions and | 
| 14 | // limitations under the License. | 
| 15 |  | 
| 16 | #include "absl/flags/internal/usage.h" | 
| 17 |  | 
| 18 | #include <map> | 
| 19 | #include <string> | 
| 20 |  | 
| 21 | #include "absl/flags/flag.h" | 
| 22 | #include "absl/flags/internal/path_util.h" | 
| 23 | #include "absl/flags/internal/program_name.h" | 
| 24 | #include "absl/flags/usage_config.h" | 
| 25 | #include "absl/strings/ascii.h" | 
| 26 | #include "absl/strings/str_cat.h" | 
| 27 | #include "absl/strings/str_split.h" | 
| 28 | #include "absl/synchronization/mutex.h" | 
| 29 |  | 
| 30 | ABSL_FLAG(bool, help, false, | 
| 31 |           "show help on important flags for this binary [tip: all flags can "  | 
| 32 |           "have two dashes]" ); | 
| 33 | ABSL_FLAG(bool, helpfull, false, "show help on all flags" ); | 
| 34 | ABSL_FLAG(bool, helpshort, false, | 
| 35 |           "show help on only the main module for this program" ); | 
| 36 | ABSL_FLAG(bool, helppackage, false, | 
| 37 |           "show help on all modules in the main package" ); | 
| 38 | ABSL_FLAG(bool, version, false, "show version and build info and exit" ); | 
| 39 | ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags" ); | 
| 40 | ABSL_FLAG(std::string, helpon, "" , | 
| 41 |           "show help on the modules named by this flag value" ); | 
| 42 | ABSL_FLAG(std::string, helpmatch, "" , | 
| 43 |           "show help on modules whose name contains the specified substr" ); | 
| 44 |  | 
| 45 | namespace absl { | 
| 46 | namespace flags_internal { | 
| 47 | namespace { | 
| 48 |  | 
| 49 | // This class is used to emit an XML element with `tag` and `text`. | 
| 50 | // It adds opening and closing tags and escapes special characters in the text. | 
| 51 | // For example: | 
| 52 | // std::cout << XMLElement("title", "Milk & Cookies"); | 
| 53 | // prints "<title>Milk & Cookies</title>" | 
| 54 | class XMLElement { | 
| 55 |  public: | 
| 56 |   XMLElement(absl::string_view tag, absl::string_view txt) | 
| 57 |       : tag_(tag), txt_(txt) {} | 
| 58 |  | 
| 59 |   friend std::ostream& operator<<(std::ostream& out, | 
| 60 |                                   const XMLElement& xml_elem) { | 
| 61 |     out << "<"  << xml_elem.tag_ << ">" ; | 
| 62 |  | 
| 63 |     for (auto c : xml_elem.txt_) { | 
| 64 |       switch (c) { | 
| 65 |         case '"': | 
| 66 |           out << """ ; | 
| 67 |           break; | 
| 68 |         case '\'': | 
| 69 |           out << "'" ; | 
| 70 |           break; | 
| 71 |         case '&': | 
| 72 |           out << "&" ; | 
| 73 |           break; | 
| 74 |         case '<': | 
| 75 |           out << "<" ; | 
| 76 |           break; | 
| 77 |         case '>': | 
| 78 |           out << ">" ; | 
| 79 |           break; | 
| 80 |         default: | 
| 81 |           out << c; | 
| 82 |           break; | 
| 83 |       } | 
| 84 |     } | 
| 85 |  | 
| 86 |     return out << "</"  << xml_elem.tag_ << ">" ; | 
| 87 |   } | 
| 88 |  | 
| 89 |  private: | 
| 90 |   absl::string_view tag_; | 
| 91 |   absl::string_view txt_; | 
| 92 | }; | 
| 93 |  | 
| 94 | // -------------------------------------------------------------------- | 
| 95 | // Helper class to pretty-print info about a flag. | 
| 96 |  | 
| 97 | class FlagHelpPrettyPrinter { | 
| 98 |  public: | 
| 99 |   // Pretty printer holds on to the std::ostream& reference to direct an output | 
| 100 |   // to that stream. | 
| 101 |   FlagHelpPrettyPrinter(int max_line_len, std::ostream* out) | 
| 102 |       : out_(*out), | 
| 103 |         max_line_len_(max_line_len), | 
| 104 |         line_len_(0), | 
| 105 |         first_line_(true) {} | 
| 106 |  | 
| 107 |   void Write(absl::string_view str, bool wrap_line = false) { | 
| 108 |     // Empty std::string - do nothing. | 
| 109 |     if (str.empty()) return; | 
| 110 |  | 
| 111 |     std::vector<absl::string_view> tokens; | 
| 112 |     if (wrap_line) { | 
| 113 |       tokens = absl::StrSplit(str, absl::ByAnyChar(" \f\n\r\t\v" ), | 
| 114 |                               absl::SkipEmpty()); | 
| 115 |     } else { | 
| 116 |       tokens.push_back(str); | 
| 117 |     } | 
| 118 |  | 
| 119 |     for (auto token : tokens) { | 
| 120 |       bool new_line = (line_len_ == 0); | 
| 121 |  | 
| 122 |       // Write the token, ending the std::string first if necessary/possible. | 
| 123 |       if (!new_line && (line_len_ + token.size() >= max_line_len_)) { | 
| 124 |         EndLine(); | 
| 125 |         new_line = true; | 
| 126 |       } | 
| 127 |  | 
| 128 |       if (new_line) { | 
| 129 |         StartLine(); | 
| 130 |       } else { | 
| 131 |         out_ << ' '; | 
| 132 |         ++line_len_; | 
| 133 |       } | 
| 134 |  | 
| 135 |       out_ << token; | 
| 136 |       line_len_ += token.size(); | 
| 137 |     } | 
| 138 |   } | 
| 139 |  | 
| 140 |   void StartLine() { | 
| 141 |     if (first_line_) { | 
| 142 |       out_ << "    " ; | 
| 143 |       line_len_ = 4; | 
| 144 |       first_line_ = false; | 
| 145 |     } else { | 
| 146 |       out_ << "      " ; | 
| 147 |       line_len_ = 6; | 
| 148 |     } | 
| 149 |   } | 
| 150 |   void EndLine() { | 
| 151 |     out_ << '\n'; | 
| 152 |     line_len_ = 0; | 
| 153 |   } | 
| 154 |  | 
| 155 |  private: | 
| 156 |   std::ostream& out_; | 
| 157 |   const int max_line_len_; | 
| 158 |   int line_len_; | 
| 159 |   bool first_line_; | 
| 160 | }; | 
| 161 |  | 
| 162 | void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, | 
| 163 |                            std::ostream* out) { | 
| 164 |   FlagHelpPrettyPrinter printer(80, out);  // Max line length is 80. | 
| 165 |  | 
| 166 |   // Flag name. | 
| 167 |   printer.Write(absl::StrCat("-" , flag.Name())); | 
| 168 |  | 
| 169 |   // Flag help. | 
| 170 |   printer.Write(absl::StrCat("(" , flag.Help(), ");" ), /*wrap_line=*/true); | 
| 171 |  | 
| 172 |   // Flag data type (for V1 flags only). | 
| 173 |   if (!flag.IsAbseilFlag() && !flag.IsRetired()) { | 
| 174 |     printer.Write(absl::StrCat("type: " , flag.Typename(), ";" )); | 
| 175 |   } | 
| 176 |  | 
| 177 |   // The listed default value will be the actual default from the flag | 
| 178 |   // definition in the originating source file, unless the value has | 
| 179 |   // subsequently been modified using SetCommandLineOption() with mode | 
| 180 |   // SET_FLAGS_DEFAULT. | 
| 181 |   std::string dflt_val = flag.DefaultValue(); | 
| 182 |   if (flag.IsOfType<std::string>()) { | 
| 183 |     dflt_val = absl::StrCat("\"" , dflt_val, "\"" ); | 
| 184 |   } | 
| 185 |   printer.Write(absl::StrCat("default: " , dflt_val, ";" )); | 
| 186 |  | 
| 187 |   if (flag.modified) { | 
| 188 |     std::string curr_val = flag.CurrentValue(); | 
| 189 |     if (flag.IsOfType<std::string>()) { | 
| 190 |       curr_val = absl::StrCat("\"" , curr_val, "\"" ); | 
| 191 |     } | 
| 192 |     printer.Write(absl::StrCat("currently: " , curr_val, ";" )); | 
| 193 |   } | 
| 194 |  | 
| 195 |   printer.EndLine(); | 
| 196 | } | 
| 197 |  | 
| 198 | // Shows help for every filename which matches any of the filters | 
| 199 | // If filters are empty, shows help for every file. | 
| 200 | // If a flag's help message has been stripped (e.g. by adding '#define | 
| 201 | // STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help' | 
| 202 | // and its variants. | 
| 203 | void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, | 
| 204 |                    HelpFormat format = HelpFormat::kHumanReadable) { | 
| 205 |   if (format == HelpFormat::kHumanReadable) { | 
| 206 |     out << flags_internal::ShortProgramInvocationName() << ": "  | 
| 207 |         << flags_internal::ProgramUsageMessage() << "\n\n" ; | 
| 208 |   } else { | 
| 209 |     // XML schema is not a part of our public API for now. | 
| 210 |     out << "<?xml version=\"1.0\"?>\n"  | 
| 211 |         // The document. | 
| 212 |         << "<AllFlags>\n"  | 
| 213 |         // The program name and usage. | 
| 214 |         << XMLElement("program" , flags_internal::ShortProgramInvocationName()) | 
| 215 |         << '\n' | 
| 216 |         << XMLElement("usage" , flags_internal::ProgramUsageMessage()) << '\n'; | 
| 217 |   } | 
| 218 |  | 
| 219 |   // Map of package name to | 
| 220 |   //   map of file name to | 
| 221 |   //     vector of flags in the file. | 
| 222 |   // This map is used to output matching flags grouped by package and file | 
| 223 |   // name. | 
| 224 |   std::map<std::string, | 
| 225 |            std::map<std::string, | 
| 226 |                     std::vector<const flags_internal::CommandLineFlag*>>> | 
| 227 |       matching_flags; | 
| 228 |  | 
| 229 |   flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { | 
| 230 |     absl::MutexLock l(InitFlagIfNecessary(flag)); | 
| 231 |  | 
| 232 |     std::string flag_filename = flag->Filename(); | 
| 233 |  | 
| 234 |     // Ignore retired flags. | 
| 235 |     if (flag->IsRetired()) return; | 
| 236 |  | 
| 237 |     // If the flag has been stripped, pretend that it doesn't exist. | 
| 238 |     if (flag->Help() == flags_internal::kStrippedFlagHelp) return; | 
| 239 |  | 
| 240 |     // Make sure flag satisfies the filter | 
| 241 |     if (!filter_cb || !filter_cb(flag_filename)) return; | 
| 242 |  | 
| 243 |     matching_flags[std::string(flags_internal::Package(flag_filename))] | 
| 244 |                   [flag_filename] | 
| 245 |                       .push_back(flag); | 
| 246 |   }); | 
| 247 |  | 
| 248 |   absl::string_view | 
| 249 |       package_separator;             // controls blank lines between packages. | 
| 250 |   absl::string_view file_separator;  // controls blank lines between files. | 
| 251 |   for (const auto& package : matching_flags) { | 
| 252 |     if (format == HelpFormat::kHumanReadable) { | 
| 253 |       out << package_separator; | 
| 254 |       package_separator = "\n\n" ; | 
| 255 |     } | 
| 256 |  | 
| 257 |     file_separator = "" ; | 
| 258 |     for (const auto& flags_in_file : package.second) { | 
| 259 |       if (format == HelpFormat::kHumanReadable) { | 
| 260 |         out << file_separator << "  Flags from "  << flags_in_file.first | 
| 261 |             << ":\n" ; | 
| 262 |         file_separator = "\n" ; | 
| 263 |       } | 
| 264 |  | 
| 265 |       for (const auto* flag : flags_in_file.second) { | 
| 266 |         flags_internal::FlagHelp(out, *flag, format); | 
| 267 |       } | 
| 268 |     } | 
| 269 |   } | 
| 270 |  | 
| 271 |   if (format == HelpFormat::kHumanReadable) { | 
| 272 |     if (filter_cb && matching_flags.empty()) { | 
| 273 |       out << "  No modules matched: use -helpfull\n" ; | 
| 274 |     } | 
| 275 |   } else { | 
| 276 |     // The end of the document. | 
| 277 |     out << "</AllFlags>\n" ; | 
| 278 |   } | 
| 279 | } | 
| 280 |  | 
| 281 | ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit); | 
| 282 | ABSL_CONST_INIT std::string* program_usage_message | 
| 283 |     GUARDED_BY(usage_message_guard) = nullptr; | 
| 284 |  | 
| 285 | }  // namespace | 
| 286 |  | 
| 287 | // -------------------------------------------------------------------- | 
| 288 | // Sets the "usage" message to be used by help reporting routines. | 
| 289 |  | 
| 290 | void SetProgramUsageMessage(absl::string_view new_usage_message) { | 
| 291 |   absl::MutexLock l(&usage_message_guard); | 
| 292 |  | 
| 293 |   if (flags_internal::program_usage_message != nullptr) { | 
| 294 |     ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice." ); | 
| 295 |     std::exit(1); | 
| 296 |   } | 
| 297 |  | 
| 298 |   program_usage_message = new std::string(new_usage_message); | 
| 299 | } | 
| 300 |  | 
| 301 | // -------------------------------------------------------------------- | 
| 302 | // Returns the usage message set by SetProgramUsageMessage(). | 
| 303 | // Note: We able to return string_view here only because calling | 
| 304 | // SetProgramUsageMessage twice is prohibited. | 
| 305 | absl::string_view ProgramUsageMessage() { | 
| 306 |   absl::MutexLock l(&usage_message_guard); | 
| 307 |  | 
| 308 |   return program_usage_message != nullptr | 
| 309 |              ? absl::string_view(*program_usage_message) | 
| 310 |              : "Warning: SetProgramUsageMessage() never called" ; | 
| 311 | } | 
| 312 |  | 
| 313 | // -------------------------------------------------------------------- | 
| 314 | // Produces the help message describing specific flag. | 
| 315 | void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, | 
| 316 |               HelpFormat format) { | 
| 317 |   if (format == HelpFormat::kHumanReadable) | 
| 318 |     flags_internal::FlagHelpHumanReadable(flag, &out); | 
| 319 | } | 
| 320 |  | 
| 321 | // -------------------------------------------------------------------- | 
| 322 | // Produces the help messages for all flags matching the filter. | 
| 323 | // If filter is empty produces help messages for all flags. | 
| 324 | void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format) { | 
| 325 |   flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) { | 
| 326 |     return filter.empty() || filename.find(filter) != absl::string_view::npos; | 
| 327 |   }; | 
| 328 |   flags_internal::FlagsHelpImpl(out, filter_cb, format); | 
| 329 | } | 
| 330 |  | 
| 331 | // -------------------------------------------------------------------- | 
| 332 | // Checks all the 'usage' command line flags to see if any have been set. | 
| 333 | // If so, handles them appropriately. | 
| 334 | int HandleUsageFlags(std::ostream& out) { | 
| 335 |   if (absl::GetFlag(FLAGS_helpshort)) { | 
| 336 |     flags_internal::FlagsHelpImpl( | 
| 337 |         out, flags_internal::GetUsageConfig().contains_helpshort_flags, | 
| 338 |         HelpFormat::kHumanReadable); | 
| 339 |     return 1; | 
| 340 |   } | 
| 341 |  | 
| 342 |   if (absl::GetFlag(FLAGS_helpfull)) { | 
| 343 |     // show all options | 
| 344 |     flags_internal::FlagsHelp(out); | 
| 345 |     return 1; | 
| 346 |   } | 
| 347 |  | 
| 348 |   if (!absl::GetFlag(FLAGS_helpon).empty()) { | 
| 349 |     flags_internal::FlagsHelp( | 
| 350 |         out, absl::StrCat("/" , absl::GetFlag(FLAGS_helpon), "." )); | 
| 351 |     return 1; | 
| 352 |   } | 
| 353 |  | 
| 354 |   if (!absl::GetFlag(FLAGS_helpmatch).empty()) { | 
| 355 |     flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch)); | 
| 356 |     return 1; | 
| 357 |   } | 
| 358 |  | 
| 359 |   if (absl::GetFlag(FLAGS_help)) { | 
| 360 |     flags_internal::FlagsHelpImpl( | 
| 361 |         out, flags_internal::GetUsageConfig().contains_help_flags); | 
| 362 |  | 
| 363 |     out << "\nTry --helpfull to get a list of all flags.\n" ; | 
| 364 |  | 
| 365 |     return 1; | 
| 366 |   } | 
| 367 |  | 
| 368 |   if (absl::GetFlag(FLAGS_helppackage)) { | 
| 369 |     flags_internal::FlagsHelpImpl( | 
| 370 |         out, flags_internal::GetUsageConfig().contains_helppackage_flags); | 
| 371 |  | 
| 372 |     out << "\nTry --helpfull to get a list of all flags.\n" ; | 
| 373 |  | 
| 374 |     return 1; | 
| 375 |   } | 
| 376 |  | 
| 377 |   if (absl::GetFlag(FLAGS_version)) { | 
| 378 |     if (flags_internal::GetUsageConfig().version_string) | 
| 379 |       out << flags_internal::GetUsageConfig().version_string(); | 
| 380 |     // Unlike help, we may be asking for version in a script, so return 0 | 
| 381 |     return 0; | 
| 382 |   } | 
| 383 |  | 
| 384 |   if (absl::GetFlag(FLAGS_only_check_args)) { | 
| 385 |     return 0; | 
| 386 |   } | 
| 387 |  | 
| 388 |   return -1; | 
| 389 | } | 
| 390 |  | 
| 391 | }  // namespace flags_internal | 
| 392 | }  // namespace absl | 
| 393 |  |