tclap 1.4.0
StdOutput.h
Go to the documentation of this file.
1// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
2
3/******************************************************************************
4 *
5 * file: StdOutput.h
6 *
7 * Copyright (c) 2004, Michael E. Smoot
8 * Copyright (c) 2017, Google LLC
9 * All rights reserved.
10 *
11 * See the file COPYING in the top directory of this distribution for
12 * more information.
13 *
14 * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 *****************************************************************************/
23
24#ifndef TCLAP_STD_OUTPUT_H
25#define TCLAP_STD_OUTPUT_H
26
27#include <tclap/Arg.h>
28#include <tclap/ArgGroup.h>
30#include <tclap/CmdLineOutput.h>
31
32#include <algorithm>
33#include <cctype>
34#include <cstddef>
35#include <iostream>
36#include <list>
37#include <sstream>
38#include <string>
39#include <utility>
40#include <vector>
41
42namespace TCLAP {
43
48class StdOutput : public CmdLineOutput {
49public:
55 virtual void usage(CmdLineInterface &c);
56
62 virtual void version(CmdLineInterface &c);
63
70 virtual void failure(CmdLineInterface &c, ArgException &e);
71
72protected:
78 void _shortUsage(CmdLineInterface &c, std::ostream &os) const;
79
86 void _longUsage(CmdLineInterface &c, std::ostream &os) const;
87
99 void spacePrint(std::ostream &os, const std::string &s, int maxWidth,
100 int indentSpaces, int secondLineOffset) const;
101
102private:
103 enum { _usageWidth = 75 };
104
105 void _partitionGroups(std::list<ArgGroup *> &groups,
106 std::list<ArgGroup *> &exclusiveGroups,
107 std::list<ArgGroup *> &nonExclusiveGroups) const;
108 void _printShortSwitches(std::ostream &os, std::string &switches,
109 std::list<ArgGroup *> &groups) const;
110 void _printLongSwitches(std::ostream &os,
111 std::list<ArgGroup *> &groups) const;
112 void _printShortOptions(std::ostream &os,
113 std::list<ArgGroup *> &groups) const;
114 void _printShortOperands(CmdLineInterface &cmd, std::ostream &os,
115 std::list<ArgGroup *> &groups) const;
116 void _printLabeledGroups(CmdLineInterface &cmd, std::ostream &os,
117 std::list<ArgGroup *> &groups) const;
118 void _printLongOperands(CmdLineInterface &cmd, std::ostream &os,
119 std::list<ArgGroup *> &groups) const;
120 void _printExclusiveLongGroup(CmdLineInterface &cmd, std::ostream &os,
121 ArgGroup &group) const;
122 void _printGroupHeading(CmdLineInterface &cmd, std::ostream &os,
123 const ArgGroup &group) const;
124 void _printLongEntry(std::ostream &os, const Arg &arg, bool required,
125 int indent) const;
126};
127
129 std::string progName = _cmd.getProgramName();
130 std::string xversion = _cmd.getVersion();
131
132 std::cout << std::endl
133 << progName << " "
134 << _cmd.translateMessage("version_label", "version:")
135 << " " << xversion << std::endl
136 << std::endl;
137}
138
140 std::cout << std::endl
141 << _cmd.translateMessage("usage_header", "USAGE: ")
142 << std::endl
143 << std::endl;
144
145 _shortUsage(_cmd, std::cout);
146
147 std::cout << std::endl << std::endl
148 << _cmd.translateMessage("where_header", "Where: ")
149 << std::endl
150 << std::endl;
151
152 _longUsage(_cmd, std::cout);
153
154 std::cout << std::endl;
155}
156
158 std::string progName = _cmd.getProgramName();
159
160 std::cerr << _cmd.translateMessage("parse_error_header", "PARSE ERROR:")
161 << " " << e.argId() << std::endl
162 << " " << e.error() << std::endl
163 << std::endl;
164
165 if (_cmd.hasHelpAndVersion()) {
166 std::cerr << _cmd.translateMessage("brief_usage_header", "Brief USAGE: ")
167 << std::endl;
168
169 _shortUsage(_cmd, std::cerr);
170
171 std::cerr << std::endl
172 << _cmd.translateMessage("complete_usage_hint",
173 "For complete USAGE and HELP type: ")
174 << std::endl
175 << " " << progName << " " << Arg::nameStartString()
176 << "help" << std::endl
177 << std::endl;
178 } else {
179 usage(_cmd);
180 }
181
182 throw ExitException(1);
183}
184
185// TODO: Remove this
186inline void removeChar(std::string &s, char r) {
187 size_t p;
188 while ((p = s.find_first_of(r)) != std::string::npos) {
189 s.erase(p, 1);
190 }
191}
192
193inline bool cmpSwitch(const char &a, const char &b) {
194 int lowa = std::tolower(a);
195 int lowb = std::tolower(b);
196
197 if (lowa == lowb) {
198 return a < b;
199 }
200
201 return lowa < lowb;
202}
203
204namespace internal {
205inline bool IsVisibleShortSwitch(const Arg &arg) {
206 return !(arg.getName() == Arg::ignoreNameString() ||
207 arg.isValueRequired() || arg.getFlag() == "") &&
208 arg.visibleInHelp();
209}
210
211inline bool IsVisibleLongSwitch(const Arg &arg) {
212 return (arg.getName() != Arg::ignoreNameString() &&
213 !arg.isValueRequired() && arg.getFlag() == "" &&
214 arg.visibleInHelp());
215}
216
217inline bool IsVisibleOption(const Arg &arg) {
218 return (arg.getName() != Arg::ignoreNameString() && arg.isValueRequired() &&
219 arg.hasLabel() && arg.visibleInHelp());
220}
221
222inline bool CompareShortID(const Arg *a, const Arg *b) {
223 if (a->getFlag() == "" && b->getFlag() != "") {
224 return false;
225 }
226 if (b->getFlag() == "" && a->getFlag() != "") {
227 return true;
228 }
229
230 return a->shortID() < b->shortID();
231}
232
233// TODO: Fix me not to put --gopt before -f
234inline bool CompareOptions(std::pair<const Arg *, bool> a,
235 std::pair<const Arg *, bool> b) {
236 // First optional, then required
237 if (!a.second && b.second) {
238 return true;
239 }
240 if (a.second && !b.second) {
241 return false;
242 }
243
244 return CompareShortID(a.first, b.first);
245}
246// Group order is presentation order; positional operands use parser order.
247inline ArgGroup *FindArgGroup(const std::list<ArgGroup *> &groups,
248 const Arg *arg) {
249 for (std::list<ArgGroup *>::const_iterator it = groups.begin();
250 it != groups.end(); ++it)
251 if (std::find((*it)->begin(), (*it)->end(), arg) != (*it)->end())
252 return *it;
253 return NULL;
254}
255
256inline bool HasVisiblePositional(const ArgGroup &group) {
257 for (ArgGroup::const_iterator it = group.begin(); it != group.end(); ++it)
258 if (!(*it)->hasLabel() && (*it)->visibleInHelp()) return true;
259 return false;
260}
261
262inline void PrintExclusiveShortGroup(std::ostream &os, const ArgGroup &group) {
263 os << (group.isRequired() ? " {" : " [");
264 std::vector<Arg *> args;
265 for (ArgGroup::const_iterator it = group.begin(); it != group.end(); ++it)
266 if ((*it)->visibleInHelp()) args.push_back(*it);
267 std::sort(args.begin(), args.end(), CompareShortID);
268 const char *separator = "";
269 for (std::vector<Arg *>::const_iterator it = args.begin();
270 it != args.end(); ++it) {
271 os << separator << (*it)->shortID();
272 separator = "|";
273 }
274 os << (group.isRequired() ? '}' : ']');
275}
276} // namespace internal
277
294 std::ostream &os) const {
295 std::list<ArgGroup *> argSets = _cmd.getArgGroups();
296
297 std::ostringstream outp;
298 outp << _cmd.getProgramName() + " ";
299
300 std::string switches = Arg::flagStartString();
301 std::list<ArgGroup *> exclusiveGroups;
302 std::list<ArgGroup *> nonExclusiveGroups;
303 _partitionGroups(argSets, exclusiveGroups, nonExclusiveGroups);
304 _printShortSwitches(outp, switches, nonExclusiveGroups);
305 _printLongSwitches(outp, nonExclusiveGroups);
306
307 // Exclusive groups containing operands are printed with those operands.
308 for (std::list<ArgGroup *>::iterator it = exclusiveGroups.begin();
309 it != exclusiveGroups.end(); ++it) {
310 if (!internal::HasVisiblePositional(**it))
311 internal::PrintExclusiveShortGroup(outp, **it);
312 }
313
314 _printShortOptions(outp, nonExclusiveGroups);
315 _printShortOperands(_cmd, outp, argSets);
316
317 // if the program name is too long, then adjust the second line offset
318 int secondLineOffset = static_cast<int>(_cmd.getProgramName().length()) + 2;
319 if (secondLineOffset > _usageWidth / 2) secondLineOffset = static_cast<int>(_usageWidth / 2);
320
321 spacePrint(os, outp.str(), _usageWidth, 3, secondLineOffset);
322}
323
324inline void StdOutput::_partitionGroups(
325 std::list<ArgGroup *> &groups, std::list<ArgGroup *> &exclusiveGroups,
326 std::list<ArgGroup *> &nonExclusiveGroups) const {
327 for (std::list<ArgGroup *>::iterator sit = groups.begin();
328 sit != groups.end(); ++sit) {
329 if (CountVisibleArgs(**sit) <= 0) {
330 continue;
331 }
332
333 if ((*sit)->isExclusive()) {
334 exclusiveGroups.push_back(*sit);
335 } else {
336 nonExclusiveGroups.push_back(*sit);
337 }
338 }
339
340 // Move "exclusive groups" that have at most a single item to
341 // non-exclusive groups as exclusivit doesn't make sense with a
342 // single option. This can happen if args are hidden in help for
343 // example.
344 for (std::list<ArgGroup *>::iterator it = exclusiveGroups.begin();
345 it != exclusiveGroups.end();) {
346 if (CountVisibleArgs(**it) < 2) {
347 nonExclusiveGroups.push_back(*it);
348 it = exclusiveGroups.erase(it);
349 } else {
350 ++it;
351 }
352 }
353}
354
355inline void StdOutput::_printShortSwitches(
356 std::ostream &os, std::string &switches,
357 std::list<ArgGroup *> &groups) const {
358 bool hasShortSwitches = false;
359
360 // First short switches (needs to be special because they are all
361 // stuck together).
362 for (std::list<ArgGroup *>::iterator sit = groups.begin();
363 sit != groups.end(); ++sit) {
364 for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
365 ++it) {
366 if (internal::IsVisibleShortSwitch(**it)) {
367 switches += (*it)->getFlag();
368 hasShortSwitches = true;
369 }
370 }
371
372 std::sort(switches.begin(), switches.end(), cmpSwitch);
373 }
374
375 if (hasShortSwitches) os << " [" << switches << ']';
376}
377
378inline void StdOutput::_printLongSwitches(std::ostream &os,
379 std::list<ArgGroup *> &groups) const {
380 // Now do long switches (e.g., --version, but no -v)
381 std::vector<Arg *> longSwitches;
382 for (std::list<ArgGroup *>::iterator sit = groups.begin();
383 sit != groups.end(); ++sit) {
384 for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
385 ++it) {
386 Arg &arg = **it;
387 if (internal::IsVisibleLongSwitch(arg)) {
388 longSwitches.push_back(&arg);
389 }
390 }
391 }
392
393 std::sort(longSwitches.begin(), longSwitches.end(),
394 internal::CompareShortID);
395 for (std::vector<Arg *>::const_iterator it = longSwitches.begin();
396 it != longSwitches.end(); ++it) {
397 os << " [" << (**it).shortID() << ']';
398 }
399}
400
401inline void StdOutput::_printShortOptions(std::ostream &os,
402 std::list<ArgGroup *> &groups) const {
403 // Next do options, we sort them later by optional first.
404 std::vector<std::pair<const Arg *, bool> > options;
405 for (std::list<ArgGroup *>::iterator sit = groups.begin();
406 sit != groups.end(); ++sit) {
407 for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
408 ++it) {
409 Arg &arg = **it;
410 int visible = CountVisibleArgs(**sit);
411 bool required = arg.isRequired();
412 if (internal::IsVisibleOption(arg)) {
413 if (visible == 1 && (**sit).isRequired()) {
414 required = true;
415 }
416
417 options.push_back(std::make_pair(&arg, required));
418 }
419 }
420 }
421
422 std::sort(options.begin(), options.end(), internal::CompareOptions);
423 for (std::vector<std::pair<const Arg *, bool> >::const_iterator it =
424 options.begin();
425 it != options.end(); ++it) {
426 const Arg &arg = *it->first;
427 bool required = it->second;
428 os << (required ? " " : " [");
429 os << arg.shortID();
430 os << (required ? "" : "]");
431 }
432}
433
434inline void StdOutput::_printShortOperands(
435 CmdLineInterface &cmd, std::ostream &os,
436 std::list<ArgGroup *> &groups) const {
437 // Positional order must match the order in which the parser consumes argv.
438 const std::list<Arg *> operands = cmd.getArgList();
439 std::list<ArgGroup *> shown;
440 for (ArgListIterator it = operands.begin(); it != operands.end(); ++it) {
441 const Arg &arg = **it;
442 if (arg.hasLabel() || !arg.isValueRequired() || !arg.visibleInHelp())
443 continue;
444 ArgGroup *group = internal::FindArgGroup(groups, &arg);
445 if (group && group->isExclusive() && CountVisibleArgs(*group) > 1) {
446 if (std::find(shown.begin(), shown.end(), group) == shown.end()) {
447 internal::PrintExclusiveShortGroup(os, *group);
448 shown.push_back(group);
449 }
450 continue;
451 }
452 bool required = arg.isRequired() ||
453 (group && group->isRequired() && CountVisibleArgs(*group) == 1);
454 os << (required ? " " : " [") << arg.shortID() << (required ? "" : "]");
455 }
456}
457
459 std::ostream &os) const {
460 std::string message = _cmd.getMessage();
461 std::list<ArgGroup *> argSets = _cmd.getArgGroups();
462
463 _printLabeledGroups(_cmd, os, argSets);
464 _printLongOperands(_cmd, os, argSets);
465
466 if (!message.empty()) {
467 spacePrint(os, message, _usageWidth, 3, 0);
468 }
469
470 os.flush();
471}
472
473inline void StdOutput::_printLabeledGroups(
474 CmdLineInterface &cmd, std::ostream &os, std::list<ArgGroup *> &groups) const {
475 for (std::list<ArgGroup *>::iterator sit = groups.begin();
476 sit != groups.end(); ++sit) {
477 ArgGroup &argGroup = **sit;
478
479 int visible = CountVisibleArgs(argGroup);
480 bool exclusive = visible > 1 && argGroup.isExclusive();
481 if (exclusive && internal::HasVisiblePositional(argGroup)) continue;
482 bool forceRequired = visible == 1 && argGroup.isRequired();
483 if (exclusive) {
484 _printGroupHeading(cmd, os, argGroup);
485 }
486
487 for (ArgGroup::iterator it = argGroup.begin(); it != argGroup.end();
488 ++it) {
489 Arg &arg = **it;
490 if (!arg.visibleInHelp()) {
491 continue;
492 }
493
494 if (!arg.hasLabel()) {
495 continue;
496 }
497
498 bool required = arg.isRequired() || forceRequired;
499 _printLongEntry(os, arg, required, exclusive ? 6 : 3);
500 }
501 }
502}
503
504inline void StdOutput::_printLongOperands(
505 CmdLineInterface &cmd, std::ostream &os, std::list<ArgGroup *> &groups) const {
506 const std::list<Arg *> operands = cmd.getArgList();
507 std::list<ArgGroup *> shown;
508 for (ArgListIterator it = operands.begin(); it != operands.end(); ++it) {
509 const Arg &arg = **it;
510 if (arg.hasLabel() || !arg.visibleInHelp()) continue;
511 ArgGroup *group = internal::FindArgGroup(groups, &arg);
512 if (group && group->isExclusive() && CountVisibleArgs(*group) > 1) {
513 if (std::find(shown.begin(), shown.end(), group) != shown.end()) continue;
514 shown.push_back(group);
515 _printExclusiveLongGroup(cmd, os, *group);
516 continue;
517 }
518 const bool required = arg.isRequired() ||
519 (group && group->isRequired() && CountVisibleArgs(*group) == 1);
520 _printLongEntry(os, arg, required, 3);
521 }
522}
523
524inline void StdOutput::_printExclusiveLongGroup(
525 CmdLineInterface &cmd, std::ostream &os, ArgGroup &group) const {
526 _printGroupHeading(cmd, os, group);
527 for (ArgGroup::iterator member = group.begin(); member != group.end(); ++member) {
528 if (!(*member)->visibleInHelp()) continue;
529 // Preserve longID() before the virtual isRequired() call.
530 spacePrint(os, (*member)->longID(), _usageWidth, 6, 3);
531 spacePrint(os, (*member)->getDescription((*member)->isRequired()), _usageWidth, 8, 0);
532 os << '\n';
533 }
534}
535
536inline void StdOutput::_printGroupHeading(CmdLineInterface &cmd,
537 std::ostream &os,
538 const ArgGroup &group) const {
539 spacePrint(os,
540 group.isRequired()
541 ? cmd.translateMessage("one_of_group", "One of:")
542 : cmd.translateMessage("either_of_group", "Either of:"),
543 _usageWidth, 3, 0);
544}
545
546inline void StdOutput::_printLongEntry(std::ostream &os, const Arg &arg,
547 bool required, int indent) const {
548 spacePrint(os, arg.longID(), _usageWidth, indent, 3);
549 spacePrint(os, arg.getDescription(required), _usageWidth, indent + 2, 0);
550 os << '\n';
551}
552
553namespace {
554inline void fmtPrintLine(std::ostream &os, const std::string &text, int maxWidth,
555 int indentSpaces, int secondLineOffset) {
556 const std::string breakCharacters(" ,|");
557 // String indentation uses unsigned counts; clamp negative geometry first.
558 indentSpaces = std::max(indentSpaces, 0);
559 secondLineOffset = std::max(secondLineOffset, 0);
560 size_t availableWidth = std::max(maxWidth - indentSpaces, 0);
561 std::string indentString(indentSpaces, ' ');
562 size_t lineStart = 0;
563 size_t lineEnd = 0;
564 size_t textLength = text.length();
565 for (;;) {
566 if (textLength - lineStart <= availableWidth) {
567 // Rest of string fits on line, just print the remainder
568 os << indentString << text.substr(lineStart) << std::endl;
569 return;
570 }
571
572 // Keep the last delimiter that fits within the available width.
573 size_t nextBreak = lineEnd;
574 while (nextBreak - lineStart <= availableWidth &&
575 nextBreak != std::string::npos) {
576 lineEnd = nextBreak;
577 nextBreak = text.find_first_of(breakCharacters, lineEnd + 1);
578 }
579
580 if (lineEnd == lineStart) {
581 // Split a long word. A zero width must still consume one byte;
582 // subtracting from zero would underflow the unsigned width.
583 lineEnd = availableWidth > 0 ? lineStart + availableWidth - 1
584 : lineStart;
585 }
586
587 if (text[lineEnd] != ' ') {
588 // Include delimiter before line break, unless it's a space
589 lineEnd++;
590 }
591
592 os << indentString << text.substr(lineStart, lineEnd - lineStart) << '\n';
593
594 // Avoid printing extra white space at start of a line
595 for (; text[lineEnd] == ' '; lineEnd++) {
596 }
597 lineStart = lineEnd;
598
599 if (secondLineOffset != 0) {
600 // Adjust offset for following lines
601 indentString.insert(indentString.end(), secondLineOffset, ' ');
602 // Clamp the remaining width to prevent unsigned underflow.
603 availableWidth -=
604 std::min(availableWidth, static_cast<size_t>(secondLineOffset));
605 secondLineOffset = 0;
606 }
607 }
608}
609} // namespace
610
611inline void StdOutput::spacePrint(std::ostream &os, const std::string &s,
612 int maxWidth, int indentSpaces,
613 int secondLineOffset) const {
614 std::stringstream ss(s);
615 std::string line;
616 std::getline(ss, line);
617 fmtPrintLine(os, line, maxWidth, indentSpaces, secondLineOffset);
618 indentSpaces += secondLineOffset;
619
620 while (std::getline(ss, line)) {
621 fmtPrintLine(os, line, maxWidth, indentSpaces, 0);
622 }
623}
624
625} // namespace TCLAP
626
627#endif // TCLAP_STD_OUTPUT_H
A simple class that defines and argument exception.
std::string argId() const
Returns the argument id.
std::string error() const
Returns the error text.
ArgGroup is the base class for implementing groups of arguments that are mutually exclusive (it repla...
Definition ArgGroup.h:41
virtual bool isExclusive() const =0
Returns true if this argument group is exclusive.
iterator begin()
Definition ArgGroup.h:110
Container::const_iterator const_iterator
Definition ArgGroup.h:47
iterator end()
Definition ArgGroup.h:111
Container::iterator iterator
Definition ArgGroup.h:46
virtual bool isRequired() const =0
Returns true if this argument group is required.
A virtual base class that defines the essential data for all arguments.
Definition Arg.h:66
static const std::string nameStartString()
Definition Arg.h:275
static const std::string ignoreNameString()
The name used to identify the ignore rest argument.
Definition Arg.h:280
static const std::string flagStartString()
Definition Arg.h:266
The base class that manages the command line definition and passes along the parsing to the appropria...
virtual std::string getVersion() const =0
Returns the version string.
virtual std::string getMessage() const =0
Returns the message string.
virtual bool hasHelpAndVersion() const =0
Indicates whether or not the help and version switches were created automatically.
virtual std::string translateMessage(const std::string &messageId, const std::string &fallback) const =0
Translates a built-in message using the current translator.
virtual std::string getProgramName() const =0
Returns the program name string.
virtual std::list< ArgGroup * > getArgGroups()=0
Returns the list of ArgGroups.
The interface that any output object must implement.
Thrown when TCLAP thinks the program should exit.
A class that isolates any output from the CmdLine object so that it may be easily modified.
Definition StdOutput.h:48
void spacePrint(std::ostream &os, const std::string &s, int maxWidth, int indentSpaces, int secondLineOffset) const
This function inserts line breaks and indents long strings according the params input.
Definition StdOutput.h:611
virtual void version(CmdLineInterface &c)
Prints the version to stdout.
Definition StdOutput.h:128
virtual void failure(CmdLineInterface &c, ArgException &e)
Prints (to stderr) an error message, short usage Can be overridden to produce alternative behavior.
Definition StdOutput.h:157
void _shortUsage(CmdLineInterface &c, std::ostream &os) const
Writes a brief usage message with short args.
Definition StdOutput.h:293
void _longUsage(CmdLineInterface &c, std::ostream &os) const
Writes a longer usage message with long and short args, provides descriptions and prints message.
Definition StdOutput.h:458
virtual void usage(CmdLineInterface &c)
Prints the usage to stdout.
Definition StdOutput.h:139
Definition Arg.h:52
int CountVisibleArgs(const ArgGroup &g)
Definition ArgGroup.h:246
std::list< Arg * >::const_iterator ArgListIterator
Typedef of an Arg list iterator.
Definition Arg.h:451
void removeChar(std::string &s, char r)
Definition StdOutput.h:186
bool cmpSwitch(const char &a, const char &b)
Definition StdOutput.h:193