appkit  1.5.1
RoadNarrows Robotics Application Kit
CmdArgDef.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: CmdArgDef.cxx
10 //
11 /*! \file
12  *
13  * \brief Command line argument definition class 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 <stdio.h>
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <ctype.h>
60 
61 #include <iostream>
62 #include <sstream>
63 #include <string>
64 #include <locale>
65 #include <vector>
66 #include <set>
67 #include <map>
68 
69 #include "rnr/rnrconfig.h"
70 #include "rnr/log.h"
71 
73 #include "rnr/appkit/IOManip.h"
74 #include "rnr/appkit/RegEx.h"
75 #include "rnr/appkit/LogBook.h"
76 #include "rnr/appkit/CmdCore.h"
77 #include "rnr/appkit/CmdExtArg.h"
78 #include "rnr/appkit/CmdArgDef.h"
79 
80 using namespace std;
81 using namespace rnr;
82 using namespace rnr::str;
83 using namespace rnr::io;
84 using namespace rnr::cmd;
85 
86 
87 // -----------------------------------------------------------------------------
88 // Support
89 // -----------------------------------------------------------------------------
90 
91 namespace rnr
92 {
93  namespace cmd
94  {
95  static const string noliteral; ///< no literal string
96 
97  /*!
98  * \brief Argument type - string symbol lookup table.
99  */
101  {
102  {ArgSymLiteral, CmdArgDef::ArgTypeLiteral},
103  {ArgSymWord, CmdArgDef::ArgTypeWord},
104  {ArgSymMultiWord, CmdArgDef::ArgTypeMultiWord},
105  {ArgSymIdentifier, CmdArgDef::ArgTypeIdentifier},
106  {ArgSymBoolean, CmdArgDef::ArgTypeBoolean},
107  {ArgSymInteger, CmdArgDef::ArgTypeInteger},
108  {ArgSymFpn, CmdArgDef::ArgTypeFpn},
109  {ArgSymFile, CmdArgDef::ArgTypeFile},
110  {ArgSymRegEx, CmdArgDef::ArgTypeRegEx},
111  {NULL, CmdArgDef::ArgTypeUndef}
112  };
113 
114  /*!
115  * \brief Argument flag modifiers - name lookup table.
116  */
118  {
119  {"FlagCommand", CmdArgDef::FlagCommand},
120  {"FlagOptional", CmdArgDef::FlagOptional},
121  {"FlagXorList", CmdArgDef::FlagXorList},
122  {NULL, 0}
123  };
124 
125  } // namespace cmd
126 } // namespace rnr
127 
128 
129 // -----------------------------------------------------------------------------
130 // CmdArgDef Class
131 // -----------------------------------------------------------------------------
132 
133 CmdArgDef::CmdArgDef()
134 {
135  m_nCmdUid = NoUid;
136  m_nFormIndex = -1;
137  m_nIndex = -1;
138  m_eType = ArgTypeUndef;
139  m_uFlags = 0;
140 }
141 
142 CmdArgDef::CmdArgDef(const CmdArgDef &src)
143 {
144  m_nCmdUid = src.m_nCmdUid;
145  m_nFormIndex = src.m_nFormIndex;
146  m_nIndex = src.m_nIndex;
147  m_strName = src.m_strName;
148  m_eType = src.m_eType;
149  m_literals = src.m_literals;
150  m_ranges = src.m_ranges;
151  m_re = src.m_re;
152  m_uFlags = src.m_uFlags;
153 }
154 
155 CmdArgDef::~CmdArgDef()
156 {
157 }
158 
159 CmdArgDef &CmdArgDef::operator=(const CmdArgDef &rhs)
160 {
161  m_nCmdUid = rhs.m_nCmdUid;
162  m_nFormIndex = rhs.m_nFormIndex;
163  m_nIndex = rhs.m_nIndex;
164  m_strName = rhs.m_strName;
165  m_eType = rhs.m_eType;
166  m_literals = rhs.m_literals;
167  m_ranges = rhs.m_ranges;
168  m_re = rhs.m_re;
169  m_uFlags = rhs.m_uFlags;
170 
171  return *this;
172 }
173 
174 bool CmdArgDef::isDefined() const
175 {
176  return (m_nIndex >= 0) && !m_strName.empty() && (m_eType != ArgTypeUndef);
177 }
178 
179 void CmdArgDef::setParent(const int nCmdUid, const int nFormIndex)
180 {
181  m_nCmdUid = nCmdUid;
182  m_nFormIndex = nFormIndex;
183 }
184 
185 void CmdArgDef::setIndex(const int nIndex)
186 {
187  m_nIndex = nIndex;
188 }
189 
190 void CmdArgDef::setName(const string &strName)
191 {
192  m_strName = strName;
193 }
194 
195 void CmdArgDef::setType(const ArgType eType)
196 {
197  m_eType = eType;
198 }
199 
200 void CmdArgDef::addLiteralValue(const string &strValue)
201 {
202  m_literals.push_back(strValue);
203 }
204 
205 const string &CmdArgDef::literalAt(const int nIndex) const
206 {
207  if( (nIndex >= 0) && (nIndex < m_literals.size()) )
208  {
209  return m_literals[nIndex];
210  }
211  else
212  {
213  return noliteral;
214  }
215 }
216 
217 void CmdArgDef::setRanges(const RangeVec &ranges)
218 {
219  m_ranges = ranges;
220 }
221 
222 bool CmdArgDef::inRange(const long value) const
223 {
224  return inRange((double)value);
225 }
226 
227 bool CmdArgDef::inRange(const double value) const
228 {
229  // no minimum or maximum
230  if( m_ranges.size() == 0 )
231  {
232  return true;
233  }
234 
235  for(size_t i = 0; i < m_ranges.size(); ++i)
236  {
237  if( (value >= m_ranges[i].min) && (value <= m_ranges[i].max) )
238  {
239  return true;
240  }
241  }
242 
243  return false;
244 }
245 
246 void CmdArgDef::setRegEx(const RegEx &re)
247 {
248  m_re = re;
249 }
250 
251 void CmdArgDef::orFlags(const unsigned uFlags)
252 {
253  m_uFlags |= uFlags;
254 }
255 
256 string CmdArgDef::constructLiteralList(const string sep) const
257 {
258  stringstream ss;
259  string pre;
260 
261  for(size_t i = 0; i < numOfLiterals(); ++i)
262  {
263  ss << pre << literalAt(i);
264  pre = sep;
265  }
266 
267  return ss.str();
268 }
269 
270 string CmdArgDef::constructRangeList(const string sep) const
271 {
272  stringstream ss;
273  string pre;
274 
275  for(size_t i = 0; i < m_ranges.size(); ++i)
276  {
277  ss << pre << m_ranges[i].min << ":" << m_ranges[i].max;
278  pre = sep;
279  }
280 
281  return ss.str();
282 }
283 
284 string CmdArgDef::constructSyntax() const
285 {
286  stringstream ss;
287  string sep;
288 
289  switch( m_eType )
290  {
291  case ArgTypeLiteral:
292  ss << "{" << constructLiteralList(" | ") << "}";
293  break;
294  case ArgTypeWord:
295  case ArgTypeMultiWord:
296  case ArgTypeIdentifier:
297  case ArgTypeBoolean:
298  case ArgTypeFile:
299  ss << "<" << m_strName << ":" << lookupArgSymbol(m_eType) << ">";
300  break;
301  case ArgTypeInteger:
302  case ArgTypeFpn:
303  ss << "<" << m_strName << ":" << lookupArgSymbol(m_eType);
304  if( m_ranges.size() > 0 )
305  {
306  ss << "(" << constructRangeList() << ")";
307  }
308  ss << ">";
309  break;
310  case ArgTypeRegEx:
311  ss << "<" << m_strName << ":" << lookupArgSymbol(m_eType);
312  if( m_re.isValid() )
313  {
314  ss << "(" << m_re.getRegEx() << ")";
315  }
316  ss << ">";
317  break;
318  default:
319  ss << "<" << m_strName << ">";
320  break;
321  }
322 
323  return ss.str();
324 }
325 
326 double CmdArgDef::match(const string &strArg, const bool bIgnoreCase) const
327 {
328  double fWeight = 0.0;
329 
330  switch( m_eType )
331  {
332  //
333  // Literal constant.
334  //
335  case ArgTypeLiteral:
336  if( matchLiteral(strArg, bIgnoreCase) >= 0 )
337  {
338  fWeight = 1.0;
339  }
340  break;
341 
342  //
343  // Contiguous block of non-whitespace characters.
344  //
345  case ArgTypeWord:
346  if( strArg.find_first_of(" \t\r\n\v\f") == string::npos )
347  {
348  fWeight = 0.91;
349  }
350  break;
351 
352  //
353  // Any string.
354  //
355  case ArgTypeMultiWord:
356  fWeight = 0.90;
357  break;
358 
359  //
360  // C conforming identifier.
361  //
362  case ArgTypeIdentifier:
363  if( isIdentifier(strArg) )
364  {
365  fWeight = 0.94;
366  }
367  break;
368 
369  //
370  // Boolean.
371  //
372  case ArgTypeBoolean:
373  {
374  bool val;
375  int rc;
376  if( bIgnoreCase )
377  {
378  rc = tobool(lowercase(strArg), val);
379  }
380  else
381  {
382  rc = tobool(strArg, val);
383  }
384 
385  if( rc == OK )
386  {
387  fWeight = 0.95;
388  }
389  }
390  break;
391 
392  //
393  // (Signed) integer in base 8, 10, or 16.
394  //
395  case ArgTypeInteger:
396  {
397  long val;
398  if( (tolong(strArg, val) == OK) && inRange(val) )
399  {
400  fWeight = 0.95;
401  }
402  }
403  break;
404 
405  //
406  // Floating-point number.
407  //
408  case ArgTypeFpn:
409  {
410  double val;
411  if( (todouble(strArg, val) == OK) && inRange(val) )
412  {
413  fWeight = 0.95;
414  }
415  }
416  break;
417 
418  //
419  // Regular expression.
420  //
421  case ArgTypeRegEx:
422  if( m_re.isValid() && m_re.match(strArg) )
423  {
424  fWeight = 0.93;
425  }
426  break;
427 
428  //
429  // Filename.
430  //
431  case ArgTypeFile:
432  fWeight = 0.91;
433  break;
434 
435  //
436  // Bug.
437  //
438  case ArgTypeUndef:
439  default:
440  stringstream ss;
441  ss
442  << "Bug: "
443  << "cmduid " << m_nCmdUid << ", "
444  << "form " << m_nFormIndex << ", "
445  << "arg " << m_nIndex << ": "
446  << m_eType << ": Unknown type.";
447  LOGERROR("%s", ss.str().c_str());
448  break;
449  }
450 
451  return fWeight;
452 }
453 
454 int CmdArgDef::matchLiteral(const string &strArg, const bool bIgnoreCase) const
455 {
456  if( bIgnoreCase )
457  {
458  string strICaseArg = lowercase(strArg);
459 
460  for(size_t i = 0; i < numOfLiterals(); ++i)
461  {
462  if( strICaseArg == lowercase(literalAt(i)) )
463  {
464  return (int)i;
465  }
466  }
467  }
468  else
469  {
470  for(size_t i = 0; i < numOfLiterals(); ++i)
471  {
472  if( strArg == literalAt(i) )
473  {
474  return (int)i;
475  }
476  }
477  }
478 
479  return -1;
480 }
481 
482 CmdExtArg CmdArgDef::convert(const string &strArg, const bool bIgnoreCase) const
483 {
484  CmdExtArg cvt(m_nCmdUid, m_nFormIndex, m_nIndex, 0, strArg);
485 
486  switch( m_eType )
487  {
488  //
489  // Converted literal value is the literal enum list index.
490  //
491  case ArgTypeLiteral:
492  {
493  long val;
494 
495  if( (val = matchLiteral(strArg, bIgnoreCase)) >= 0 )
496  {
497  cvt.e(val);
498  }
499  }
500  break;
501 
502  //
503  // The converted value is the argument for these argument types.
504  //
505  case ArgTypeWord:
506  case ArgTypeMultiWord:
507  case ArgTypeIdentifier:
508  case ArgTypeFile:
509  case ArgTypeRegEx:
510  cvt.s(strArg);
511  break;
512 
513  //
514  // Converted to boolean.
515  //
516  case ArgTypeBoolean:
517  {
518  bool val;
519  int rc;
520 
521  if( bIgnoreCase )
522  {
523  rc = tobool(lowercase(strArg), val);
524  }
525  else
526  {
527  rc = tobool(strArg, val);
528  }
529  if( rc == OK )
530  {
531  cvt.b(val);
532  }
533  }
534  break;
535 
536  //
537  // Convert to integer.
538  //
539  case ArgTypeInteger:
540  {
541  long val;
542 
543  if( tolong(strArg, val) == OK )
544  {
545  cvt.i(val);
546  }
547  }
548  break;
549 
550  //
551  // Convert to floating-point number.
552  //
553  case ArgTypeFpn:
554  {
555  double val;
556 
557  if( todouble(strArg, val) == OK )
558  {
559  cvt.f(val);
560  }
561  }
562  break;
563 
564  //
565  // Unknown or uninitialized.
566  //
567  case ArgTypeUndef:
568  default:
569  break;
570  }
571 
572  return cvt;
573 }
574 
575 CmdArgDef::ArgType CmdArgDef::lookupArgType(const string strSymbol)
576 {
577  for(int i = 0; ArgTypeLookupTbl[i].m_sName != NULL; ++i)
578  {
579  if( strSymbol == ArgTypeLookupTbl[i].m_sName )
580  {
582  }
583  }
584  return CmdArgDef::ArgTypeUndef;
585 }
586 
587 const string CmdArgDef::lookupArgSymbol(const CmdArgDef::ArgType eType)
588 {
589  for(int i = 0; ArgTypeLookupTbl[i].m_sName != NULL; ++i)
590  {
591  if( eType == ArgTypeLookupTbl[i].m_nValue )
592  {
593  return ArgTypeLookupTbl[i].m_sName;
594  }
595  }
596  return undefstring;
597 }
598 
599 const string CmdArgDef::lookupFlagNames(const unsigned uFlags)
600 {
601  stringstream ss;
602  string sep;
603 
604  for(int i = 0; ArgFlagLookupTbl[i].m_sName != NULL; ++i)
605  {
606  if( uFlags & ArgFlagLookupTbl[i].m_nValue )
607  {
608  ss << sep << ArgFlagLookupTbl[i].m_sName;
609  sep = "|";
610  }
611  }
612  return ss.str();
613 }
614 
615 ostream &rnr::cmd::operator<<(ostream &os, const CmdArgDef &argdef)
616 {
617  os << indent() << "{" << endl;
618 
619  os << deltaindent(2);
620 
621  os << indent() << "cmduid = " << argdef.m_nCmdUid << endl;
622  os << indent() << "formindex = " << argdef.m_nFormIndex << endl;
623  os << indent() << "index = " << argdef.m_nIndex << endl;
624  os << indent() << "name = " << argdef.m_strName << endl;
625  os << indent() << "type = "
626  << argdef.m_eType << "("
627  << argdef.lookupArgSymbol(argdef.m_eType) << ")" << endl;
628  os << indent() << "flags = 0x"
629  << std::hex << argdef.m_uFlags << std::dec
630  << "(" << argdef.lookupFlagNames(argdef.m_uFlags) << ")" << endl;
631  os << indent() << "literals[" << argdef.m_literals.size() << "] = {"
632  << argdef.constructLiteralList(", ") << "}" << endl;
633  os << indent() << "ranges[" << argdef.m_ranges.size() << "] = ("
634  << argdef.constructRangeList(", ") << ")" << endl;
635  os << indent() << "regex = " << argdef.m_re << endl;
636 
637  os << deltaindent(-2);
638 
639  os << indent() << "}";
640 
641  return os;
642 }
643 
static const string noliteral
no literal string
Definition: CmdArgDef.cxx:95
const char *const ArgSymFpn
fpn (double)
Definition: CmdCore.h:128
Command line extended argument interface.
int tolong(const std::string &str, long &val)
Convert string to a long integer.
str::StringVec m_literals
literal argument valid values
Definition: CmdArgDef.h:445
Command EXTended ARGument class holding parsed command context and the raw and converted argmument va...
Definition: CmdExtArg.h:91
const std::string & s() const
Get the converted string value.
Definition: CmdExtArg.h:215
std::string constructLiteralList(const std::string sep=" ") const
Construct literal list string.
Definition: CmdArgDef.cxx:256
osManipIndent indent()
Left indent at current stream indentation level.
Definition: IOManip.cxx:115
static const std::string lookupFlagNames(const unsigned uFlags)
Look up argument flags, given flags value.
Definition: CmdArgDef.cxx:599
int tobool(const std::string &str, bool &val)
Convert string to boolean.
std::string lowercase(const std::string &str)
Convert in-place string to lower case.
const char *const ArgSymLiteral
Variable argument symbol names.
Definition: CmdCore.h:122
String.
Definition: StringTheory.h:78
int m_nCmdUid
parent command&#39;s unique id
Definition: CmdArgDef.h:440
Command argument compiled definition class.
Definition: CmdArgDef.h:89
long i() const
Get the converted integer value.
Definition: CmdExtArg.h:236
unsigned m_uFlags
argument modifiers
Definition: CmdArgDef.h:448
Regular Express Class.
Definition: RegEx.h:84
const int NoUid
Special values.
Definition: CmdCore.h:116
Of string spaces and their strangian operators.
const char *const ArgSymFile
file path
Definition: CmdCore.h:129
const char *const ArgSymMultiWord
any sequence
Definition: CmdCore.h:124
int todouble(const std::string &str, double &val)
Convert string to a double-precision floating-point number.
int m_nIndex
argument index
Definition: CmdArgDef.h:442
int m_nFormIndex
parent command&#39;s form index
Definition: CmdArgDef.h:441
const std::string undefstring
"undef" string
LogBook class interface.
static const std::string lookupArgSymbol(const CmdArgDef::ArgType eType)
Look up argument symbol, given argument type.
Definition: CmdArgDef.cxx:587
Commands.
Definition: CmdAddOns.h:81
bool b() const
Get the converted boolean value.
Definition: CmdExtArg.h:229
const char *const ArgSymIdentifier
identifier
Definition: CmdCore.h:125
const char * m_sName
name
Definition: CmdCore.h:175
const char *const ArgSymRegEx
regular expression
Definition: CmdCore.h:130
Stream I/O manipulators and helpers.
RegEx m_re
reg expression argument valid pattern
Definition: CmdArgDef.h:447
ArgType m_eType
argument type
Definition: CmdArgDef.h:444
double f() const
Get the converted floating-point number value.
Definition: CmdExtArg.h:243
std::string m_strName
argument name
Definition: CmdArgDef.h:443
ArgType
Argument type enumeration.
Definition: CmdArgDef.h:95
Command line argument definition class interface.
static NameValuePair ArgFlagLookupTbl[]
Argument flag modifiers - name lookup table.
Definition: CmdArgDef.cxx:117
Handy Dandy Name-Value Pair entry structure.
Definition: CmdCore.h:173
Command line core data types.
std::vector< range > RangeVec
vector of subranges
Definition: CmdArgDef.h:129
long e() const
Get the converted enumeration index value.
Definition: CmdExtArg.h:222
bool isIdentifier(const std::string &str)
Test if string is a valid identifier.
static NameValuePair ArgTypeLookupTbl[]
Argument type - string symbol lookup table.
Definition: CmdArgDef.cxx:100
osManipIndent deltaindent(const long nDelta)
Set relative delta indentation level.
Definition: IOManip.cxx:99
The Regular Expression Class interface.
std::string constructRangeList(const std::string sep=",") const
Construct ranges string.
Definition: CmdArgDef.cxx:270
const char *const ArgSymWord
non-whitespace seq
Definition: CmdCore.h:123
RoadNarrows Robotics.
Definition: Camera.h:74
std::ostream & operator<<(std::ostream &os, const timespec &obj)
A timespec insertion operator.
RangeVec m_ranges
numeric argument valid ranges
Definition: CmdArgDef.h:446
const char *const ArgSymBoolean
boolean (bool)
Definition: CmdCore.h:126
const char *const ArgSymInteger
integer (long)
Definition: CmdCore.h:127
Input/Output.
Definition: IOManip.h:77