appkit  1.5.1
RoadNarrows Robotics Application Kit
CmdAddOns.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Application Tool Kit
4 //
5 // Link: https://github.com/roadnarrows-robotics/rnr-sdk
6 //
7 // Library: librnr_appkit
8 //
9 // File: CmdAddOns.cxx
10 //
11 /*! \file
12  *
13  * \brief Command line interface command add-ons implementation.
14  *
15  * \author Robin Knight (robin.knight@roadnarrows.com)
16  *
17  * \par Copyright
18  * \h_copy 2016-2017. RoadNarrows LLC.\n
19  * http://www.roadnarrows.com\n
20  * All Rights Reserved
21  *
22  * \par License:
23  * MIT
24  */
25 /*
26  * @EulaBegin@
27  *
28  * Permission is hereby granted, without written agreement and without
29  * license or royalty fees, to use, copy, modify, and distribute this
30  * software and its documentation for any purpose, provided that
31  * (1) The above copyright notice and the following two paragraphs
32  * appear in all copies of the source code and (2) redistributions
33  * including binaries reproduces these notices in the supporting
34  * documentation. Substantial modifications to this software may be
35  * copyrighted by their authors and need not follow the licensing terms
36  * described here, provided that the new terms are clearly indicated in
37  * all files where they apply.
38  *
39  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
40  * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
41  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
42  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
43  * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
44  * THE POSSIBILITY OF SUCH DAMAGE.
45  *
46  * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
47  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
48  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
49  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
50  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
51  *
52  * @EulaEnd@
53  */
54 ////////////////////////////////////////////////////////////////////////////////
55 
56 #include <unistd.h>
57 #include <stdlib.h>
58 
59 #include <iostream>
60 #include <sstream>
61 #include <string>
62 #include <vector>
63 #include <set>
64 #include <map>
65 
66 #include "rnr/rnrconfig.h"
67 #include "rnr/log.h"
68 
70 #include "rnr/appkit/CmdCore.h"
71 #include "rnr/appkit/CmdDef.h"
72 #include "rnr/appkit/CommandLine.h"
73 #include "rnr/appkit/CmdAddOns.h"
74 
75 using namespace std;
76 using namespace rnr;
77 using namespace rnr::str;
78 using namespace rnr::cmd;
79 using namespace rnr::cmd::addons;
80 
81 /*!
82  * \brief RoadNarrows Robotics
83  */
84 namespace rnr
85 {
86  namespace cmd
87  {
88  namespace addons
89  {
90  //------------------------------------------------------------------------
91  // Private
92  //------------------------------------------------------------------------
93 
94  static string atname("@N@"); ///< string to replace by command name
95 
96 
97  //------------------------------------------------------------------------
98  // Help Command Add-On
99  //------------------------------------------------------------------------
100 
101  /*!
102  * \brief Execute 'help' command.
103  *
104  * \par Syntax:
105  * help [{--list | -l | --synopsis | -s | --usage | -u}] [<cmd>]
106  *
107  * \todo Support ellipses ... syntax.
108  *
109  * \param cli Command line interface.
110  * \param argv Extended command line arguments.
111  *
112  * \return OK(0) on success, negative value on failure.
113  */
114  static int execHelp(CommandLine &cli, const CmdExtArgVec &argv)
115  {
116  HelpSect section = HelpSectAll; // default help option
117 
118  std::set<string> setOfCmds; // set of commands for help
119  std::set<string>::iterator iterHelp; // help commands iterator
120  size_t i;
121 
122  //
123  // Process Input arguments.
124  //
125  for(i = 1; i < argv.size(); ++i)
126  {
127  if( (argv[i] == "--brief") || (argv[i] == "-b") )
128  {
129  section = HelpSectBrief;
130  }
131  else if( (argv[i] == "--list") || (argv[i] == "-l") )
132  {
133  section = HelpSectName;
134  }
135  else if( (argv[i] == "--usage") || (argv[i] == "-u") )
136  {
137  section = HelpSectUsage;
138  }
139  else
140  {
141  setOfCmds.insert(argv[i].s());
142  }
143  }
144 
145  //
146  // All commands.
147  //
148  if( setOfCmds.empty() )
149  {
150  CmdDefCIter iterDefs;
151 
152  // alphbetize
153  for(iterDefs = cli.begin(); iterDefs != cli.end(); ++iterDefs)
154  {
155  setOfCmds.insert(iterDefs->second.getName());
156  }
157  }
158 
159  //
160  // Print help for command(s).
161  //
162  for(iterHelp = setOfCmds.begin();
163  iterHelp != setOfCmds.end();
164  ++iterHelp)
165  {
166  const string &cmdName = *iterHelp;
167  const CmdDef &cmdDef = cli.at(cmdName);
168 
169  if( cmdDef.getUid() == NoUid )
170  {
171  cout << "Error: help: No help for command '" << cmdName << "'."
172  << endl;
173  return RC_ERROR;
174  }
175 
176  printCmdHelp(cout, cmdDef, section);
177 
178  switch( section )
179  {
180  case HelpSectName:
181  cout << " ";
182  break;
183  case HelpSectBrief:
184  case HelpSectUsage:
185  cout << endl;
186  break;
187  case HelpSectAll:
188  default:
189  cout << " ---" << endl << endl;
190  break;
191  }
192  }
193 
194  switch( section )
195  {
196  case HelpSectName:
197  cout << endl;
198  break;
199  case HelpSectBrief:
200  case HelpSectUsage:
201  case HelpSectAll:
202  default:
203  cout << " " << setOfCmds.size() << " commands" << endl;
204  break;
205  }
206 
207  return OK;
208  }
209 
210  int printCmdHelp(ostream &os,
211  const CmdDef &cmddef,
212  const HelpSect section)
213  {
214  return printCmdHelp(os, cmddef.getName(), cmddef.getSyntax(),
215  cmddef.getSynopsis(), cmddef.getLongDesc(),
216  section);
217  }
218 
219  int printCmdHelp(ostream &os, const CmdDesc &desc, const HelpSect section)
220  {
221  return printCmdHelp(os,
222  desc.name, desc.syntax, desc.synopsis, desc.longdesc,
223  section);
224  }
225 
226  int printCmdHelp(ostream &os,
227  const string &strName,
228  const string &strSyntax,
229  const string &strSynopsis,
230  const string &strLongDesc,
231  const HelpSect section)
232  {
233  size_t i;
234 
235  if( strName.empty() )
236  {
237  os << "Error: Command has no name." << endl;
238  return RC_ERROR;
239  }
240 
241  if( strSyntax.empty() )
242  {
243  os << "Error: Command has no extended usage syntax." << endl;
244  return RC_ERROR;
245  }
246 
247  StringVec usages = split(strSyntax, '\n');
248 
249  switch( section )
250  {
251  case HelpSectName:
252  os << strName;
253  break;
254 
255  case HelpSectBrief:
256  os << strName << " - " << strSynopsis << endl;
257  break;
258 
259  case HelpSectUsage:
260  for(i = 0; i < usages.size(); ++i)
261  {
262  if( !usages[i].empty() )
263  {
264  os << usages[i] << "\n";
265  }
266  }
267  break;
268 
269  case HelpSectAll:
270  default:
271  ssize_t len;
272  size_t w;
273 
274  len = (ssize_t)strName.size() + 2;
275  len = 80 - 2 * len;
276  os << strName << "()";
277  if( len > 1 )
278  {
279  w = os.width();
280  os.width(len);
281  os << "";
282  os.width(w);
283  os << strName << "()";
284  }
285  os << "\n\n";
286 
287  os << "Name\n " << strName;
288  os << " - " << strSynopsis;
289  os << "\n\n";
290 
291  os << "Synopsis\n";
292  for(i = 0; i < usages.size(); ++i)
293  {
294  if( !usages[i].empty() )
295  {
296  os << " " << usages[i] << "\n";
297  }
298  }
299  os << "\n";
300 
301  if( !strLongDesc.empty() )
302  {
303  os << "Description\n";
304 
305  StringVec lines = split(strLongDesc, '\n');
306  size_t wstart, wend, wlen; // word data
307  size_t cursor; // output line cursor
308 
309  for(i = 0; i < lines.size(); ++i)
310  {
311  // indent
312  os << " ";
313  cursor = 2;
314 
315  //
316  // Output line, wrapping if necessary.
317  //
318  for(size_t j = 0; j < lines[i].size(); )
319  {
320  // whitespace
321  while( isspace(lines[i][j]) && (j < lines[i].size()) )
322  {
323  // less than max output line length
324  if( cursor < 80 )
325  {
326  cout << ' ';
327  ++cursor;
328  }
329  // wrap
330  else
331  {
332  // wrap and indent
333  cout << "\n ";
334  cursor = 2;
335  }
336  ++j;
337  }
338 
339  if( j >= lines[i].size() )
340  {
341  break;
342  }
343 
344  // word
345  wstart = j;
346 
347  while( !isspace(lines[i][j]) && (j < lines[i].size()) )
348  {
349  ++j;
350  }
351 
352  wend = j;
353  wlen = wend - wstart;
354 
355  // wrap
356  if( (cursor > 2) && (cursor+wlen >= 80) )
357  {
358  /// wrap and indent
359  cout << "\n ";
360  cursor = 2;
361  }
362 
363  cout << lines[i].substr(wstart, wlen);
364  cursor += wlen;
365  }
366 
367  cout << '\n';
368  }
369  cout << '\n';
370  }
371  break;
372  }
373 
374  return OK;
375  }
376 
377  int addHelpCommand(CommandLine &cli, const string &strName)
378  {
379  string cmd(strName);
380  int uid;
381 
382  if( cmd.empty() )
383  {
384  cmd = "help";
385  }
386 
387  CmdDesc desc =
388  {
389  .name = cmd,
390 
391  .syntax = replace(atname, cmd,
392  "@N@ [{--brief | -b | --list | -l | --usage | -u}] [<cmd>]"),
393 
394  .synopsis = "Print command help.",
395 
396  .longdesc =
397  "Print command help. "
398  "If a <cmd> is specified, then only help for that command is "
399  "printed. Otherwise all command help is printed.\n\n"
400  "Mandotory arguments to long options are mandatory for short "
401  "options too.\n\n"
402  "-b, --brief Print only command brief descriptions.\n"
403  "-l, --list Print only command names\n"
404  "-u, --usage Print only command usages (syntax)."
405  };
406 
407  uid = cli.addCommand(desc, execHelp);
408 
409  return uid;
410  }
411 
412 
413  //------------------------------------------------------------------------
414  // Quit Command Add-On
415  //------------------------------------------------------------------------
416 
417  /*!
418  * \brief Execute 'quit' command.
419  *
420  * \par Syntax:
421  * quit
422  *
423  * \param cli Command line interface.
424  * \param argv Extended command line arguments.
425  *
426  * \return OK(0) on success, negative value on failure.
427  */
428  static int execQuit(CommandLine &cli, const CmdExtArgVec &argv)
429  {
430  cli.quit();
431  return OK;
432  }
433 
434  int addQuitCommand(CommandLine &cli, const string &strName)
435  {
436  string cmd(strName);
437  int uid;
438 
439  if( cmd.empty() )
440  {
441  cmd = "quit";
442  }
443 
444  CmdDesc desc =
445  {
446  .name = cmd,
447 
448  .syntax = replace(atname, cmd, "@N@"),
449 
450  .synopsis = replace(atname, cmd, "@N@ command-line interface.")
451  };
452 
453  uid = cli.addCommand(desc, execQuit);
454 
455  return uid;
456  }
457 
458 
459  //------------------------------------------------------------------------
460  // Backtrace Enable Command Add-On
461  //------------------------------------------------------------------------
462 
463  /*!
464  * \brief Execute 'backtrace' command.
465  *
466  * \par Syntax:
467  * bt <onoff>
468  *
469  * \param cli Command line interface.
470  * \param argv Extended command line arguments.
471  *
472  * \return OK(0) on success, negative value on failure.
473  */
474  static int execBtEnable(CommandLine &cli, const CmdExtArgVec &argv)
475  {
476  cli.setBtEnable(argv[1].b());
477  return OK;
478  }
479 
480  int addBtEnableCommand(CommandLine &cli, const string &strName)
481  {
482  string cmd(strName);
483  int uid;
484 
485  if( cmd.empty() )
486  {
487  cmd = "bt";
488  }
489 
490  CmdDesc desc =
491  {
492  .name = cmd,
493 
494  .syntax = replace(atname, cmd, "@N@ <onoff:bool>"),
495 
496  .synopsis = "Enable/disable backtracing of input line parsing.",
497 
498  .longdesc =
499  "Enable/disable backtracing of input line parsing."
500  "The <onoff> argument is a boolean string whose conversion is "
501  "either true(enable) and false(disable)."
502  };
503 
504  uid = cli.addCommand(desc, execBtEnable);
505 
506  return uid;
507  }
508 
509  } // namespace addons
510  } // namespace cmd
511 } // namespace rnr
512 
Command line interface command add-ons interface.
std::vector< std::string > StringVec
Useful types.
Definition: StringTheory.h:83
int getUid() const
Get command&#39;s unique id.
Definition: CmdDef.h:134
std::string name
command name
Definition: CmdCore.h:87
CmdDefMap::const_iterator CmdDefCIter
cmd const iter type
Definition: CommandLine.h:418
static int execQuit(CommandLine &cli, const CmdExtArgVec &argv)
Execute &#39;quit&#39; command.
Definition: CmdAddOns.cxx:428
CmdDefCIter begin() const
Return a constant iterator pointing to the first element of the list of command definitions.
Definition: CommandLine.h:1119
std::string synopsis
short command synopsis
Definition: CmdCore.h:89
std::string longdesc
long command description
Definition: CmdCore.h:90
void quit()
Mark command-line interface to quit.
Definition: CommandLine.h:489
String.
Definition: StringTheory.h:78
const std::string & getSynopsis() const
Return command&#39;s synopsis.
Definition: CmdDef.h:164
CmdDefCIter end() const
Return a constant iterator referring to the past-the-end element of the list of command definitions...
Definition: CommandLine.h:1130
static int execBtEnable(CommandLine &cli, const CmdExtArgVec &argv)
Execute &#39;backtrace&#39; command.
Definition: CmdAddOns.cxx:474
Add-Ons.
Definition: CmdAddOns.h:86
const int NoUid
Special values.
Definition: CmdCore.h:116
Of string spaces and their strangian operators.
User available command description structure.
Definition: CmdCore.h:85
size_t split(const std::string &str, const char delim, StringVec &elems)
Split string.
const CmdDef & at(const int uid) const
Get the command definition with the unique id.
Compiled command definition class.
Definition: CmdDef.h:88
Command line interface class interface.
Commands.
Definition: CmdAddOns.h:81
std::vector< CmdExtArg > CmdExtArgVec
vector of ext args type
Definition: CmdExtArg.h:397
virtual int addCommand(const CmdDesc &desc)
Add a command to the command line interface.
HelpSect
Command help section identifiers.
Definition: CmdAddOns.h:95
command name help section
Definition: CmdAddOns.h:98
static string atname("@N@")
string to replace by command name
Command line command definition class interface.
int addBtEnableCommand(CommandLine &cli, const std::string &strName="bt")
Add the core &#39;bt&#39; command to the command-line interface.
void setBtEnable(bool bEnable)
Enable/disable backtracing.
Definition: CommandLine.h:510
const std::string & getName() const
Return command&#39;s name.
Definition: CmdDef.h:144
const std::string & getLongDesc() const
Return command&#39;s long description.
Definition: CmdDef.h:174
int addHelpCommand(CommandLine &cli, const std::string &strName="help")
Add the core &#39;help&#39; command to the command-line interface.
Command line core data types.
int printCmdHelp(ostream &os, const string &strName, const string &strSyntax, const string &strSynopsis, const string &strLongDesc, const HelpSect section)
Definition: CmdAddOns.cxx:226
static int execHelp(CommandLine &cli, const CmdExtArgVec &argv)
Execute &#39;help&#39; command.
Definition: CmdAddOns.cxx:114
command brief description help section
Definition: CmdAddOns.h:99
command usage (synopsis) help section
Definition: CmdAddOns.h:100
std::string & replace(const std::string &what, const std::string &with, std::string &str)
In-place replace all whats in string with with.
const std::string & getSyntax() const
Return command&#39;s syntax usage.
Definition: CmdDef.h:154
RoadNarrows Robotics.
Definition: Camera.h:74
std::string syntax
parsable command extended usage syntax
Definition: CmdCore.h:88
all commond help sections
Definition: CmdAddOns.h:97
CommandLine class.
Definition: CommandLine.h:444
int addQuitCommand(CommandLine &cli, const std::string &strName="quit")
Add the core &#39;quit&#39; command to the command-line interface.