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>
29 #include <tclap/CmdLineInterface.h>
30 #include <tclap/CmdLineOutput.h>
31 
32 #include <algorithm>
33 #include <cctype>
34 #include <iostream>
35 #include <list>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 namespace TCLAP {
41 
46 class StdOutput : public CmdLineOutput {
47 public:
53  virtual void usage(CmdLineInterface &c);
54 
60  virtual void version(CmdLineInterface &c);
61 
68  virtual void failure(CmdLineInterface &c, ArgException &e);
69 
70 protected:
76  void _shortUsage(CmdLineInterface &c, std::ostream &os) const;
77 
84  void _longUsage(CmdLineInterface &c, std::ostream &os) const;
85 
97  void spacePrint(std::ostream &os, const std::string &s, int maxWidth,
98  int indentSpaces, int secondLineOffset) const;
99 };
100 
102  std::string progName = _cmd.getProgramName();
103  std::string xversion = _cmd.getVersion();
104 
105  std::cout << std::endl
106  << progName << " version: " << xversion << std::endl
107  << std::endl;
108 }
109 
110 inline void StdOutput::usage(CmdLineInterface &_cmd) {
111  std::cout << std::endl << "USAGE: " << std::endl << std::endl;
112 
113  _shortUsage(_cmd, std::cout);
114 
115  std::cout << std::endl << std::endl << "Where: " << std::endl << std::endl;
116 
117  _longUsage(_cmd, std::cout);
118 
119  std::cout << std::endl;
120 }
121 
123  std::string progName = _cmd.getProgramName();
124 
125  std::cerr << "PARSE ERROR: " << e.argId() << std::endl
126  << " " << e.error() << std::endl
127  << std::endl;
128 
129  if (_cmd.hasHelpAndVersion()) {
130  std::cerr << "Brief USAGE: " << std::endl;
131 
132  _shortUsage(_cmd, std::cerr);
133 
134  std::cerr << std::endl
135  << "For complete USAGE and HELP type: " << std::endl
136  << " " << progName << " " << Arg::nameStartString()
137  << "help" << std::endl
138  << std::endl;
139  } else {
140  usage(_cmd);
141  }
142 
143  throw ExitException(1);
144 }
145 
146 // TODO: Remove this
147 inline void removeChar(std::string &s, char r) {
148  size_t p;
149  while ((p = s.find_first_of(r)) != std::string::npos) {
150  s.erase(p, 1);
151  }
152 }
153 
154 inline bool cmpSwitch(const char &a, const char &b) {
155  int lowa = std::tolower(a);
156  int lowb = std::tolower(b);
157 
158  if (lowa == lowb) {
159  return a < b;
160  }
161 
162  return lowa < lowb;
163 }
164 
165 namespace internal {
166 inline bool IsVisibleShortSwitch(const Arg &arg) {
167  return !(arg.getName() == Arg::ignoreNameString() ||
168  arg.isValueRequired() || arg.getFlag() == "") &&
169  arg.visibleInHelp();
170 }
171 
172 inline bool IsVisibleLongSwitch(const Arg &arg) {
173  return (arg.getName() != Arg::ignoreNameString() &&
174  !arg.isValueRequired() && arg.getFlag() == "" &&
175  arg.visibleInHelp());
176 }
177 
178 inline bool IsVisibleOption(const Arg &arg) {
179  return (arg.getName() != Arg::ignoreNameString() && arg.isValueRequired() &&
180  arg.hasLabel() && arg.visibleInHelp());
181 }
182 
183 inline bool CompareShortID(const Arg *a, const Arg *b) {
184  if (a->getFlag() == "" && b->getFlag() != "") {
185  return false;
186  }
187  if (b->getFlag() == "" && a->getFlag() != "") {
188  return true;
189  }
190 
191  return a->shortID() < b->shortID();
192 }
193 
194 // TODO: Fix me not to put --gopt before -f
195 inline bool CompareOptions(std::pair<const Arg *, bool> a,
196  std::pair<const Arg *, bool> b) {
197  // First optional, then required
198  if (!a.second && b.second) {
199  return true;
200  }
201  if (a.second && !b.second) {
202  return false;
203  }
204 
205  return CompareShortID(a.first, b.first);
206 }
207 } // namespace internal
208 
225  std::ostream &os) const {
226  std::list<ArgGroup *> argSets = _cmd.getArgGroups();
227 
228  std::ostringstream outp;
229  outp << _cmd.getProgramName() + " ";
230 
231  std::string switches = Arg::flagStartString();
232 
233  std::list<ArgGroup *> exclusiveGroups;
234  std::list<ArgGroup *> nonExclusiveGroups;
235  for (std::list<ArgGroup *>::iterator sit = argSets.begin();
236  sit != argSets.end(); ++sit) {
237  if (CountVisibleArgs(**sit) <= 0) {
238  continue;
239  }
240 
241  if ((*sit)->isExclusive()) {
242  exclusiveGroups.push_back(*sit);
243  } else {
244  nonExclusiveGroups.push_back(*sit);
245  }
246  }
247 
248  // Move "exclusive groups" that have at most a single item to
249  // non-exclusive groups as exclusivit doesn't make sense with a
250  // single option. This can happen if args are hidden in help for
251  // example.
252  for (std::list<ArgGroup *>::iterator it = exclusiveGroups.begin();
253  it != exclusiveGroups.end();) {
254  if (CountVisibleArgs(**it) < 2) {
255  nonExclusiveGroups.push_back(*it);
256  it = exclusiveGroups.erase(it);
257  } else {
258  ++it;
259  }
260  }
261 
262  // First short switches (needs to be special because they are all
263  // stuck together).
264  for (std::list<ArgGroup *>::iterator sit = nonExclusiveGroups.begin();
265  sit != nonExclusiveGroups.end(); ++sit) {
266  for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
267  ++it) {
268  if (internal::IsVisibleShortSwitch(**it)) {
269  switches += (*it)->getFlag();
270  }
271  }
272 
273  std::sort(switches.begin(), switches.end(), cmpSwitch);
274  }
275 
276  outp << " [" << switches << ']';
277 
278  // Now do long switches (e.g., --version, but no -v)
279  std::vector<Arg *> longSwitches;
280  for (std::list<ArgGroup *>::iterator sit = nonExclusiveGroups.begin();
281  sit != nonExclusiveGroups.end(); ++sit) {
282  for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
283  ++it) {
284  Arg &arg = **it;
286  longSwitches.push_back(&arg);
287  }
288  }
289  }
290 
291  std::sort(longSwitches.begin(), longSwitches.end(),
293  for (std::vector<Arg *>::const_iterator it = longSwitches.begin();
294  it != longSwitches.end(); ++it) {
295  outp << " [" << (**it).shortID() << ']';
296  }
297 
298  // Now do all exclusive groups
299  for (std::list<ArgGroup *>::iterator sit = exclusiveGroups.begin();
300  sit != exclusiveGroups.end(); ++sit) {
301  ArgGroup &argGroup = **sit;
302  outp << (argGroup.isRequired() ? " {" : " [");
303 
304  std::vector<Arg *> args;
305  for (ArgGroup::iterator it = argGroup.begin(); it != argGroup.end();
306  ++it) {
307  if ((**it).visibleInHelp()) {
308  args.push_back(*it);
309  }
310  }
311 
312  std::sort(args.begin(), args.end(), internal::CompareShortID);
313  std::string sep = "";
314  for (std::vector<Arg *>::const_iterator it = args.begin();
315  it != args.end(); ++it) {
316  outp << sep << (**it).shortID();
317  sep = "|";
318  }
319 
320  outp << (argGroup.isRequired() ? '}' : ']');
321  }
322 
323  // Next do options, we sort them later by optional first.
324  std::vector<std::pair<const Arg *, bool> > options;
325  for (std::list<ArgGroup *>::iterator sit = nonExclusiveGroups.begin();
326  sit != nonExclusiveGroups.end(); ++sit) {
327  for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
328  ++it) {
329  Arg &arg = **it;
330  int visible = CountVisibleArgs(**sit);
331  bool required = arg.isRequired();
332  if (internal::IsVisibleOption(arg)) {
333  if (visible == 1 && (**sit).isRequired()) {
334  required = true;
335  }
336 
337  options.push_back(std::make_pair(&arg, required));
338  }
339  }
340  }
341 
342  std::sort(options.begin(), options.end(), internal::CompareOptions);
343  for (std::vector<std::pair<const Arg *, bool> >::const_iterator it =
344  options.begin();
345  it != options.end(); ++it) {
346  const Arg &arg = *it->first;
347  bool required = it->second;
348  outp << (required ? " " : " [");
349  outp << arg.shortID();
350  outp << (required ? "" : "]");
351  }
352 
353  // Next do argsuments ("unlabled") in order of definition
354  for (std::list<ArgGroup *>::iterator sit = nonExclusiveGroups.begin();
355  sit != nonExclusiveGroups.end(); ++sit) {
356  for (ArgGroup::iterator it = (*sit)->begin(); it != (*sit)->end();
357  ++it) {
358  Arg &arg = **it;
359  if (arg.getName() == Arg::ignoreNameString()) {
360  continue;
361  }
362 
363  if (arg.isValueRequired() && !arg.hasLabel() &&
364  arg.visibleInHelp()) {
365  outp << (arg.isRequired() ? " " : " [");
366  outp << arg.shortID();
367  outp << (arg.isRequired() ? "" : "]");
368  }
369  }
370  }
371 
372  // if the program name is too long, then adjust the second line offset
373  int secondLineOffset = static_cast<int>(_cmd.getProgramName().length()) + 2;
374  if (secondLineOffset > 75 / 2) secondLineOffset = static_cast<int>(75 / 2);
375 
376  spacePrint(os, outp.str(), 75, 3, secondLineOffset);
377 }
378 
380  std::ostream &os) const {
381  std::string message = _cmd.getMessage();
382  std::list<ArgGroup *> argSets = _cmd.getArgGroups();
383 
384  std::list<Arg *> unlabled;
385  for (std::list<ArgGroup *>::iterator sit = argSets.begin();
386  sit != argSets.end(); ++sit) {
387  ArgGroup &argGroup = **sit;
388 
389  int visible = CountVisibleArgs(argGroup);
390  bool exclusive = visible > 1 && argGroup.isExclusive();
391  bool forceRequired = visible == 1 && argGroup.isRequired();
392  if (exclusive) {
393  spacePrint(os, argGroup.isRequired() ? "One of:" : "Either of:", 75,
394  3, 0);
395  }
396 
397  for (ArgGroup::iterator it = argGroup.begin(); it != argGroup.end();
398  ++it) {
399  Arg &arg = **it;
400  if (!arg.visibleInHelp()) {
401  continue;
402  }
403 
404  if (!arg.hasLabel()) {
405  unlabled.push_back(&arg);
406  continue;
407  }
408 
409  bool required = arg.isRequired() || forceRequired;
410  if (exclusive) {
411  spacePrint(os, arg.longID(), 75, 6, 3);
412  spacePrint(os, arg.getDescription(required), 75, 8, 0);
413  } else {
414  spacePrint(os, arg.longID(), 75, 3, 3);
415  spacePrint(os, arg.getDescription(required), 75, 5, 0);
416  }
417  os << '\n';
418  }
419  }
420 
421  for (ArgListIterator it = unlabled.begin(); it != unlabled.end(); ++it) {
422  const Arg &arg = **it;
423  spacePrint(os, arg.longID(), 75, 3, 3);
424  spacePrint(os, arg.getDescription(), 75, 5, 0);
425  os << '\n';
426  }
427 
428  if (!message.empty()) {
429  spacePrint(os, message, 75, 3, 0);
430  }
431 
432  os.flush();
433 }
434 
435 namespace {
436 inline void fmtPrintLine(std::ostream &os, const std::string &s, int maxWidth,
437  int indentSpaces, int secondLineOffset) {
438  const std::string splitChars(" ,|");
439  int maxChars = maxWidth - indentSpaces;
440  std::string indentString(indentSpaces, ' ');
441  int from = 0;
442  int to = 0;
443  int end = s.length();
444  for (;;) {
445  if (end - from <= maxChars) {
446  // Rest of string fits on line, just print the remainder
447  os << indentString << s.substr(from) << std::endl;
448  return;
449  }
450 
451  // Find the next place where it is good to break the string
452  // (to) by finding the place where it is too late (tooFar) and
453  // taking the previous one.
454  int tooFar = to;
455  while (tooFar - from <= maxChars &&
456  static_cast<std::size_t>(tooFar) != std::string::npos) {
457  to = tooFar;
458  tooFar = s.find_first_of(splitChars, to + 1);
459  }
460 
461  if (to == from) {
462  // In case there was no good place to break the string,
463  // just break it in the middle of a word at line length.
464  to = from + maxChars - 1;
465  }
466 
467  if (s[to] != ' ') {
468  // Include delimiter before line break, unless it's a space
469  to++;
470  }
471 
472  os << indentString << s.substr(from, to - from) << '\n';
473 
474  // Avoid printing extra white space at start of a line
475  for (; s[to] == ' '; to++) {
476  }
477  from = to;
478 
479  if (secondLineOffset != 0) {
480  // Adjust offset for following lines
481  indentString.insert(indentString.end(), secondLineOffset, ' ');
482  maxChars -= secondLineOffset;
483  secondLineOffset = 0;
484  }
485  }
486 }
487 } // namespace
488 
489 inline void StdOutput::spacePrint(std::ostream &os, const std::string &s,
490  int maxWidth, int indentSpaces,
491  int secondLineOffset) const {
492  std::stringstream ss(s);
493  std::string line;
494  std::getline(ss, line);
495  fmtPrintLine(os, line, maxWidth, indentSpaces, secondLineOffset);
496  indentSpaces += secondLineOffset;
497 
498  while (std::getline(ss, line)) {
499  fmtPrintLine(os, line, maxWidth, indentSpaces, 0);
500  }
501 }
502 
503 } // namespace TCLAP
504 
505 #endif // TCLAP_STD_OUTPUT_H
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:53
bool IsVisibleShortSwitch(const Arg &arg)
Definition: StdOutput.h:166
iterator begin()
Definition: ArgGroup.h:109
A simple class that defines and argument exception.
Definition: ArgException.h:36
bool IsVisibleLongSwitch(const Arg &arg)
Definition: StdOutput.h:172
std::string argId() const
Returns the argument id.
Definition: ArgException.h:66
virtual void usage(CmdLineInterface &c)
Prints the usage to stdout.
Definition: StdOutput.h:110
Thrown when TCLAP thinks the program should exit.
Definition: ArgException.h:178
virtual void version(CmdLineInterface &c)
Prints the version to stdout.
Definition: StdOutput.h:101
bool cmpSwitch(const char &a, const char &b)
Definition: StdOutput.h:154
virtual bool hasLabel() const
Definition: Arg.h:374
virtual std::string getMessage() const =0
Returns the message string.
virtual std::string longID(const std::string &valueId="val") const
Returns a long ID for the usage.
Definition: Arg.h:498
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:379
virtual bool isRequired() const
Indicates whether the argument is required.
Definition: Arg.h:527
bool CompareOptions(std::pair< const Arg *, bool > a, std::pair< const Arg *, bool > b)
Definition: StdOutput.h:195
virtual bool visibleInHelp() const
Returns true if this Arg is visible in the help output.
Definition: Arg.h:372
static const std::string ignoreNameString()
The name used to identify the ignore rest argument.
Definition: Arg.h:225
static const std::string nameStartString()
Definition: Arg.h:220
virtual bool isExclusive() const =0
Returns true if this argument group is exclusive.
Container::iterator iterator
Definition: ArgGroup.h:44
const std::string & getName() const
Returns the argument name.
Definition: Arg.h:525
virtual std::string shortID(const std::string &valueId="val") const
Returns a short ID for the usage.
Definition: Arg.h:485
The base class that manages the command line definition and passes along the parsing to the appropria...
bool isValueRequired() const
Indicates whether a value must be specified for argument.
Definition: Arg.h:529
virtual std::list< ArgGroup * > getArgGroups()=0
Returns the list of ArgGroups.
A class that isolates any output from the CmdLine object so that it may be easily modified...
Definition: StdOutput.h:46
std::ostringstream ostringstream
Definition: sstream.h:38
int CountVisibleArgs(const ArgGroup &g)
Definition: ArgGroup.h:243
std::string getDescription() const
Returns the argument description.
Definition: Arg.h:262
const std::string & getFlag() const
Returns the argument flag.
Definition: Arg.h:523
void _shortUsage(CmdLineInterface &c, std::ostream &os) const
Writes a brief usage message with short args.
Definition: StdOutput.h:224
virtual bool hasHelpAndVersion() const =0
Indicates whether or not the help and version switches were created automatically.
virtual std::string getProgramName() const =0
Returns the program name string.
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:489
std::list< Arg * >::const_iterator ArgListIterator
Typedef of an Arg list iterator.
Definition: Arg.h:380
bool IsVisibleOption(const Arg &arg)
Definition: StdOutput.h:178
Definition: Arg.h:46
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:122
ArgGroup is the base class for implementing groups of arguments that are mutually exclusive (it repla...
Definition: ArgGroup.h:41
std::string error() const
Returns the error text.
Definition: ArgException.h:61
void removeChar(std::string &s, char r)
Definition: StdOutput.h:147
virtual std::string getVersion() const =0
Returns the version string.
iterator end()
Definition: ArgGroup.h:110
bool CompareShortID(const Arg *a, const Arg *b)
Definition: StdOutput.h:183
static const std::string flagStartString()
Definition: Arg.h:211
The interface that any output object must implement.
Definition: CmdLineOutput.h:45