appkit  1.5.1
RoadNarrows Robotics Application Kit
CommandLine.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: CommandLine.cxx
10 //
11 /*! \file
12  *
13  * \brief Command line interface 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 <sys/types.h>
57 #include <sys/select.h>
58 #include <stdio.h>
59 #include <unistd.h>
60 #include <stdlib.h>
61 #include <termios.h>
62 #include <ctype.h>
63 
64 #include <iostream>
65 #include <sstream>
66 #include <string>
67 #include <vector>
68 #include <map>
69 #include <set>
70 
71 #include "rnr/rnrconfig.h"
72 #include "rnr/log.h"
73 
74 #include "rnr/appkit/LogStream.h"
76 #include "rnr/appkit/IOManip.h"
77 #include "rnr/appkit/RegEx.h"
78 #include "rnr/appkit/LogBook.h"
79 #include "rnr/appkit/ReadLine.h"
80 #include "rnr/appkit/Token.h"
81 #include "rnr/appkit/CmdCore.h"
82 #include "rnr/appkit/CmdExtArg.h"
83 #include "rnr/appkit/CmdArgDef.h"
84 #include "rnr/appkit/CmdFormDef.h"
85 #include "rnr/appkit/CmdDef.h"
86 #include "rnr/appkit/CommandLine.h"
87 
88 using namespace std;
89 using namespace rnr;
90 using namespace rnr::str;
91 using namespace rnr::io;
92 using namespace rnr::cmd;
93 
94 // -----------------------------------------------------------------------------
95 // Private Implementation
96 // -----------------------------------------------------------------------------
97 
98 /*!
99  * \brief Debugging macros.
100  */
101 #undef CL_ENABLE_DEBUG ///< define to enable
102 
103 #ifdef CL_ENABLE_DEBUG
104 static int dbg_calldepth_ = -1;
105 
106 #define CL_CALL_DEPTH dbg_calldepth_
107 #define CL_SET_CALL_DEPTH(_n) dbg_calldepth_ = _n
108 #define CL_PUSH_CALL_DEPTH() ++dbg_calldepth_
109 #define CL_POP_CALL_DEPTH() --dbg_calldepth_
110 
111 /*!
112  * \brief Enter function debug macro.
113  * \param _args Stream of function arguments.
114  * \param _post Stream of post call data.
115  */
116 #define CL_DBG_CALL_IN(_args, _post) \
117 do \
118 { \
119  CL_PUSH_CALL_DEPTH(); \
120  cerr << space(dbg_calldepth_*2) << __func__ << "(" << _args << ")" <<_post; \
121 } while(0)
122 
123 /*!
124  * \brief Enter parse function debug macro.
125  * \param _cmddef Command definition.
126  * \param _formdef Form definition.
127  * \param _toks Input tokens.
128  * \param _pos Parse position in tokens.
129  * \param _post Stream of post call data.
130  */
131 #define CL_DBG_PARSE_CALL_IN(_cmddef, _formdef, _toks, _pos, _post) \
132  CL_DBG_CALL_IN("cmddef(uid=" << (_cmddef).getUid() \
133  << ", name=" << (_cmddef).getName() << ")" \
134  << ", form(index=" << (_formdef).getIndex() \
135  << ", argc=" << (_formdef).numOfArgs() << ")" \
136  << ", tokens=" << (_toks).size() \
137  << ", pos=" << (_pos), _post)
138 
139 /*!
140  * \brief Exit function debug macro with results on New Line.
141  * \param _res Results.
142  */
143 #define CL_DBG_CALL_OUT_NL(_res) \
144 do \
145 { \
146  cerr << space(dbg_calldepth_*2) << "--> (" << _res << ")" << endl; \
147  CL_POP_CALL_DEPTH(); \
148 } while(0)
149 
150 /*!
151  * \brief Exit function debug macro with results In Line.
152  * \param _res Results.
153  */
154 #define CL_DBG_CALL_OUT_IL(_res) \
155 do \
156 { \
157  cerr << "--> (" << _res << ")" << endl; \
158  CL_POP_CALL_DEPTH(); \
159 } while(0)
160 
161 /*!
162  * \brief Debug macro with no indentation.
163  * \param _data Any data stream.
164  */
165 #define CL_DBG(_data) cerr << _data
166 
167 #else
168 #define CL_CALL_DEPTH
169 #define CL_SET_CALL_DEPTH(_n)
170 #define CL_PUSH_CALL_DEPTH()
171 #define CL_POP_CALL_DEPTH()
172 #define CL_DBG_CALL_IN(_args, _post)
173 #define CL_DBG_PARSE_CALL_IN(_cmddef, _formdef, _toks, _pos, _post)
174 #define CL_DBG_CALL_OUT_NL(_res)
175 #define CL_DBG_CALL_OUT_IL(_res)
176 #define CL_DBG(os)
177 #endif // CL_ENABLE_DEBUG
178 
179 namespace rnr
180 {
181  namespace cmd
182  {
183  /*!
184  * \brief Command usage syntax special characters.
185  *
186  * \param c Character to test.
187  *
188  * \return Returns true if c is special, false otherwise.
189  */
190  static inline bool isspecial(int c)
191  {
192  return( (c == '<') || (c == ':') || (c == '>') ||
193  (c == '{') || (c == '|') || (c == '}') ||
194  (c == '[') || (c == ']') ||
195  (c == '(') || (c == ')') );
196  }
197 
198  /*!
199  * \brief Test if c is a double quote character.
200  */
201  static inline bool isdquote(int c)
202  {
203  return(c == '"');
204  }
205 
206  /*!
207  * \brief Test if c is a open parenthesis character.
208  */
209  static inline bool isoparen(int c)
210  {
211  return(c == '(');
212  }
213 
214  /*!
215  * \brief Test if c is a close parenthesis character.
216  */
217  static inline bool iscparen(int c)
218  {
219  return(c == ')');
220  }
221 
222  /*!
223  * \brief Testif c is an escape character.
224  */
225  static inline bool isesc(int c)
226  {
227  return(c == '\\');
228  }
229 
230  /*!
231  * \brief Convert ascii character to binary character value.
232  *
233  * \param c ASCII character.
234  *
235  * \return Binary character.
236  */
237  static char tohex(int c)
238  {
239  if( (c >= '0') && (c <= '9') )
240  {
241  return (char)(c - '0');
242  }
243  else if( (c >= 'a') && (c <= 'f') )
244  {
245  return (char)(c - 'a');
246  }
247  else if( (c >= 'A') && (c <= 'F') )
248  {
249  return (char)(c - 'A');
250  }
251  else
252  {
253  return (char)0;
254  }
255  }
256 
257  static CmdDef nocmddef; ///< "no cmd def" command definition
258  static const string noprompt; ///< "no prompt" prompt value
259 
260  } //namespace cmd
261 } // namespace rnr
262 
263 
264 // -----------------------------------------------------------------------------
265 // CmdExec Class
266 // -----------------------------------------------------------------------------
267 
268 int CmdExec::execute(const StringVec &argv)
269 {
270  switch( m_variant )
271  {
272  case Variant1:
273  return m_exec.fn1 != NULL? m_exec.fn1(argv): ENoExec;
274  case Variant2:
275  case Variant3:
276  default:
277  return ENoExec;
278  }
279 }
280 
281 int CmdExec::execute(CommandLine &cli, const CmdExtArgVec &argv)
282 {
283  switch( m_variant )
284  {
285  case Variant2:
286  return m_exec.fn2 != NULL? m_exec.fn2(argv): ENoExec;
287  case Variant3:
288  return m_exec.fn3 != NULL? m_exec.fn3(cli, argv): ENoExec;
289  case Variant1:
290  default:
291  return ENoExec;
292  }
293 }
294 
295 ostream &rnr::cmd::operator<<(ostream &os, const CmdExec &obj)
296 {
297  os << indent()
298  << "uid = " << obj.m_uid << ", "
299  << "variant = " << obj.m_variant << ", "
300  << "fn = " << obj.m_exec.p;
301 
302  return os;
303 }
304 
305 
306 // -----------------------------------------------------------------------------
307 // DataSect Class
308 // -----------------------------------------------------------------------------
309 
310 DataSect::DataSect()
311 {
312  m_pData = NULL;
313  m_fnDealloc = NULL;
314 }
315 
316 DataSect::DataSect(const string &ns, void *pData, DeallocFunc fn)
317 {
318  m_strNs = ns;
319  m_pData = pData;
320  m_fnDealloc = fn;
321 }
322 
323 //DataSect::DataSect(const DataSect &src)
324 //{
325 // m_strNs = src.m_strNs;
326 // m_pData = src.m_pData;
327 // m_fnDealloc = src.m_fnDealloc;
328 //}
329 
330 DataSect::~DataSect()
331 {
332  if( (m_pData != NULL) && (m_fnDealloc != NULL) )
333  {
334  m_fnDealloc(m_pData);
335  }
336 }
337 
338 void DataSect::set(const string &ns, void *pData, DeallocFunc fn)
339 {
340  m_strNs = ns;
341  m_pData = pData;
342  m_fnDealloc = fn;
343 }
344 
345 ostream &rnr::cmd::operator<<(ostream &os, const DataSect &obj)
346 {
347  os << indent()
348  << "namespace = " << obj.m_strNs << ", "
349  << "data = " << obj.m_pData << ", "
350  << "dealloc = " << (void *)obj.m_fnDealloc;
351 
352  return os;
353 }
354 
355 
356 // -----------------------------------------------------------------------------
357 // CommandLine Class
358 // -----------------------------------------------------------------------------
359 
360 //
361 // Logging bookmarks.
362 //
363 static const string markExec("ExecMark");
364 
365 //
366 // Logging common prefix strings.
367 //
368 static const string labelExec("Execute: ");
369 static const string labelNoExec("NoExec: ");
370 static const string labelCompile("Compile: ");
371 static const string labelParse("Parse: ");
372 static const string labelInput("Input: ");
373 static const string labelAt("At: ");
374 static const string labelSelect("Select: ");
375 static const string labelTry("Try: ");
376 static const string labelFit("Fitness: ");
377 static const string labelNoMatch("NoMatch: ");
378 static const string labelMatch("Match: ");
379 static const string labelSyntax("Error: ");
380 static const string labelFail("Failure: ");
381 static const string labelBlank(" ");
382 
383 CommandLine::CommandLine(const string strName,
384  const string strPrompt,
385  bool bUseRlLib,
386  bool bIgnoreCase) :
387  m_strName(strName),
388  m_bIgnoreCase(bIgnoreCase),
389  m_readline(strName, strPrompt, bUseRlLib),
390  m_log("Command Line Log", 50)
391 {
392  m_nUidCnt = 0;
393  m_bIsCompiled = false;
394 
396 
397  pushPrompt(strPrompt);
398 }
399 
401 {
402 }
403 
404 bool CommandLine::isDefined() const
405 {
406  return m_bIsCompiled;
407 }
408 
409 
410 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
411 // Command Addition and Compile Methods
412 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
413 
415 {
416  int uid;
417 
418  // command syntax must be defined
419  if( desc.syntax.empty() )
420  {
421  m_log << "No syntax forms specified." << eoe;
423  return NoUid;
424  }
425 
426  // add command
427  if( (uid = addCommand(desc.syntax)) != NoUid )
428  {
429  m_cmdDefs[uid].addHelp(desc.synopsis, desc.longdesc);
430  }
431 
432  return uid;
433 }
434 
436 {
437  int uid;
438 
439  // add command
440  if( (uid = addCommand(desc)) != NoUid )
441  {
442  m_cmdExecs[uid] = CmdExec(uid, fnExec);
443  }
444 
445  return uid;
446 }
447 
449 {
450  int uid;
451 
452  // add command
453  if( (uid = addCommand(desc)) != NoUid )
454  {
455  m_cmdExecs[uid] = CmdExec(uid, fnExec);
456  }
457 
458  return uid;
459 }
460 
462 {
463  int uid;
464 
465  // add command
466  if( (uid = addCommand(desc)) != NoUid )
467  {
468  m_cmdExecs[uid] = CmdExec(uid, fnExec);
469  }
470 
471  return uid;
472 }
473 
474 int CommandLine::addCommand(const string strSyntax)
475 {
476  StringVec syntaxForms;
477  string strName;
478  int uid;
479 
480  //
481  // Split command syntax forms along newlines.
482  //
483  split(strSyntax, '\n', syntaxForms);
484 
485  //
486  // No syntax forms.
487  //
488  if( syntaxForms.size() == 0 || syntaxForms[0].empty() )
489  {
490  m_log << "No syntax forms specified." << eoe;
492  return NoUid;
493  }
494 
495  //
496  // New command.
497  //
498  CmdDef newdef;
499 
500  // set command's unique id
501  newdef.setUid(m_nUidCnt++);
502 
503  uid = newdef.getUid();
504 
505  // add to map of commands
506  m_cmdDefs[uid] = newdef;
507 
508  // just added
509  CmdDef &cmddef = cmdAt(uid);
510 
511  //
512  // Add all syntax forms.
513  //
514  for(size_t i = 0; i < syntaxForms.size(); ++i)
515  {
516  CmdFormDef formdef(syntaxForms[i]);
517 
518  // add command form
519  cmddef.pushForm(formdef);
520  }
521 
522  // save raw syntax
523  cmddef.m_strSyntax = strSyntax;
524 
525  m_bIsCompiled = false;
526 
527  return uid;
528 }
529 
530 int CommandLine::addCommand(const string strSyntax, CmdExec1Func fnExec)
531 {
532  int uid;
533 
534  // add command
535  if( (uid = addCommand(strSyntax)) != NoUid )
536  {
537  m_cmdExecs[uid] = CmdExec(uid, fnExec);
538  }
539 
540  return uid;
541 }
542 
543 int CommandLine::addCommand(const string strSyntax, CmdExec2Func fnExec)
544 {
545  int uid;
546 
547  // add command
548  if( (uid = addCommand(strSyntax)) != NoUid )
549  {
550  m_cmdExecs[uid] = CmdExec(uid, fnExec);
551  }
552 
553  return uid;
554 }
555 
556 int CommandLine::addCommand(const string strSyntax, CmdExec3Func fnExec)
557 {
558  int uid;
559 
560  // add command
561  if( (uid = addCommand(strSyntax)) != NoUid )
562  {
563  m_cmdExecs[uid] = CmdExec(uid, fnExec);
564  }
565 
566  return uid;
567 }
568 
569 int CommandLine::removeCommand(const int uid)
570 {
571  CmdDefIter dpos; // definition position
572  CmdExecIter epos; // execution position
573  int rc; // return code
574 
575  if( uid == NoUid )
576  {
577  rc = EBadVal;
578  }
579  else if( (dpos = m_cmdDefs.find(uid)) == m_cmdDefs.end() )
580  {
581  rc = EBadVal;
582  }
583  else
584  {
585  // remove command definition
586  m_cmdDefs.erase(dpos);
587 
588  // remove any command execution function
589  if( (epos = m_cmdExecs.find(uid)) != m_cmdExecs.end() )
590  {
591  m_cmdExecs.erase(epos);
592  }
593 
594  if( m_cmdDefs.size() == 0 )
595  {
596  m_bIsCompiled = false;
597  }
598 
599  m_log << "Command " << uid << " removed." << eoe;
600  rc = AOk;
601  }
602 
603  if( rc != AOk )
604  {
605  m_log << "No command with uid " << uid << " found." << eoe;
607  }
608 
609  return rc;
610 }
611 
612 int CommandLine::removeCommand(const string &strCmd)
613 {
614  CmdDef &cmddef = cmdAt(strCmd);
615 
616  return removeCommand(cmddef.getUid());
617 }
618 
620 {
621  m_cmdDefs.clear();
622  m_bIsCompiled = false;
623  m_log << "All commands removed." << eoe;
624  return AOk;
625 }
626 
628 {
629  CmdDefIter iter;
630  int rc = AOk;
631 
632  CL_DBG_CALL_IN("", endl);
633 
634  m_bIsCompiled = false;
635 
636  m_log.clear();
637  m_log << bookmark(markExec) << labelExec << __func__ << eoe;
638 
639  //
640  // No commands added.
641  //
642  if( m_nUidCnt == 0 )
643  {
644  m_log << labelFail << "No commands added." << eoe;
646  rc = EError;
647  }
648 
649  //
650  // Compile each command.
651  //
652  for(iter = m_cmdDefs.begin();
653  (rc == AOk) && (iter != m_cmdDefs.end());
654  ++iter)
655  {
656  CmdDef &cmddef = iter->second;
657 
658  if( !cmddef.isDefined() )
659  {
660  rc = compile(cmddef);
661  }
662  }
663 
664  //
665  // Finalize compilation.
666  //
667  if( rc == AOk )
668  {
669  rc = finalize();
670  }
671 
672  //
673  // Build readline generator.
674  //
675  if( rc == AOk )
676  {
678  }
679 
680  //
681  // All good.
682  //
683  if( rc == AOk )
684  {
685  // register readline generator callback
687 
688  m_bIsCompiled = true;
689 
690  m_log.clear();
691  m_log << bookmark(markExec) << labelExec << __func__ << eoe;
692  m_log << labelCompile << "Compiled " << numOfCmds() << " commands." << eoe;
693  m_log << labelExec << "Ok" << eoe;
694  }
695 
696  CL_DBG_CALL_OUT_NL("rc=" << rc);
697 
698  return rc;
699 }
700 
702 {
703  TokenVec tokens;
704  ssize_t tokcnt;
705  int i;
706  int rc;
707 
708  CL_DBG_CALL_IN("cmddef.uid=" << cmddef.getUid(), " ***" << endl);
709 
710  m_log.clear();
711  m_log << bookmark(markExec) << labelExec << __func__ << eoe;
712  m_log << labelCompile << "cmddef " << cmddef.getUid()
713  << ": " << cmddef.getName() << eoe;
714 
715  // reset command definition to pre-compiled state
716  cmddef.reset();
717 
718  for(i = 0, rc = AOk; (i < cmddef.numOfForms()) && (rc == AOk); ++i)
719  {
720  CmdFormDef &form = cmddef.formAt(i);
721 
722  if( (tokcnt = tokenizeSyntax(form.getSyntax(), tokens)) < 0 )
723  {
724  rc = tokcnt;
725  }
726 
727  else if( tokcnt == 0 )
728  {
729  m_log << labelFail
730  << "cmddef " << cmddef.m_nUid << ", "
731  << "form " << form.getIndex() << ": No syntax specified." << eoe;
733  rc = EBadSyntax;
734  }
735 
736  else
737  {
738  rc = parseSyntax(cmddef, form, tokens);
739  }
740  }
741 
742  if( rc == AOk )
743  {
744  m_log << labelExec << "Ok" << eoe;
745  }
746  else
747  {
748  m_log << labelFail << "Could not compile." << eoe;
749  }
750 
751  CL_DBG_CALL_OUT_NL("rc=" << rc);
752  CL_DBG(endl);
753 
754  return rc;
755 }
756 
758 {
759  CmdDefIter iter, jter; // command definition iterators
760 
761  for(iter = m_cmdDefs.begin(); iter != m_cmdDefs.end(); ++iter)
762  {
763  CmdDef &cmddef_i = iter->second;
764 
765  jter = iter;
766 
767  for(++jter; jter != m_cmdDefs.end(); ++jter)
768  {
769  CmdDef &cmddef_j = jter->second;
770 
771  if( cmddef_i.getName() == cmddef_j.getName() )
772  {
773  m_log << labelSyntax << "Duplicate command names found at uid's "
774  << cmddef_i.getUid() << " and " << cmddef_j.getUid() << "."
775  << eoe;
777  return EAmbigCmd;
778  }
779  }
780  }
781 
782  return AOk;
783 }
784 
785 
786 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
787 // Command Line Data Section Methods
788 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
789 
790 int CommandLine::addDataSection(const string &ns,
791  void *pData,
793 {
794  DataSectIter pos = m_dataSects.find(ns);
795 
796  if( pos == m_dataSects.end() )
797  {
798  m_dataSects[ns] = DataSect(); // avoid double deletes
799  m_dataSects[ns].set(ns, pData, fn); // now assign
800  return AOk;
801  }
802  else
803  {
804  LOGERROR_STREAM("Data section '" << ns << "' already exist - cannot add.");
805  return EBadVal;
806  }
807 }
808 
809 int CommandLine::removeDataSection(const string &ns)
810 {
811  DataSectIter pos = m_dataSects.find(ns);
812 
813  if( pos != m_dataSects.end() )
814  {
815  LOGERROR_STREAM("No data section '" << ns << "' exist - cannot remove.");
816  return EBadVal;
817  }
818  else if( isReservedDataSection(pos->first) )
819  {
820  LOGERROR_STREAM("Reserved data section '" << ns << "' cannot be removed.");
821  return ENoExec;
822  }
823  else
824  {
825  m_dataSects.erase(pos);
826  return AOk;
827  }
828 }
829 
830 void *CommandLine::getDataSection(const string &ns)
831 {
832  DataSectIter pos = m_dataSects.find(ns);
833 
834  return pos != m_dataSects.end()? pos->second.data(): NULL;
835 }
836 
837 bool CommandLine::isReservedDataSection(const string &ns) const
838 {
839  return (ns == DataSectNsCore) || (ns == DataSectNsOS) ||
840  (ns == DataSectNsNet);
841 }
842 
843 
844 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
845 // Command Line Interface Methods
846 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
847 
848 int CommandLine::readCommand(FILE *fp, int &uid, int &iform, StringVec &argv)
849 {
850  CmdExtArgVec extargv;
851  int rc;
852 
853  argv.clear();
854 
855  uid = NoUid;
856  iform = NoIndex;
857 
858  if( ((rc = readCommand(fp, extargv)) == AOk) && (extargv.size() > 0) )
859  {
860  uid = extargv[0].uid();
861  iform = extargv[0].formIndex();
862 
863  toVec(extargv, argv);
864  }
865 
866  return rc;
867 }
868 
870 {
871  string strLine;
872  int rc;
873 
874  m_log.clear();
875  m_log << bookmark(markExec) << labelExec << __func__ << eoe;
876 
877  argv.clear();
878 
879  //
880  // Preliminary checks.
881  //
882  if( numOfCmds() == 0 )
883  {
884  m_log << labelNoExec << "No commands added to interface." << eoe;
886  return ENoExec;
887  }
888  else if( !isDefined() )
889  {
890  m_log << labelNoExec << "Commands not fully compiled." << eoe;
892  return ENoExec;
893  }
894 
895  //
896  // Read a line of input.
897  //
898  if( fileno(fp) == fileno(stdin) )
899  {
900  strLine = m_readline.rlreadLine();
901  }
902  else
903  {
904  strLine = m_readline.ireadLine(fp);
905  }
906 
907  m_log << labelInput << "stdin" << "[" << getLineNum() << "] "
908  << strLine << eoe;
909 
910  //
911  // Process input, match to best command.
912  //
913  if( (rc = processInput(strLine, argv)) == AOk )
914  {
915  m_log << labelExec << "Ok" << eoe;
916 
917  if( argv.size() > 0 )
918  {
919  LOGDIAG3_STREAM("Command "
920  << m_cmdDefs[argv[0].uid()].getName()
921  << "(" << argv[0].uid() << "), form "
922  << argv[0].formIndex() << " matched.");
923  }
924  }
925  else
926  {
927  m_log << labelFail << "Input not matched to any command." << eoe;
928 
929  LOGDIAG3("No command matched to input.");
930  }
931 
932  if( getBtEnable() )
933  {
934  backtrace(cerr);
935  }
936 
937  return rc;
938 }
939 
940 bool CommandLine::kbhit(FILE *fp)
941 {
942  int fd = fileno(fp);
943  struct termios ctio, ntio;
944  fd_set fdset;
945  struct timeval timeout;
946  bool bAvail;
947 
948  // get current file stream parameters
949  if( tcgetattr(fd, &ctio) < 0 )
950  {
951  return false;
952  }
953 
954  ntio = ctio;
955 
956  // set parameters for non-blocking
957  ntio.c_lflag &= ~ICANON;
958  ntio.c_lflag &= ~ECHO;
959  ntio.c_cc[VMIN] = 0;
960  ntio.c_cc[VTIME] = 0;
961 
962  // apply new parameter values
963  if( tcsetattr(fd, TCSANOW, &ntio) < 0 )
964  {
965  return false;
966  }
967 
968  // build select set
969  FD_ZERO(&fdset);
970  FD_SET(fd, &fdset);
971 
972  // timeout (gets munged after each select())
973  timeout.tv_sec = (time_t)0;
974  timeout.tv_usec = (time_t)10;
975 
976  bAvail = select(fd+1, &fdset, NULL, NULL, &timeout) > 0? true: false;
977 
978  // restore old parameters
979  tcsetattr(fd, TCSANOW, &ctio);
980 
981  return bAvail;
982 }
983 
985 {
986  int uid;
987  CmdExecIter pos;
988 
989  // no command nor arguments
990  if( argv.size() == 0 )
991  {
992  return EBadSyntax;
993  }
994 
995  // find the added command by name (the first argument)
996  CmdDef &cmd = cmdAt(argv[0]);
997 
998  // get the command's unique id
999  uid = cmd.getUid();
1000 
1001  // no command added with this uid
1002  if( uid == NoUid )
1003  {
1004  return EUnknownCmd;
1005  }
1006 
1007  // find the uid associated command execution function
1008  if( (pos = m_cmdExecs.find(uid)) == m_cmdExecs.end() )
1009  {
1010  // no execution function
1011  return EUnknownCmd;
1012  }
1013 
1014  // execute
1015  return pos->second.execute(argv);
1016 }
1017 
1019 {
1020  CmdExecIter pos;
1021 
1022  // no command nor arguments
1023  if( argv.size() == 0 )
1024  {
1025  return EBadSyntax;
1026  }
1027 
1028  // find the uid associated command execution function
1029  if( (pos = m_cmdExecs.find(argv[0].uid())) == m_cmdExecs.end() )
1030  {
1031  // no execution function
1032  return EUnknownCmd;
1033  }
1034 
1035  // execute
1036  return pos->second.execute(*this, argv);
1037 }
1038 
1040 {
1041  m_readline.addToHistory(str::c14n(argv));
1042 }
1043 
1045 {
1046  StringVec v;
1047 
1048  toVec(argv, v);
1049  m_readline.addToHistory(str::c14n(v));
1050 }
1051 
1052 void CommandLine::pushPrompt(const string &strPrompt)
1053 {
1054  m_prompts.push_back(strPrompt);
1055  m_readline.setPrompt(strPrompt);
1056 }
1057 
1059 {
1060  if( !m_prompts.empty() )
1061  {
1062  m_prompts.pop_back();
1064  }
1065  else
1066  {
1068  }
1069 }
1070 
1071 const string &CommandLine::getPrompt() const
1072 {
1073  if( !m_prompts.empty() )
1074  {
1075  return m_prompts.back();
1076  }
1077  else
1078  {
1079  return noprompt;
1080  }
1081 }
1082 
1083 int CommandLine::processInput(const string &strLine, CmdExtArgVec &argv)
1084 {
1085  TokenVec tokens; // tokenized input arguments
1086  int argc; // argument count
1087  int rc; // return code
1088 
1089  argv.clear();
1090 
1091  //
1092  // The line is empty - find out why?
1093  //
1094  if( strLine.empty() )
1095  {
1096  rc = checkReadResult();
1097  }
1098 
1099  //
1100  // Tokenize the input line.
1101  //
1102  else if( (argc = (int)tokenizeInput(strLine, tokens)) < 0 )
1103  {
1104  // bad token - error string already set
1105  rc = EBadSyntax;
1106  }
1107 
1108  //
1109  // Line full of whitespace - again, find out why?
1110  //
1111  else if( argc == 0 )
1112  {
1113  rc = checkReadResult();
1114  }
1115 
1116  //
1117  // Otherwise match the input arguments to the best form of the best fit
1118  // command definition.
1119  //
1120  else
1121  {
1122  rc = match(tokens, argv);
1123  }
1124 
1125  return rc;
1126 }
1127 
1128 int CommandLine::match(const TokenVec &tokens, CmdExtArgVec &argv)
1129 {
1130  CmdDefIter iter; // command definition iterator
1131 
1132  double fUltFitness; // best fitness value
1133  int uidUlt; // best command unique id
1134  int iformUlt; // best form index
1135 
1136  double fPenultFitness; // second best fitness value
1137  int uid2nd; // seconds best command unique id
1138  int iform2nd; // second best form index
1139 
1140  CmdExtArgVec argvCmd; // working command extended arguments
1141  double fFitness; // working fitness
1142  int cnt; // count of commands matching argv0
1143  int rc; // return code
1144 
1145  // This is considered a bug.
1146  if( tokens.size() == 0 )
1147  {
1148  LOGERROR("Bug: No tokens.");
1149  return EError;
1150  }
1151 
1152  argvCmd.clear();
1153 
1154  fPenultFitness = fUltFitness = 0.0;
1155  uid2nd = uidUlt = NoUid;
1156  iform2nd = iformUlt = NoIndex;
1157 
1158  cnt = 0;
1159 
1160  //
1161  // Find the best and second best command fits.
1162  //
1163  for(iter = m_cmdDefs.begin(); iter != m_cmdDefs.end(); ++iter)
1164  {
1165  rc = matchCommand(iter->second, tokens, argvCmd, fFitness);
1166 
1167  switch( rc )
1168  {
1169  case AOk: // complete match
1170  ++cnt;
1171  break;
1172  case EArgv0: // argv0 not matched
1173  break;
1174  default: // argv0 matched, but args failed to match
1175  ++cnt;
1176  continue;
1177  }
1178 
1179  // Found a new best.
1180  if( fFitness > fUltFitness )
1181  {
1182  fPenultFitness = fUltFitness;
1183  uid2nd = uidUlt;
1184  iform2nd = iformUlt;
1185 
1186  argv = argvCmd;
1187  fUltFitness = fFitness;
1188  uidUlt = iter->second.getUid();
1189  iformUlt = argv[0].formIndex();
1190  }
1191 
1192  // Found a new second best, but not a new best.
1193  else if( fFitness > fPenultFitness )
1194  {
1195  fPenultFitness = fFitness;
1196  uid2nd = iter->second.getUid();
1197  iform2nd = argvCmd[0].formIndex();
1198  }
1199  }
1200 
1201  //
1202  // No interface match.
1203  //
1204  if( uidUlt == NoUid )
1205  {
1206  if( cnt > 0 )
1207  {
1208  m_log << labelNoMatch << c14n(tokens) << eoe;
1209  }
1210  else
1211  {
1212  m_log << labelSyntax << "Command " << tokens[0] << " not found." << eoe;
1213  }
1214  rc = EUnknownCmd;
1215  }
1216 
1217  //
1218  // Duplicate fitness.
1219  //
1220  else if( fUltFitness == fPenultFitness )
1221  {
1222  m_log << labelNoMatch
1223  << "Command '" << tokens[0] << "' ambiguous. Matches:"
1224  << eoe;
1225  m_log << labelBlank << cmdAt(uidUlt).formAt(iformUlt).getSyntax() << eoe;
1226  m_log << labelBlank << cmdAt(uid2nd).formAt(iform2nd).getSyntax() << eoe;
1227  rc = EAmbigCmd;
1228  }
1229 
1230  //
1231  // Got a unambiguous, matched command.
1232  //
1233  else
1234  {
1235  m_log << labelSelect << cmdAt(uidUlt).formAt(iformUlt).getSyntax() << eoe;
1236  rc = AOk;
1237  }
1238 
1239  return rc;
1240 }
1241 
1243  const TokenVec &tokens,
1244  CmdExtArgVec &argv,
1245  double &fFitness)
1246 {
1247  CmdExtArgVec argvForm; // working form extended arguments
1248  double fMaxFitness; // working and best form fitness
1249  int iBest; // best form index
1250  int i; // working index
1251  int rc; // return code
1252 
1253  // This is considered a bug.
1254  if( tokens.size() == 0 )
1255  {
1256  LOGERROR("Bug: No tokens.");
1257  return EError;
1258  }
1259 
1260  argv.clear();
1261 
1262  fFitness = 0.0;
1263  fMaxFitness = 0.0;
1264  iBest = NoIndex;
1265 
1266  //
1267  // If argv0 doesn't match, don't try to apply matching algorithm to this
1268  // command.
1269  //
1270  // Note: All command forms must have the same argv0 argument type and value.
1271  //
1272  if( cmddef.at(0).at(0).match(tokens[0].value()) == 0.0 )
1273  {
1274  return EArgv0;
1275  }
1276 
1277  for(i = 0; i < cmddef.numOfForms(); ++i)
1278  {
1279  const CmdFormDef &form = cmddef.at(i);
1280 
1281  m_log << labelTry << form.getSyntax() << eoe;
1282 
1283  if( (rc = matchCommandForm(form, tokens, argvForm, fFitness)) == AOk )
1284  {
1285  if( fFitness > fMaxFitness )
1286  {
1287  argv = argvForm;
1288  fMaxFitness = fFitness;
1289  iBest = i;
1290  }
1291  }
1292 
1293  m_log << labelFit << fFitness << eoe;
1294 
1295  if( fMaxFitness >= 1.0 )
1296  {
1297  break;
1298  }
1299  }
1300 
1301  //
1302  // Found the best command form that matches the input.
1303  //
1304  if( iBest >= 0 )
1305  {
1306  m_log << labelMatch
1307  << "Command '" << cmddef.getName()
1308  << "', form " << iBest << ": Fitness " << fMaxFitness
1309  << eoe;
1310  fFitness = fMaxFitness;
1311  rc = AOk;
1312  }
1313 
1314  //
1315  // No fit.
1316  //
1317  else
1318  {
1319  m_log << labelNoMatch
1320  << "Command '" << cmddef.getName()
1321  << "', all forms."
1322  << eoe;
1323  rc = EBadSyntax;
1324  }
1325 
1326  return rc;
1327 }
1328 
1330  const TokenVec &tokens,
1331  CmdExtArgVec &argv,
1332  double &fFitness)
1333 {
1334  int argcToks = (int)tokens.size(); // number of input tokens
1335  int argcForm = form.numOfArgs(); // form total number of arguments
1336  double fWeight, fDecay; // match weight and decay multiplier
1337  int iArg; // working form argument index
1338  int iTok; // working input tokens index
1339  int rc; // return code
1340 
1341  argv.clear();
1342 
1343  fFitness = 0.0;
1344  fDecay = 1.0;
1345 
1346  // command name (for logging)
1347  const string &strCmdName = m_cmdDefs[form.getParentCmdUid()].getName();
1348 
1349  //
1350  // Too many input arguments.
1351  //
1352  if( argcToks > argcForm )
1353  {
1354  m_log << labelNoMatch
1355  << "Command '" << strCmdName << "', "
1356  << "form " << form.getIndex() << ": "
1357  << "Too many arguments: "
1358  << argcToks << " specified, "
1359  << argcForm << " maximum."
1360  << eoe;
1361  return EBadSyntax;
1362  }
1363 
1364  //
1365  // Missing required input arguments.
1366  //
1367  else if( argcToks < form.numOfRequiredArgs() )
1368  {
1369  m_log << labelNoMatch
1370  << "Command '" << strCmdName << "', "
1371  << "form " << form.getIndex() << ": "
1372  << "Missing required arguments: "
1373  << argcToks << " specified, "
1374  << form.numOfRequiredArgs() << " required."
1375  << eoe;
1376  return EBadSyntax;
1377  }
1378 
1379  //
1380  // Match input arguments to form syntax.
1381  //
1382  for(iArg = 0, iTok = 0, rc = AOk;
1383  (iArg < argcForm) && (iTok < argcToks) && (rc == AOk);
1384  ++iArg)
1385  {
1386  const CmdArgDef &argdef = form.at(iArg);
1387 
1388  // try to match input token to argument sytax
1389  fWeight = argdef.match(tokens[iTok].value(), m_bIgnoreCase);
1390 
1391  // no match
1392  if( fWeight == 0.0 )
1393  {
1394  rc = EBadSyntax;
1395 
1396  // start log entry
1397  m_log << labelNoMatch
1398  << "Input argument " << tokens[iTok] << " doesn't match "
1399  << "command '" << strCmdName << "', "
1400  << "form " << form.getIndex() << ", "
1401  << (argdef.isOptional()? "(opt)": "")
1402  << "arg " << argdef.getIndex()
1403  << " definition."
1404  << eoe;
1405 
1406  //
1407  // Log reason of match failure.
1408  //
1409  switch( argdef.getType() )
1410  {
1412  m_log << labelBlank << " Not one of: "
1413  << argdef.constructLiteralList() << "."
1414  << eoe;
1415  break;
1416 
1422  m_log << labelBlank << " Not a "
1423  << "'" << CmdArgDef::lookupArgSymbol(argdef.getType())
1424  << "' type." << eoe;
1425  break;
1426 
1428  case CmdArgDef::ArgTypeFpn:
1429  m_log << labelBlank << " Not a "
1430  << "'" << CmdArgDef::lookupArgSymbol(argdef.getType())
1431  << "' type";
1432  if( argdef.getRanges().size() > 0 )
1433  {
1434  m_log << " or out of range " << argdef.constructRangeList();
1435  }
1436  m_log << "." << eoe;
1437  break;
1438 
1440  m_log << labelBlank << " Failed to match re: "
1441  << "'" << argdef.getRegEx() << "'."
1442  << eoe;
1443  break;
1444 
1446  default:
1447  LOGERROR("Bug: Unknown type %d.", argdef.getType());
1448  rc = EError;
1449  break;
1450  }
1451  }
1452 
1453  //
1454  // Good argument match.
1455  //
1456  if( rc == AOk )
1457  {
1458  m_log << labelMatch
1459  << "Input argument " << tokens[iTok] << " matches "
1460  << "cmd '" << strCmdName << "', "
1461  << "form " << form.getIndex() << ", "
1462  << "arg " << argdef.getIndex() << " "
1463  << "definition."
1464  << eoe;
1465 
1466  argv.push_back(argdef.convert(tokens[iTok].value(), m_bIgnoreCase));
1467 
1468  // don't decay perfection
1469  if( fWeight == 1.0 )
1470  {
1471  fFitness += fWeight;
1472  }
1473  else
1474  {
1475  fFitness += fWeight * fDecay;
1476  }
1477 
1478  fDecay *= 0.9;
1479 
1480  ++iTok;
1481  }
1482 
1483  //
1484  // Bad match, but argument is optional, and there are more optionals.
1485  //
1486  else if( argdef.isOptional() && (iArg < argcForm-1) )
1487  {
1488  rc = AOk;
1489  }
1490 
1491  //
1492  // Bad match.
1493  //
1494  else
1495  {
1496  fFitness = 0.0;
1497  }
1498  }
1499 
1500  // normalize fitness
1501  fFitness /= (double)argcToks;
1502 
1503  return rc;
1504 }
1505 
1507 {
1508  // EOF
1509  if( m_readline.isEoF() )
1510  {
1511  m_log << "EOF" << eoe;
1512  return EEoF;
1513  }
1514 
1515  // file read error
1516  else if( m_readline.isFError() )
1517  {
1518  m_log << m_readline.getErrorStr() << eoe;
1519  return ERead;
1520  }
1521 
1522  // read successful
1523  else
1524  {
1525  return AOk;
1526  }
1527 }
1528 
1530 {
1531  for(size_t i = 0; i < v1.size(); ++i)
1532  {
1533  v2.push_back(v1[i].arg());
1534  }
1535 }
1536 
1537 
1538 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1539 // Command Info and Argument Access Methods
1540 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1541 
1542 int CommandLine::numOfArgs(int uid, int iform) const
1543 {
1544  return at(uid).at(iform).numOfArgs();
1545 }
1546 
1547 int CommandLine::numOfArgs(const CmdExtArg &arg) const
1548 {
1549  return at(arg.uid()).at(arg.formIndex()).numOfArgs();
1550 }
1551 
1552 int CommandLine::numOfRequiredArgs(int uid, int iform) const
1553 {
1554  return at(uid).at(iform).numOfRequiredArgs();
1555 }
1556 
1558 {
1559  return at(arg.uid()).at(arg.formIndex()).numOfRequiredArgs();
1560 }
1561 
1562 int CommandLine::numOfOptionalArgs(int uid, int iform) const
1563 {
1564  return at(uid).at(iform).numOfOptionalArgs();
1565 }
1566 
1568 {
1569  return at(arg.uid()).at(arg.formIndex()).numOfOptionalArgs();
1570 }
1571 
1572 const string &CommandLine::getArgName(const CmdExtArg &arg) const
1573 {
1574  // all terrain armored transport, of course
1575  const CmdArgDef &argdef= at(arg.uid()).at(arg.formIndex()).at(arg.argIndex());
1576 
1577  return argdef.getName();
1578 }
1579 
1581 {
1582  const CmdArgDef &argdef= at(arg.uid()).at(arg.formIndex()).at(arg.argIndex());
1583 
1584  return argdef.getType();
1585 }
1586 
1587 
1588 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1589 // ReadLine Generator Methods
1590 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1591 
1593 {
1594  return AOk;
1595 }
1596 
1597 const string CommandLine::rlGeneratorWrapper(void *pAppArg,
1598  const string &strText,
1599  int nIndex,
1600  const string &strContext,
1601  int nStart,
1602  int nEnd,
1603  unsigned &uFlags)
1604 {
1605  if( pAppArg != NULL )
1606  {
1607  return ((CommandLine *)pAppArg)->rlGenerator(strText, nIndex, strContext,
1608  nStart, nEnd, uFlags);
1609  }
1610  else
1611  {
1612  return emptystring;
1613  }
1614 }
1615 
1616 const string CommandLine::rlGenerator(const string &strText,
1617  int nIndex,
1618  const string &strContext,
1619  int nStart,
1620  int nEnd,
1621  unsigned &uFlags)
1622 {
1623  static vector<CmdArgDef*> argdefs;
1624 
1625  StringVec tabList;
1626  size_t i;
1627 
1628  //cerr << "{text=" << strText
1629  // << ", index=" << nIndex
1630  // << ", context=" << strContext
1631  // << ", start=" << nStart
1632  // << ", end=" << nEnd
1633  // << "}" << endl;
1634 
1635  if( nIndex == 0 )
1636  {
1637  //cerr << "rebuild args" << endl;
1638  argdefs.clear();
1639  rlArgDefs(strContext.substr(0, nStart), argdefs);
1640  }
1641 
1642  rlTabList(strText, argdefs, tabList, uFlags);
1643 
1644  if( nIndex < tabList.size() )
1645  {
1646  return tabList[nIndex];
1647  }
1648  else
1649  {
1650  return RlTabEnd;
1651  }
1652 }
1653 
1654 void CommandLine::rlArgDefs(const string &strSubtext,
1655  vector<CmdArgDef*> &argdefs)
1656 {
1657  CmdDefIter iter; // command definition iterator
1658  vector<CmdArgDef*> v[2]; // work swap vectors of argument definitions
1659  int tog0, tog1; // vector toggle source/destination indices
1660  StringVec tokens; // input tokens
1661  size_t argc; // token working argument count
1662  size_t i, j; // working indices
1663 
1664  argdefs.clear();
1665 
1666  tokenizeInput(strSubtext, tokens);
1667 
1668  tog0 = 0, tog1 = 1;
1669 
1670  //
1671  // Seed with argv0 (command).
1672  //
1673  for(iter = m_cmdDefs.begin(); iter != m_cmdDefs.end(); ++iter)
1674  {
1675  for(j = 0; j < iter->second.numOfForms(); ++j)
1676  {
1677  CmdFormDef &form = iter->second.formAt(j);
1678  v[tog0].push_back(&form.argAt(0));
1679  }
1680  }
1681 
1682  //
1683  // Walk across token arguments, paring unmatched argument defs from list.
1684  //
1685  for(argc = 0; argc < tokens.size(); ++argc)
1686  {
1687  v[tog1].clear();
1688 
1689  for(i = 0; i < v[tog0].size(); ++i)
1690  {
1691  CmdArgDef *p = v[tog0][i];
1692 
1693  if( p->match(tokens[argc], m_bIgnoreCase) > 0.0 )
1694  {
1695  CmdFormDef &form =
1696  m_cmdDefs[p->getParentCmdUid()].formAt(p->getParentFormIndex());
1697 
1698  if( argc+1 < form.numOfArgs() )
1699  {
1700  v[tog1].push_back(&form.argAt(argc+1));
1701  }
1702  }
1703  }
1704 
1705  tog0 = tog1;
1706  tog1 = (tog1 + 1) % 2;
1707  }
1708 
1709  // copy to interface parameter
1710  for(i = 0; i < v[tog0].size(); ++i)
1711  {
1712  argdefs.push_back(v[tog0][i]);
1713  }
1714 }
1715 
1716 void CommandLine::rlTabList(const string &strText,
1717  vector<CmdArgDef*> &argdefs,
1718  StringVec &tabList,
1719  unsigned &uFlags)
1720 {
1721  size_t len; // length of input text
1722  set<string> A; // set with unique keys == values
1723  set<string>::iterator iter, jter; // bidirectional set iterators
1724  string str; // working string
1725  size_t i, j, min; // working indices, minimum
1726 
1727  // clear TAB list of all existing candidates
1728  tabList.clear();
1729 
1730  // no filename completion
1731  uFlags = ReadLine::FlagTabNoFilename;
1732 
1733  // nothing to TAB complete
1734  if( argdefs.size() == 0 )
1735  {
1736  return;
1737  }
1738 
1739  // length of partial text to complete
1740  len = strText.size();
1741 
1742  //
1743  // Build tab completion super list.
1744  //
1745  // Note: Sets automatically handle duplicates.
1746  //
1747  for(i = 0; i < argdefs.size(); ++i)
1748  {
1749  switch( argdefs[i]->getType() )
1750  {
1751  // Find all literals that partially match text.
1753  for(j = 0; j < argdefs[i]->numOfLiterals(); ++j)
1754  {
1755  str = argdefs[i]->literalAt(j);
1756 
1757  if( rlPartialMatch(strText, str, len) )
1758  {
1759  A.insert(str);
1760  }
1761  }
1762  break;
1763 
1765  for(j = 0; FalseHood[j] != NULL; ++j)
1766  {
1767  if( rlPartialMatch(strText, FalseHood[j], len) )
1768  {
1769  // bit of hack. don't want 'false' and 'f' in TAB complition list
1770  if( FalseHood[j] != "f" )
1771  {
1772  A.insert(FalseHood[j]);
1773  }
1774  }
1775  }
1776  for(j = 0; TruthHood[j] != NULL; ++j)
1777  {
1778  if( rlPartialMatch(strText, TruthHood[j], len) )
1779  {
1780  // bit of hack. don't want 'true' and 't' in TAB complition list
1781  if( TruthHood[j] != "t" )
1782  {
1783  A.insert(TruthHood[j]);
1784  }
1785  }
1786  }
1787  break;
1788 
1789  // Argument types not posible to TAB complete, provide syntax if no text.
1794  case CmdArgDef::ArgTypeFpn:
1796  if( len == 0 )
1797  {
1798  A.insert(argdefs[i]->constructSyntax());
1799  uFlags |= ReadLine::FlagTabNoDefault;
1800  }
1801  break;
1802 
1803  // Filename argument type, allow filename completion.
1805  uFlags &= ~ReadLine::FlagTabNoFilename;
1806  break;
1807 
1808  // Unknown.
1810  default:
1811  break;
1812  }
1813  }
1814 
1815  //
1816  // Find the greatest common prefix substring.
1817  //
1818  if( !(uFlags & ReadLine::FlagTabNoDefault) && (A.size() > 1) )
1819  {
1820  jter = A.begin();
1821  iter = jter++;
1822 
1823  size_t min = iter->size();
1824 
1825  for(; (jter != A.end()) && (min > 0); ++iter, ++jter)
1826  {
1827  j = gcss(*iter, *jter);
1828 
1829  if( j < min )
1830  {
1831  min = j;
1832  }
1833  }
1834 
1835  // Common substring is longer than text. This is the only default TAB entry.
1836  if( min > len )
1837  {
1838  str = *A.begin();
1839  A.clear();
1840  A.insert(str.substr(0, min));
1841  uFlags |= ReadLine::FlagTabNoSpace;
1842  }
1843  }
1844 
1845  //
1846  // Copy set to vector list
1847  //
1848  for(iter = A.begin(); iter != A.end(); ++iter)
1849  {
1850  tabList.push_back(*iter);
1851  }
1852 
1853  // Disable default if multiple unique matches.
1854  if( tabList.size() > 1 )
1855  {
1856  uFlags |= ReadLine::FlagTabNoDefault;
1857  }
1858 }
1859 
1860 bool CommandLine::rlPartialMatch(const string &strText,
1861  const string strLiteral,
1862  size_t uLen)
1863 {
1864  if( m_bIgnoreCase )
1865  {
1866  return lowercase(strText) == lowercase(strLiteral.substr(0, uLen));
1867  }
1868  else
1869  {
1870  return strText == strLiteral.substr(0, uLen);
1871  }
1872 }
1873 
1874 
1875 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1876 // Attribute and Data Access Methods
1877 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1878 
1879 bool CommandLine::hasCmd(const int uid) const
1880 {
1881  return m_cmdDefs.find(uid) != m_cmdDefs.end();
1882 }
1883 
1884 bool CommandLine::hasCmd(const string &strName) const
1885 {
1886  return at(strName).isDefined();
1887 }
1888 
1889 const CmdDef &CommandLine::at(const int uid) const
1890 {
1891  CmdDefCIter pos;
1892 
1893  if( (pos = m_cmdDefs.find(uid)) != m_cmdDefs.end() )
1894  {
1895  return pos->second;
1896  }
1897  else
1898  {
1899  return nocmddef;
1900  }
1901 }
1902 
1903 const CmdDef &CommandLine::at(const string &strName) const
1904 {
1905  CmdDefCIter iter;
1906 
1907  for(iter = m_cmdDefs.begin(); iter != m_cmdDefs.end(); ++iter)
1908  {
1909  if( iter->second.getName() == strName )
1910  {
1911  return iter->second;
1912  }
1913  }
1914 
1915  return nocmddef;
1916 }
1917 
1919 {
1920  CmdDefIter pos;
1921 
1922  if( (pos = m_cmdDefs.find(uid)) != m_cmdDefs.end() )
1923  {
1924  return pos->second;
1925  }
1926  else
1927  {
1928  return nocmddef;
1929  }
1930 }
1931 
1932 CmdDef &CommandLine::cmdAt(const string &strName)
1933 {
1934  CmdDefIter iter;
1935 
1936  for(iter = m_cmdDefs.begin(); iter != m_cmdDefs.end(); ++iter)
1937  {
1938  if( iter->second.getName() == strName )
1939  {
1940  return iter->second;
1941  }
1942  }
1943 
1944  return nocmddef;
1945 }
1946 
1947 const string &CommandLine::getErrorStr() const
1948 {
1949  return m_log.lastText();
1950 }
1951 
1952 ostream &CommandLine::backtrace(ostream &os, const bool bAll) const
1953 {
1954  if( bAll )
1955  {
1956  return m_log.printLog(os);
1957  }
1958  else
1959  {
1960  return m_log.printToMark(os, markExec, LogBook::NEWEST);
1961  }
1962 }
1963 
1964 
1965 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1966 // Lexical Analyzer Methods
1967 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1968 
1969 ssize_t CommandLine::tokenizeInput(const string &strInput, TokenVec &tokens)
1970 {
1971  size_t len; // length of input string
1972  ssize_t cursor; // cursor character position along input string
1973  size_t start; // start character position of token in input string
1974 
1975  CL_DBG_CALL_IN("\"" << strInput << "\"", endl);
1976 
1977  tokens.clear();
1978 
1979  len = strInput.length();
1980 
1981  for(cursor = 0; (cursor >= 0) && (cursor < len); )
1982  {
1983  // find start of the next token
1984  while( (cursor < len) && isspace((int)strInput[cursor]) )
1985  {
1986  ++cursor;
1987  }
1988 
1989  // no more tokens
1990  if( cursor >= len )
1991  {
1992  break;
1993  }
1994 
1995  // new qouted string token
1996  if( isdquote(strInput[cursor]) )
1997  {
1998  cursor = lexQuotedString(strInput, cursor, tokens);
1999  }
2000 
2001  // new contiguous word token
2002  else
2003  {
2004  cursor = lexWord(strInput, cursor, tokens);
2005  }
2006  }
2007 
2008 #ifdef CL_ENABLE_DEBUG
2009  {
2010  stringstream ss;
2011  string sep;
2012  for(size_t i = 0; i < tokens.size(); ++i)
2013  {
2014  ss << sep << tokens[i];
2015  sep = ", ";
2016  }
2017  CL_DBG_CALL_OUT_NL(ss.str());
2018  }
2019 #endif // CL_ENABLE_DEBUG
2020 
2021  return cursor >= 0? (ssize_t)tokens.size(): cursor;
2022 }
2023 
2024 ssize_t CommandLine::tokenizeInput(const string &strInput, StringVec &tokens)
2025 {
2026  TokenVec tokenz;
2027  ssize_t cnt;
2028  ssize_t i;
2029 
2030  tokens.clear();
2031 
2032  cnt = tokenizeInput(strInput, tokenz);
2033 
2034  for(i = 0; i < cnt; ++i)
2035  {
2036  tokens.push_back(tokenz[i].value());
2037  }
2038 
2039  return cnt;
2040 }
2041 
2042 ssize_t CommandLine::tokenizeSyntax(const string &strSyntax, TokenVec &tokens)
2043 {
2044  size_t len; // length of syntax string
2045  ssize_t cursor; // cursor character position along syntax string
2046  size_t start; // start character position of token in syntax string
2047 
2048  CL_DBG_CALL_IN("\"" << strSyntax << "\", tokens", endl);
2049 
2050  tokens.clear();
2051 
2052  len = strSyntax.length();
2053 
2054  for(cursor = 0; (cursor >= 0) && (cursor < len); )
2055  {
2056  // find start of the next token
2057  while( (cursor < len) && isspace((int)strSyntax[cursor]) )
2058  {
2059  ++cursor;
2060  }
2061 
2062  // no more tokens
2063  if( cursor >= len )
2064  {
2065  break;
2066  }
2067 
2068  // new parenthetical expression
2069  if( isoparen(strSyntax[cursor]) )
2070  {
2071  cursor = lexSyntaxParenExpr(strSyntax, cursor, tokens);
2072  }
2073 
2074  // special character token
2075  else if( isspecial(strSyntax[cursor]) )
2076  {
2077  start = cursor++;
2078  pushToken(strSyntax, start, cursor, tokens);
2079  }
2080 
2081  // new contiguous word token
2082  else
2083  {
2084  cursor = lexSyntaxWord(strSyntax, cursor, tokens);
2085  }
2086  }
2087 
2088 #ifdef CL_ENABLE_DEBUG
2089  {
2090  stringstream ss;
2091  string sep;
2092  for(size_t i = 0; i < tokens.size(); ++i)
2093  {
2094  ss << sep << tokens[i];
2095  sep = ", ";
2096  }
2097  CL_DBG_CALL_OUT_NL(ss.str());
2098  }
2099 #endif // CL_ENABLE_DEBUG
2100 
2101  return cursor >= 0? (ssize_t)tokens.size(): cursor;
2102 }
2103 
2104 ssize_t CommandLine::lexSyntaxWord(const string &strSyntax,
2105  ssize_t cursor,
2106  TokenVec &tokens)
2107 {
2108  size_t len;
2109  size_t start;
2110 
2111  len = strSyntax.length();
2112  start = cursor;
2113 
2114  // find end of word
2115  while( (cursor < len) &&
2116  !isspace((int)strSyntax[cursor]) &&
2117  !isspecial(strSyntax[cursor]) )
2118  {
2119  ++cursor;
2120  }
2121 
2122  if( cursor > start )
2123  {
2124  pushToken(strSyntax, start, cursor, tokens);
2125  }
2126  else
2127  {
2128  logLexToken(strSyntax, start, cursor, tokens);
2129  m_log << labelSyntax << "No <word> found." << eoe;
2131  cursor = EBadSyntax;
2132  }
2133 
2134  return cursor;
2135 }
2136 
2137 ssize_t CommandLine::lexSyntaxParenExpr(const string &strSyntax,
2138  ssize_t cursor,
2139  TokenVec &tokens)
2140 {
2141  size_t len; // length of input string;
2142  size_t start; // start character position of token in input string
2143  int pdepth; // parenthesis depth
2144  bool escseq; // is [not] in an escape sequence
2145  bool eop; // is [not] at end of parenthetical
2146  char c; // working character
2147  string value; // working token value
2148 
2149  len = strSyntax.length();
2150  start = cursor;
2151  pdepth = 0;
2152  escseq = false;
2153  eop = false;
2154 
2155  if( isoparen(strSyntax[cursor]) )
2156  {
2157  // '(' token
2158  start = cursor++;
2159  pushToken(strSyntax, start, cursor, tokens);
2160 
2161  start = cursor;
2162  ++pdepth;
2163  }
2164  else
2165  {
2166  logLexToken(strSyntax, start, cursor, tokens);
2167  m_log << labelSyntax << "No starting '(' found." << eoe;
2169  return EBadSyntax;
2170  }
2171 
2172  while( (cursor >= 0) && (cursor < len) && !eop )
2173  {
2174  switch( strSyntax[cursor] )
2175  {
2176  case '\\':
2177  escseq = true;
2178  break;
2179  case '(':
2180  if( !escseq )
2181  {
2182  ++pdepth;
2183  }
2184  escseq = false;
2185  break;
2186  case ')':
2187  if( !escseq )
2188  {
2189  if( --pdepth == 0 )
2190  {
2191  eop = true;
2192  }
2193  }
2194  escseq = false;
2195  break;
2196  default:
2197  escseq = false;
2198  break;
2199  }
2200 
2201  if( !eop )
2202  {
2203  value.push_back(strSyntax[cursor++]);
2204  }
2205  }
2206 
2207  if( iscparen(strSyntax[cursor]) )
2208  {
2209  // expression token
2210  pushToken(strSyntax, start, cursor, tokens);
2211 
2212  // ')' token
2213  start = cursor++;
2214  pushToken(strSyntax, start, cursor, tokens);
2215 
2216  return cursor;
2217  }
2218  else
2219  {
2220  logLexToken(strSyntax, start, cursor, tokens);
2221  m_log << labelSyntax << "No ending ')' found." << eoe;
2223  return EBadSyntax;
2224  }
2225 }
2226 
2227 ssize_t CommandLine::lexWord(const string &strInput,
2228  ssize_t cursor,
2229  TokenVec &tokens)
2230 {
2231  size_t len;
2232  size_t start;
2233 
2234  len = strInput.length();
2235  start = cursor;
2236 
2237  while( (cursor < len) && !isspace((int)strInput[cursor]) )
2238  {
2239  ++cursor;
2240  }
2241 
2242  if( cursor > start )
2243  {
2244  pushToken(strInput, start, cursor, tokens);
2245  }
2246  else
2247  {
2248  logLexToken(strInput, start, cursor, tokens);
2249  m_log << labelSyntax << "No <word> found." << eoe;
2250  cursor = EBadSyntax;
2251  }
2252 
2253  return cursor;
2254 }
2255 
2256 ssize_t CommandLine::lexQuotedString(const string &strInput,
2257  ssize_t cursor,
2258  TokenVec &tokens)
2259 {
2260  size_t len; // length of input string;
2261  size_t start; // start character position of token in input string
2262  bool escseq; // is [not] in an escape sequence
2263  bool eos; // is [not] at end of string
2264  char c; // working converted character
2265  string value; // working token value
2266 
2267  len = strInput.length();
2268 
2269  if( isdquote(strInput[cursor]) )
2270  {
2271  ++cursor;
2272  start = cursor;
2273  }
2274  else
2275  {
2276  logLexToken(strInput, cursor, cursor, tokens);
2277  m_log << labelSyntax << "No starting double qoute '\"' found." << eoe;
2278  return EBadSyntax;
2279  }
2280 
2281  escseq = false;
2282  eos = false;
2283 
2284  while( (cursor >= 0) && (cursor < len) && !eos )
2285  {
2286  // escape sequence
2287  if( escseq )
2288  {
2289  switch( strInput[cursor] )
2290  {
2291  case 't': // tab
2292  c = '\t';
2293  break;
2294  case 'n': // newline
2295  c = '\n';
2296  break;
2297  case 'r': // carriage return
2298  c = '\r';
2299  break;
2300  case 'v': // vertical tab
2301  c = '\v';
2302  break;
2303  case 'f': // formfeed
2304  c = '\f';
2305  break;
2306  case 'x': // h[h]
2307  {
2308  int i = cursor + 1;
2309  if( (i < len) && isxdigit(strInput[i]) )
2310  {
2311  c = tohex(strInput[i]);
2312  ++cursor;
2313  ++i;
2314  if( (i < len) && isxdigit(strInput[i]) )
2315  {
2316  c <<= 4;
2317  c |= tohex(strInput[i]);
2318  ++cursor;
2319  }
2320  }
2321  else
2322  {
2323  c = 'x';
2324  }
2325  }
2326  break;
2327  default:
2328  c = strInput[cursor];
2329  break;
2330  }
2331 
2332  value.push_back(c);
2333  escseq = false;
2334  ++cursor;
2335  }
2336 
2337  else
2338  {
2339  switch( strInput[cursor] )
2340  {
2341  case '\\':
2342  escseq = true;
2343  break;
2344  case '"':
2345  eos = true;
2346  break;
2347  default:
2348  value.push_back(strInput[cursor++]);
2349  break;
2350  }
2351  }
2352  }
2353 
2354  if( isdquote(strInput[cursor]) )
2355  {
2356  pushToken(strInput, start, cursor, tokens);
2357 
2358  ++cursor; // get past quote
2359 
2360  return cursor;
2361  }
2362  else
2363  {
2364  logLexToken(strInput, start, cursor, tokens);
2365  m_log << labelSyntax << "No ending double qoute '\"' found." << eoe;
2366  return EBadSyntax;
2367  }
2368 }
2369 
2370 void CommandLine::logLexToken(const string &strSource,
2371  const size_t start,
2372  const ssize_t cursor,
2373  TokenVec &tokens,
2374  const bool bLoc)
2375 {
2376  stringstream ss;
2377 
2378  pushToken(strSource, start, cursor, tokens);
2379  tokens.back().printAnnotated(ss, strSource);
2380  m_log << labelAt << ss.str() << eoe;
2381 }
2382 
2383 void CommandLine::pushToken(const string &strSource,
2384  const size_t start,
2385  const ssize_t cursor,
2386  TokenVec &tokens)
2387 {
2388  Token tok(strSource.substr(start, cursor-start),
2389  getLineNum(), start, cursor-1);
2390  tokens.push_back(tok);
2391 }
2392 
2393 
2394 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2395 // Extended Usage Syntax Parsing Methods
2396 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2397 
2399  CmdFormDef &form,
2400  const TokenVec &tokens)
2401 {
2402  size_t tokcnt = tokens.size();
2403  size_t pos = 0;
2404  bool bOk = true;
2405  int rc;
2406 
2407  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, 0, endl);
2408 
2409  m_log << labelParse
2410  << "cmddef " << cmddef.getUid() << ", "
2411  << "form " << form.getIndex() << ": "
2412  << form.getSyntax()
2413  << eoe;
2414 
2415  // command
2416  if( !parseArgv0(cmddef, form, tokens, pos) )
2417  {
2418  m_log << labelFail << "Parssing special argv0." << eoe;
2419  bOk = false;
2420  }
2421 
2422  // required argmuents - may be none
2423  else if( !parseRequiredArgList(cmddef, form, tokens, pos) )
2424  {
2425  m_log << labelFail << "Parsing required argument list." << eoe;
2426  bOk = false;
2427  }
2428 
2429  // optional arguments - may be none
2430  else if( !parseOptionalArgList(cmddef, form, tokens, pos) )
2431  {
2432  m_log << labelFail << "Parsing optional argument list." << eoe;
2433  bOk = false;
2434  }
2435 
2436  // final checks
2437  else if( pos < tokcnt )
2438  {
2439  m_log << labelFail
2440  << "Extraneous tokens found after optional arguments."
2441  << eoe;
2442  bOk = false;
2443  }
2444 
2445  // all good
2446  if( bOk )
2447  {
2448  m_log << labelParse << "Ok" << eoe;
2449  LOGDIAG2_STREAM("Command("
2450  << "uid=" << cmddef.getUid() << ", "
2451  << "name=" << cmddef.getName() << ", "
2452  << "form=" << form.getIndex() << ", "
2453  << "argc=" << form.numOfArgs() << ") "
2454  << "successfully parsed.");
2455  rc = AOk;
2456  }
2457 
2458  // not so good - log error
2459  else
2460  {
2462  rc = EBadSyntax;
2463  }
2464 
2465  CL_DBG_CALL_OUT_NL("rc=" << rc << ", " << okstr(bOk) << ", pos=" << pos);
2466 
2467  return rc;
2468 }
2469 
2471  CmdFormDef &form,
2472  const TokenVec &tokens,
2473  size_t &pos)
2474 {
2475  bool bOk;
2476  string strName;
2477 
2478  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2479 
2480  if( pos < tokens.size() )
2481  {
2482  if( peekEq("<", tokens[pos]) )
2483  {
2484  bOk = parseVariableArg(cmddef, form, tokens, pos);
2485  }
2486  else
2487  {
2488  bOk = parseLiteralArg(cmddef, form, tokens, pos);
2489  }
2490  }
2491  else
2492  {
2493  m_log << labelSyntax << "Command argument not found in form "
2494  << form.getIndex() << "."
2495  << eoe;
2497  bOk = false;
2498  }
2499 
2500  if( bOk )
2501  {
2502  form.m_nArgcReq++;
2503 
2505 
2506  // set command's name
2507  if( form.lastArg().getType() == CmdArgDef::ArgTypeLiteral )
2508  {
2509  // literals are anonymous, use the literal itself
2510  strName = form.lastArg().literalAt(0);
2511  }
2512  else
2513  {
2514  // variables have required names
2515  strName = form.lastArg().getName();
2516  }
2517 
2518  // assign command name
2519  if( cmddef.getName().empty() )
2520  {
2521  cmddef.setName(strName);
2522  }
2523  // all forms must have the same command name
2524  else if( cmddef.getName() != strName )
2525  {
2526  m_log << labelSyntax << "Expected command name '" << cmddef.getName()
2527  << "', but found '" << strName << "' in form " << form.getIndex()
2528  << "."
2529  << eoe;
2531  bOk = false;
2532  }
2533  }
2534 
2535  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2536 
2537  return bOk;
2538 }
2539 
2541  CmdFormDef &form,
2542  const TokenVec &tokens,
2543  size_t &pos)
2544 {
2545  bool bOk = true;
2546 
2547  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2548 
2549  while( bOk && (pos < tokens.size()) )
2550  {
2551  // peek if start of optional arguments
2552  if( peekEq("[", tokens[pos]) )
2553  {
2554  break;
2555  }
2556 
2557  // required argument
2558  else if( (bOk = parseArg(cmddef, form, tokens, pos)) )
2559  {
2560  form.m_nArgcReq++;
2561  }
2562  }
2563 
2564  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2565 
2566  return bOk;
2567 }
2568 
2570  CmdFormDef &form,
2571  const TokenVec &tokens,
2572  size_t &pos)
2573 {
2574  bool bOk = true;
2575 
2576  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2577 
2578  while( bOk && (pos < tokens.size()) )
2579  {
2580  // peek if start an optional arguments
2581  if( !peekEq("[", tokens[pos]) )
2582  {
2583  break;
2584  }
2585 
2586  // "[ argument ]"
2587  if( tokEq("[", tokens, pos) &&
2588  parseArg(cmddef, form, tokens, pos) &&
2589  tokEq("]", tokens, pos) )
2590  {
2592  form.m_nArgcOpt++;
2593  }
2594  else
2595  {
2596  bOk = false;
2597  }
2598  }
2599 
2600  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2601 
2602  return bOk;
2603 }
2604 
2606  CmdFormDef &form,
2607  const TokenVec &tokens,
2608  size_t &pos)
2609 {
2610  bool bOk;
2611 
2612  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2613 
2614  if( pos < tokens.size() )
2615  {
2616  if( peekEq("{", tokens[pos]) )
2617  {
2618  bOk = parseXorListArg(cmddef, form, tokens, pos);
2619  }
2620  else if( peekEq("<", tokens[pos]) )
2621  {
2622  bOk = parseVariableArg(cmddef, form, tokens, pos);
2623  }
2624  else
2625  {
2626  bOk = parseLiteralArg(cmddef, form, tokens, pos);
2627  }
2628  }
2629  else
2630  {
2631  m_log << labelSyntax << "Command argument not found." << eoe;
2633  bOk = false;
2634  }
2635 
2636  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2637 
2638  return bOk;
2639 }
2640 
2642  CmdFormDef &form,
2643  const TokenVec &tokens,
2644  size_t &pos)
2645 {
2646  bool bOk;
2647  CmdArgDef argdef;
2648  StringVec literals;
2649 
2650  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2651 
2652  form.pushArg(argdef);
2653 
2654  bOk = tokEq("{", tokens, pos) &&
2655  parseXorList(cmddef, form, tokens, pos, literals) &&
2656  tokEq("}", tokens, pos);
2657 
2658  if( bOk )
2659  {
2660  stringstream ss;
2661  int n = form.numOfArgs() - 1;
2662 
2663  ss << "_" << n;
2664 
2665  form.lastArg().setName(ss.str());
2668 
2669  for(size_t i = 0; i < literals.size(); ++i)
2670  {
2671  form.lastArg().addLiteralValue(literals[i]);
2672  }
2673  }
2674 
2675  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2676 
2677  return bOk;
2678 }
2679 
2681  CmdFormDef &form,
2682  const TokenVec &tokens,
2683  size_t &pos)
2684 {
2685  bool bOk;
2686  CmdArgDef argdef;
2687  string strName;
2688  CmdArgDef::ArgType eType;
2689  CmdArgDef::RangeVec ranges;
2690  RegEx re;
2691 
2692  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2693 
2694  form.pushArg(argdef);
2695 
2696  bOk = tokEq("<", tokens, pos) &&
2697  parseIdentifier(cmddef, form, tokens, pos, strName);
2698 
2699  if( bOk )
2700  {
2701  // variable modifier
2702  if( peekEq(":", tokens[pos]) )
2703  {
2704  bOk = tokEq(":", tokens, pos) &&
2705  parseVarMod(cmddef, form, tokens, pos, eType, ranges, re);
2706  }
2707  // default
2708  else
2709  {
2710  eType = CmdArgDef::ArgTypeWord;
2711  }
2712  }
2713 
2714  bOk = bOk && tokEq(">", tokens, pos);
2715 
2716  if( bOk )
2717  {
2718  form.lastArg().setName(strName);
2719  form.lastArg().setType(eType);
2720  form.lastArg().setRanges(ranges);
2721  form.lastArg().setRegEx(re);
2722  }
2723 
2724  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2725 
2726  return bOk;
2727 }
2728 
2730  CmdFormDef &form,
2731  const TokenVec &tokens,
2732  size_t &pos)
2733 {
2734  bool bOk;
2735  CmdArgDef argdef;
2736  string strValue;
2737 
2738  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2739 
2740  form.pushArg(argdef);
2741 
2742  bOk = parseLiteralValue(cmddef, form, tokens, pos, strValue);
2743 
2744  if( bOk )
2745  {
2746  stringstream ss;
2747  int n = form.numOfArgs() - 1;
2748 
2749  ss << "_" << n;
2750 
2751  form.lastArg().setName(ss.str());
2753  form.lastArg().addLiteralValue(strValue);
2754  }
2755 
2756  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2757 
2758  return bOk;
2759 }
2760 
2762  CmdFormDef &form,
2763  const TokenVec &tokens,
2764  size_t &pos,
2765  StringVec &literals)
2766 {
2767  bool bOk = true;
2768  bool more = true;
2769  string strValue;
2770 
2771  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2772 
2773  while( more && bOk && (pos < tokens.size()) )
2774  {
2775  if( peekEq("}", tokens[pos]) )
2776  {
2777  m_log << labelSyntax << "Expected literal value but found '}'." << eoe;
2779  bOk = false;
2780  }
2781 
2782  else if( (bOk = parseLiteralValue(cmddef, form, tokens, pos, strValue)) )
2783  {
2784  literals.push_back(strValue);
2785 
2786  // peek if start of another xor option
2787  if( peekEq("|", tokens[pos]) )
2788  {
2789  bOk = tokEq("|", tokens, pos);
2790  }
2791  else
2792  {
2793  more = false;
2794  }
2795  }
2796  }
2797 
2798  if( more && bOk )
2799  {
2800  m_log << labelSyntax << "No literal value found." << eoe;
2802  bOk = false;
2803  }
2804 
2805  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", pos=" << pos);
2806 
2807  return bOk;
2808 }
2809 
2811  CmdFormDef &form,
2812  const TokenVec &tokens,
2813  size_t &pos,
2814  string &strIdent)
2815 {
2816  bool bOk;
2817 
2818  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2819 
2820  strIdent.clear();
2821 
2822  if( pos < tokens.size() )
2823  {
2824  strIdent = tokens[pos].value();
2825  bOk = tokIdentifier(tokens, pos);
2826  }
2827  else
2828  {
2829  m_log << labelSyntax << "No identifier found." << eoe;
2831  bOk = false;
2832  }
2833 
2834  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", identifier='" << strIdent
2835  << "', pos=" << pos);
2836 
2837  return bOk;
2838 }
2839 
2841  CmdFormDef &form,
2842  const TokenVec &tokens,
2843  size_t &pos,
2844  CmdArgDef::ArgType &eType,
2845  CmdArgDef::RangeVec &ranges,
2846  RegEx &re)
2847 {
2848  bool bOk;
2849 
2850  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, endl);
2851 
2852  bOk = parseVarType(cmddef, form, tokens, pos, eType);
2853 
2854  if( bOk )
2855  {
2856  if( peekEq("(", tokens[pos]) )
2857  {
2858  switch( eType )
2859  {
2861  case CmdArgDef::ArgTypeFpn:
2862  bOk = tokEq("(", tokens, pos) &&
2863  parseVarRangeExpr(cmddef, form, tokens, pos, ranges) &&
2864  tokEq(")", tokens, pos);
2865  break;
2867  bOk = tokEq("(", tokens, pos) &&
2868  parseVarRegExpr(cmddef, form, tokens, pos, re) &&
2869  tokEq(")", tokens, pos);
2870  break;
2871  default:
2872  m_log << labelSyntax
2873  << "Unexpected '(' token found for argument type "
2874  << "'" << CmdArgDef::lookupArgSymbol(eType) << "'."
2875  << eoe;
2877  bOk = false;
2878  }
2879  }
2880  }
2881 
2882  CL_DBG_CALL_OUT_NL(okstr(bOk) << ", type=" << eType << ", pos=" << pos);
2883 
2884  return bOk;
2885 }
2886 
2888  CmdFormDef &form,
2889  const TokenVec &tokens,
2890  size_t &pos,
2891  CmdArgDef::ArgType &eType)
2892 {
2893  bool bOk;
2894 
2895  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, " ");
2896 
2897  eType = CmdArgDef::ArgTypeUndef;
2898 
2899  if( pos < tokens.size() )
2900  {
2901  eType = CmdArgDef::lookupArgType(tokens[pos].value());
2902 
2903  if( eType != CmdArgDef::ArgTypeUndef )
2904  {
2905  ++pos;
2906  bOk = true;
2907  }
2908  else
2909  {
2910  m_log << labelSyntax << "Unknown variable type '" << tokens[pos].value()
2911  << "'." << eoe;
2913  bOk = false;
2914  }
2915  }
2916 
2917  else
2918  {
2919  m_log << labelSyntax << "No variable type found." << eoe;
2921  bOk = false;
2922  }
2923 
2924  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", type=" << eType << ", pos=" << pos);
2925 
2926  return bOk;
2927 }
2928 
2930  CmdFormDef &form,
2931  const TokenVec &tokens,
2932  size_t &pos,
2933  CmdArgDef::RangeVec &ranges)
2934 {
2935  bool bOk = true;
2936  StringVec subranges;
2937  StringVec minmax;
2938  CmdArgDef::range r;
2939 
2940  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, " ");
2941 
2942  ranges.clear();
2943 
2944  if( pos < tokens.size() )
2945  {
2946  split(tokens[pos].value(), ',', subranges);
2947 
2948  for(size_t i = 0; bOk && (i < subranges.size()); ++i)
2949  {
2950  minmax.clear();
2951 
2952  split(subranges[i], ':', minmax);
2953 
2954  if( minmax.size() == 0 )
2955  {
2956  m_log << labelSyntax << "No 'min[:max]' subrange found in "
2957  << "'" << tokens[pos].value() << "'.'"
2958  << eoe;
2959  bOk = false;
2960  break;
2961  }
2962  else if( minmax.size() > 2 )
2963  {
2964  m_log << labelSyntax << "Too many 'min:max:?' subrange values found in "
2965  << "'" << tokens[pos].value() << "'.'"
2966  << eoe;
2967  bOk = false;
2968  break;
2969  }
2970 
2971  if( todouble(minmax[0], r.min) != OK )
2972  {
2973  m_log << labelSyntax << "Subrange min '" << minmax[0] << "' NaN in "
2974  << "'" << tokens[pos].value() << "'.'"
2975  << eoe;
2976  bOk = false;
2977  break;
2978  }
2979 
2980  if( minmax.size() == 2 )
2981  {
2982  if( todouble(minmax[1], r.max) != OK )
2983  {
2984  m_log << labelSyntax << "Subrange max '" << minmax[1] << "' NaN in "
2985  << "'" << tokens[pos].value() << "'.'"
2986  << eoe;
2987  bOk = false;
2988  break;
2989  }
2990  }
2991  else
2992  {
2993  r.max = r.min;
2994  }
2995 
2996  // reverse ranges are ok, just swap order
2997  if( r.max < r.min )
2998  {
2999  double tmp;
3000 
3001  r.min = tmp;
3002  r.min = r.max;
3003  r.max = r.min;
3004  }
3005 
3006  ranges.push_back(r);
3007  }
3008  }
3009 
3010  // everything is cool, but no ranges specified
3011  if( bOk && (ranges.size() == 0) )
3012  {
3013  m_log << labelSyntax << "Range expression not found." << eoe;
3014  bOk = false;
3015  }
3016 
3017  if( bOk )
3018  {
3019  ++pos; // advance token parse position
3020  }
3021  else
3022  {
3024  }
3025 
3026  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", numranges=" << ranges.size()
3027  << ", pos=" << pos);
3028 
3029  return bOk;
3030 }
3031 
3033  CmdFormDef &form,
3034  const TokenVec &tokens,
3035  size_t &pos,
3036  RegEx &re)
3037 {
3038  bool bOk;
3039 
3040  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, " ");
3041 
3042  if( pos < tokens.size() )
3043  {
3044  if( m_bIgnoreCase )
3045  {
3047  }
3048 
3049  re = tokens[pos++].value();
3050 
3051  if( re.isValid() )
3052  {
3053  bOk = true;
3054  }
3055  else
3056  {
3057  m_log << labelSyntax << "Regular expression '"
3058  << "'" << re.getRegEx() << "': "
3059  << re.getErrorStr() << "."
3060  << eoe;
3062  bOk = false;
3063  }
3064  }
3065 
3066  else
3067  {
3068  m_log << labelSyntax << "Regular expression not found." << eoe;
3070  bOk = false;
3071  }
3072 
3073  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", re='" << re.getRegEx() << "', pos="
3074  << pos);
3075 
3076  return bOk;
3077 }
3078 
3080  CmdFormDef &form,
3081  const TokenVec &tokens,
3082  size_t &pos,
3083  string &strValue)
3084 {
3085  bool bOk;
3086 
3087  CL_DBG_PARSE_CALL_IN(cmddef, form, tokens, pos, " ");
3088 
3089  strValue.clear();
3090 
3091  if( pos < tokens.size() )
3092  {
3093  strValue = tokens[pos++].value();
3094  bOk = true;
3095  }
3096  else
3097  {
3098  m_log << labelSyntax << "Literal value not found." << eoe;
3100  bOk = false;
3101  }
3102 
3103  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", value='" << strValue << "', "
3104  << "pos=" << pos);
3105 
3106  return bOk;
3107 }
3108 
3109 bool CommandLine::tokEq(const string strCmp,
3110  const TokenVec &tokens,
3111  size_t &pos)
3112 {
3113  bool bOk;
3114 
3115  CL_DBG_CALL_IN("input, strcmp=\"" << strCmp << "\", "
3116  << "tokens, pos=" << pos, " ");
3117 
3118  if( pos >= tokens.size() )
3119  {
3120  m_log << labelSyntax << "Token " << pos << " '" << strCmp
3121  << "' does not exist." << eoe;
3123  bOk = false;
3124  }
3125 
3126  else if( tokens[pos].value() != strCmp )
3127  {
3128  m_log << labelSyntax << "Expected token '" << strCmp << "'"
3129  << ", but found " << tokens[pos]
3130  << eoe;
3132  bOk = false;
3133  }
3134 
3135  else
3136  {
3137  ++pos;
3138  bOk = true;
3139  }
3140 
3141  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", pos=" << pos);
3142 
3143  return bOk;
3144 }
3145 
3146 bool CommandLine::tokIdentifier(const TokenVec &tokens, size_t &pos)
3147 {
3148  bool bOk;
3149 
3150  CL_DBG_CALL_IN("tokens=" << tokens.size() << ", pos=" << pos, " ");
3151 
3152  bOk = isIdentifier(tokens[pos].value());
3153 
3154  if( bOk )
3155  {
3156  ++pos;
3157  }
3158  else
3159  {
3160  m_log << labelSyntax << "Invalid identifier " << tokens[pos] << eoe;
3162  }
3163 
3164  CL_DBG_CALL_OUT_IL(okstr(bOk) << ", pos=" << pos);
3165 
3166  return bOk;
3167 }
3168 
3169 
3170 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3171 // Static Convenience Methods
3172 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3173 
3174 string CommandLine::c14n(const TokenVec &tokens)
3175 {
3176  stringstream ss;
3177  string sep;
3178 
3179  for(size_t i = 0; i < tokens.size(); ++i)
3180  {
3181  ss << sep << prettify(tokens[i].value());
3182 
3183  if( i == 0 )
3184  {
3185  sep = " ";
3186  }
3187  }
3188 
3189  return ss.str();
3190 }
3191 
3192 
3193 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3194 // Output Methods and Operators
3195 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3196 
3197 ostream &rnr::cmd::operator<<(ostream &os, const CommandLine &cl)
3198 {
3199  string sep;
3200 
3201  //
3202  // start of command line interface
3203  //
3204  os << setindent(0);
3205  os << "CommandLine " << cl.m_strName << endl;
3206  os << "{" << endl;
3207 
3208  os << deltaindent(2);
3209 
3210  //
3211  // attributes
3212  //
3213  os << indent() << "name = " << cl.m_strName << endl;
3214  os << indent() << "have_readline = " << cl.m_readline.haveRlLib() << endl;
3215  os << indent() << "use_readline = " << cl.m_readline.useRlLib() << endl;
3216  os << indent() << "prompt = " << cl.getPrompt() << endl;
3217  os << indent() << "ignorecase = " << cl.m_bIgnoreCase << endl;
3218 
3219  //
3220  // command definitions map
3221  //
3222  os << indent() << "cmddefs[" << cl.numOfCmds() << "] =" << endl;
3223  os << indent() << "{" << endl;
3224  os << deltaindent(2);
3225  for(CmdDefCIter iter = cl.m_cmdDefs.begin();
3226  iter != cl.m_cmdDefs.end();
3227  ++iter)
3228  {
3229  os << sep << iter->second;
3230  if( sep.empty() )
3231  {
3232  sep = ",\n";
3233  }
3234  }
3235  os << endl;
3236  os << deltaindent(-2);
3237  os << indent() << "}" << endl;
3238 
3239  //
3240  // command execution functions
3241  //
3242  os << indent() << "execs[" << cl.m_cmdExecs.size() << "] =" << endl;
3243  os << indent() << "{" << endl;
3244  os << deltaindent(2);
3245  sep.clear();
3246  for(CmdExecCIter iter = cl.m_cmdExecs.begin();
3247  iter != cl.m_cmdExecs.end();
3248  ++iter)
3249  {
3250  os << sep << iter->second;
3251  if( sep.empty() )
3252  {
3253  sep = ",\n";
3254  }
3255  }
3256  os << endl;
3257  os << deltaindent(-2);
3258  os << indent() << "}" << endl;
3259 
3260  //
3261  // command data sections
3262  //
3263  os << indent() << "ds[" << cl.m_dataSects.size() << "] =" << endl;
3264  os << indent() << "{" << endl;
3265  os << deltaindent(2);
3266  sep.clear();
3267  for(DataSectCIter iter = cl.m_dataSects.begin();
3268  iter != cl.m_dataSects.end();
3269  ++iter)
3270  {
3271  os << sep << iter->second;
3272  if( sep.empty() )
3273  {
3274  sep = ",\n";
3275  }
3276  }
3277  os << endl;
3278  os << deltaindent(-2);
3279  os << indent() << "}" << endl;
3280 
3281  //
3282  // end of command line interface
3283  //
3284  os << deltaindent(-2);
3285  os << indent() << "}";
3286  os << setindent(0);
3287 
3288  return os;
3289 }
Command line command form definition class interface.
int matchCommandForm(const CmdFormDef &form, const TokenVec &tokens, CmdExtArgVec &argv, double &fFitness)
Match command form against input line argument list.
CmdFormDef & formAt(const int nIndex)
Get command modifiable form at index.
Definition: CmdDef.cxx:187
argument has a mutually exclusive list
Definition: CmdArgDef.h:116
void popPrompt()
Pop prompt string from stack of prompts.
static const int ReFlagICase
ignore case when matching
Definition: RegEx.h:107
std::ostream & printToMark(std::ostream &os, const std::string strMark, int endpt) const
Print the log book entires between the bookmark and the specified end point to the output stream...
Definition: LogBook.cxx:646
int numOfOptionalArgs() const
Get the total number of optional arguments.
Definition: CmdFormDef.h:221
Command line extended argument interface.
void reset()
Reset command definition to pre-compiled state.
Definition: CmdDef.cxx:134
const int EUnknownCmd
unknown, unmatched command
Definition: CmdCore.h:103
int matchCommand(const CmdDef &cmddef, const TokenVec &tokens, CmdExtArgVec &argv, double &fFitness)
Match best command form against input line argument list.
std::ostream & printLog(std::ostream &os) const
Print the entire log book to the output stream.
Definition: LogBook.cxx:630
Command EXTended ARGument class holding parsed command context and the raw and converted argmument va...
Definition: CmdExtArg.h:91
no filename TAB completion attempt
Definition: ReadLine.h:124
int checkReadResult()
Check read input result.
virtual ~CommandLine()
Destructor.
CmdExecMap::iterator CmdExecIter
exec iterator type
Definition: CommandLine.h:424
std::vector< std::string > StringVec
Useful types.
Definition: StringTheory.h:83
std::string & rlreadLine()
Interactively read a line of input from standard input (stdin).
Definition: ReadLine.cxx:174
std::string m_strName
name of this command line
Definition: CommandLine.h:1187
static std::string c14n(const TokenVec &tokens)
Canonicalization of a string.
#define LOGDIAG2_STREAM(args)
Diagnostic level 2 stream logging.
Definition: LogStream.h:128
virtual ssize_t tokenizeInput(const std::string &strInput, TokenVec &tokens)
Lexically analyze input string to generate a series of tokens.
int getUid() const
Get command&#39;s unique id.
Definition: CmdDef.h:134
virtual int execute(const str::StringVec &argv)
Execute a comamnd with the given arguments.
void toVec(const CmdExtArgVec &v1, str::StringVec &v2)
Convert extended argument vector to string argument vector.
CmdArgDef & argAt(const int nIndex)
Get form&#39;s modifiable argument at index.
Definition: CmdFormDef.cxx:188
floating point number (double)
Definition: CmdArgDef.h:104
int removeDataSection(const std::string &ns)
Remove a data section from the command-line interface.
int numOfRequiredArgs() const
Get the total number of required arguments.
Definition: CmdFormDef.h:209
CmdDefMap::const_iterator CmdDefCIter
cmd const iter type
Definition: CommandLine.h:418
static bool isoparen(int c)
Test if c is a open parenthesis character.
double match(const std::string &strArg, const bool bIgnoreCase=false) const
Match argument string against argument definition pattern.
Definition: CmdArgDef.cxx:326
int m_nUid
command unique id
Definition: CmdDef.h:228
virtual int rlBuildReadLineGenerator()
Perform any necessary pre-processing to prepare for command line TAB completion.
LogBook m_log
trace and error log
Definition: CommandLine.h:1195
std::string constructLiteralList(const std::string sep=" ") const
Construct literal list string.
Definition: CmdArgDef.cxx:256
void registerAltGenerator(AltAppGenFunc fnAltAppGen, void *pAppArg)
Register alternate application-specific tab-completion generator.
Definition: ReadLine.cxx:155
osManipIndent indent()
Left indent at current stream indentation level.
Definition: IOManip.cxx:115
virtual const std::string rlGenerator(const std::string &strText, int nIndex, const std::string &strContext, int nStart, int nEnd, unsigned &uFlags)
TAB completion generator.
std::string synopsis
short command synopsis
Definition: CmdCore.h:89
ReadLine m_readline
readline interface
Definition: CommandLine.h:1194
Command line interface data section class.
Definition: CommandLine.h:315
int match(const TokenVec &tokens, CmdExtArgVec &argv)
Match the input tokens to the compiled commands to find the best fit.
void setRanges(const RangeVec &ranges)
Set numeric range values.
Definition: CmdArgDef.cxx:217
const char *const DataSectNsNet
network data section ns
Definition: CmdCore.h:143
const int EAmbigCmd
ambiguous command
Definition: CmdCore.h:102
bool parseVarRegExpr(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, RegEx &re)
Parse variable regular expression.
virtual ssize_t tokenizeSyntax(const std::string &strSyntax, TokenVec &tokens)
Lexically analyze extened usage syntax string to generate a series of tokens.
double min
minimum value
Definition: CmdArgDef.h:125
size_t getLineNum() const
Get the line number of the last read line.
Definition: CommandLine.h:853
std::string longdesc
long command description
Definition: CmdCore.h:90
bool m_bIsCompiled
has [not] been successfully compiled
Definition: CommandLine.h:1190
bool getBtEnable() const
Test if backtracing is enabled.
Definition: CommandLine.h:499
std::string lowercase(const std::string &str)
Convert in-place string to lower case.
static char tohex(int c)
Convert ascii character to binary character value.
void clear()
Clear the log book and bookmarks, along with any pending entry.
Definition: LogBook.cxx:521
static bool isdquote(int c)
Test if c is a double quote character.
static bool iscparen(int c)
Test if c is a close parenthesis character.
static void dealloc(void *p)
Deallocate (delete) allocated (new) core data section.
Definition: CmdCore.cxx:85
String.
Definition: StringTheory.h:78
Variant m_variant
function variant enum
Definition: CommandLine.h:297
virtual int removeCommand(const int uid)
Remove command from command line interface.
bool peekEq(const std::string strCmp, const Token &token) const
Peek if token is equal to string.
Definition: CommandLine.h:1789
const char *const DataSectNsCore
Reserved command line data section namespaces.
Definition: CmdCore.h:141
const CmdFormDef & at(const int nIndex) const
Get command form at index.
Definition: CmdDef.cxx:175
virtual ssize_t lexQuotedString(const std::string &strInput, ssize_t cursor, TokenVec &tokens)
Quoted string lexical analyzer.
undefined argument type
Definition: CmdArgDef.h:97
osManipIndent setindent(const long nIndent)
Set absolute indentation level.
Definition: IOManip.cxx:81
const char * TruthHood[]
strings that equate to true
Number minimum and maximum (sub)range.
Definition: CmdArgDef.h:123
int formIndex() const
Get the argument&#39;s associated parsed form definition index.
Definition: CmdExtArg.h:165
void pushArg(CmdArgDef &argdef)
Push argument to end of argument list.
Definition: CmdFormDef.cxx:167
Command argument compiled definition class.
Definition: CmdArgDef.h:89
int(* CmdExec2Func)(const CmdExtArgVec &argv)
Command execution function type, variant 2.
Definition: CommandLine.h:139
const std::string & getArgName(const CmdExtArg &arg) const
Get the argument name.
int getParentCmdUid() const
Get parent command&#39;s unique id.
Definition: CmdFormDef.h:142
void pushPrompt(const std::string &strPrompt)
Push prompt string onto stack of prompts.
virtual int compile()
Compile all added commands.
no space(&#39; &#39;) after TAB completion
Definition: ReadLine.h:125
const std::string & getRegEx() const
Get the pre-compiled regular expression.
Definition: RegEx.h:342
Regular Express Class.
Definition: RegEx.h:84
const int NoUid
Special values.
Definition: CmdCore.h:116
bool haveRlLib() const
Test if have readline library.
Definition: ReadLine.h:379
int numOfCmds() const
Get the total number of added commands.
Definition: CommandLine.h:1108
const int EError
general, unspecified error
Definition: CmdCore.h:99
argument is optional
Definition: CmdArgDef.h:115
Of string spaces and their strangian operators.
void addLiteralValue(const std::string &strValue)
Add literal value to list of argument values.
Definition: CmdArgDef.cxx:200
void rlArgDefs(const std::string &strSubtext, std::vector< CmdArgDef * > &argdefs)
Build list of all command argument definitions that match completed subtext.
User available command description structure.
Definition: CmdCore.h:85
int m_uid
command unique id associated with this execution
Definition: CommandLine.h:296
int numOfArgs(int uid, int iform) const
Get the total number of arguments.
size_t split(const std::string &str, const char delim, StringVec &elems)
Split string.
int uid() const
Get the argument&#39;s associated parsed command unique id.
Definition: CmdExtArg.h:158
bool parseArgv0(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse argument 0 (command name) syntax.
std::ostream & backtrace(std::ostream &os, const bool bAll=false) const
Insert trace and error log backtrace into output stream.
void orFlags(const unsigned uFlags)
Or flags into argment modifier flags.
Definition: CmdArgDef.cxx:251
virtual void logLexToken(const std::string &strSource, const size_t start, const ssize_t cursor, TokenVec &tokens, const bool bLoc=false)
Log lexigraphical token.
int todouble(const std::string &str, double &val)
Convert string to a double-precision floating-point number.
const std::string & literalAt(const int nIndex) const
Get literal value at index.
Definition: CmdArgDef.cxx:205
static CmdDef nocmddef
"no cmd def" command definition
const CmdDef & at(const int uid) const
Get the command definition with the unique id.
void * m_pData
pointer to section data
Definition: CommandLine.h:322
void setType(const ArgType eType)
Set argument&#39;s type.
Definition: CmdArgDef.cxx:195
int getParentFormIndex() const
Get parent command form&#39;s index.
Definition: CmdArgDef.h:184
std::string prettify(const std::string &str)
Prettify string.
std::vector< Token > TokenVec
vector of tokens type
Definition: Token.h:222
Compiled command definition class.
Definition: CmdDef.h:88
LogBook class interface.
CmdArgDef::ArgType getArgDefType(const CmdExtArg &arg) const
Get the argument type.
virtual int parseSyntax(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens)
Parse command syntax.
int finalize()
Finalize command compilation.
static const std::string lookupArgSymbol(const CmdArgDef::ArgType eType)
Look up argument symbol, given argument type.
Definition: CmdArgDef.cxx:587
Command line interface class interface.
int getIndex() const
Get forms&#39;s index.
Definition: CmdFormDef.h:152
Commands.
Definition: CmdAddOns.h:81
std::vector< CmdExtArg > CmdExtArgVec
vector of ext args type
Definition: CmdExtArg.h:397
CmdExecMap m_cmdExecs
map of added command executions
Definition: CommandLine.h:1192
virtual int addCommand(const CmdDesc &desc)
Add a command to the command line interface.
bool parseOptionalArgList(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse optional argument list syntax.
virtual ssize_t lexSyntaxParenExpr(const std::string &strSyntax, ssize_t cursor, TokenVec &tokens)
Syntax parenthetical expression lexical analyzer.
virtual ssize_t lexWord(const std::string &strInput, ssize_t cursor, TokenVec &tokens)
Word lexical analyzer.
static bool isesc(int c)
Testif c is an escape character.
std::string m_strSyntax
parsable command extended usage syntax
Definition: CmdDef.h:230
CmdExtArg convert(const std::string &strArg, const bool bIgnoreCase=false) const
Convert argument string to type.
Definition: CmdArgDef.cxx:482
bool parseVarRangeExpr(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, CmdArgDef::RangeVec &ranges)
Parse variable range expression.
int numOfRequiredArgs(int uid, int iform) const
Get the total number of required arguments.
CmdDefMap::iterator CmdDefIter
cmd iterator type
Definition: CommandLine.h:417
bool parseRequiredArgList(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse required argument list syntax.
static const string noprompt
"no prompt" prompt value
DeallocFunc m_fnDealloc
data deallocator function
Definition: CommandLine.h:323
bool parseVariableArg(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse variable argument syntax.
FnVar m_exec
function execution variant
Definition: CommandLine.h:298
bool tokIdentifier(const TokenVec &tokens, size_t &pos)
Test if string has valid identifier syntax.
LogBook & eoe(LogBook &log)
LogBook end-of-entry stream manipulator.
Definition: LogBook.cxx:926
Stream I/O manipulators and helpers.
const int ENoExec
cannot execute
Definition: CmdCore.h:105
int getParentCmdUid() const
Get parent command&#39;s unique id.
Definition: CmdArgDef.h:174
const int ERead
read error
Definition: CmdCore.h:101
The thin ReadLine wrapper class interface.
std::string m_strNs
data section namespace
Definition: CommandLine.h:321
#define LOGDIAG3_STREAM(args)
Diagnostic level 3 stream logging.
Definition: LogStream.h:115
const int EBadSyntax
bad syntax
Definition: CmdCore.h:104
const std::string & getErrorStr() const
Get the last RegExs operation error string.
Definition: RegEx.h:401
CmdDef & cmdAt(const int uid)
Get modifiable command definition with the given unique id.
const int NoIndex
no index
Definition: CmdCore.h:117
const int AOk
(0) A-Ok, no error, success, good
Definition: CmdCore.h:93
bool isOptional() const
Test if argument is an optional argument.
Definition: CmdArgDef.h:299
Command line command definition class interface.
Helper class to hold a command line execution function.
Definition: CommandLine.h:164
bool parseVarMod(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, CmdArgDef::ArgType &eType, CmdArgDef::RangeVec &ranges, RegEx &re)
Parse variable modifier.
const int EBadVal
bad value
Definition: CmdCore.h:108
int numOfOptionalArgs(int uid, int iform) const
Get the total number of optional arguments.
ArgType
Argument type enumeration.
Definition: CmdArgDef.h:95
CmdArgDef & lastArg()
Get the last pushed argument.
Definition: CmdFormDef.cxx:200
argument is the command
Definition: CmdArgDef.h:114
#define LOGERROR_STREAM(args)
Error stream logging.
Definition: LogStream.h:167
LogBook & bookmark(LogBook &log)
LogBook bookmark stream manipulator.
Definition: LogBook.cxx:932
Command line argument definition class interface.
DataSectMap::iterator DataSectIter
iterator type
Definition: CommandLine.h:431
static const int NEWEST
newest, most recent entries
Definition: LogBook.h:92
void setName(const std::string &strName)
Set command&#39;s name.
Definition: CmdDef.cxx:147
int(* CmdExec3Func)(CommandLine &cli, const CmdExtArgVec &argv)
Command execution function type, variant 3.
Definition: CommandLine.h:154
int(* CmdExec1Func)(const str::StringVec &argv)
Command execution function type, variant 1.
Definition: CommandLine.h:119
virtual int readCommand(int &uid, int &iform, str::StringVec &argv)
Read an input line from stdin and match to the best compiled command.
Definition: CommandLine.h:683
void setRegEx(const RegEx &re)
Set regular expression value.
Definition: CmdArgDef.cxx:246
PromptStack m_prompts
stack of prompt strings
Definition: CommandLine.h:1193
const std::string & getName() const
Return command&#39;s name.
Definition: CmdDef.h:144
void pushForm(CmdFormDef &formdef)
Push new command form to end of form list.
Definition: CmdDef.cxx:166
identifier (C conforming)
Definition: CmdArgDef.h:101
ArgType getType() const
Get argument&#39;s type.
Definition: CmdArgDef.h:214
virtual ssize_t lexSyntaxWord(const std::string &strSyntax, ssize_t cursor, TokenVec &tokens)
Syntax word lexical analyzer.
std::string okstr(bool b)
Convert boolean to "ok" or "not-ok".
Definition: StringTheory.h:98
bool rlPartialMatch(const std::string &strText, const std::string strLiteral, const size_t uLen)
Match partial text agains literal string.
const char *const DataSectNsOS
OS data section ns.
Definition: CmdCore.h:142
const std::string & getName() const
Get argument&#39;s name.
Definition: CmdArgDef.h:204
void setUid(const int uid)
Set command&#39;s unique id.
Definition: CmdDef.cxx:142
CmdExecMap::const_iterator CmdExecCIter
exec const iter type
Definition: CommandLine.h:425
bool parseLiteralArg(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse literal, fixed-valued argument syntax.
bool useRlLib() const
Test if the readline library is enabled.
Definition: ReadLine.h:393
ReadLine class provides a C++ wrapper around the readline C library.
Definition: ReadLine.h:112
int m_nArgcReq
number of required arguments
Definition: CmdFormDef.h:253
void(* DeallocFunc)(void *)
Definition: CommandLine.h:319
const int EArgv0
not this command argv0
Definition: CmdCore.h:106
Logging facitlities built on librnr log.h macros to support C++ output insertion streaming.
bool isFError() const
Test if the last read operation resulted in an I/O error condition.
Definition: ReadLine.h:483
Command line core data types.
bool parseXorListArg(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse mutually exclusive argument values syntax.
static const char * RlTabEnd
no more matches value (empty string)
Definition: ReadLine.h:94
int argIndex() const
Get the argument&#39;s associated parsed argument definition index.
Definition: CmdExtArg.h:172
static bool isspecial(int c)
Command usage syntax special characters.
no default TAB completion match
Definition: ReadLine.h:123
bool hasCmd(const int uid) const
Test if command exists.
bool parseXorList(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, str::StringVec &literals)
Parse mutually exclusive list.
std::vector< range > RangeVec
vector of subranges
Definition: CmdArgDef.h:129
static const std::string rlGeneratorWrapper(void *pAppArg, const std::string &strText, int nIndex, const std::string &strContext, int nStart, int nEnd, unsigned &uFlags)
Static TAB completion generator wrapper.
virtual int removeAllCommands()
Remove all commands from command line interface.
const std::string & getPrompt() const
Get the current prompt string.
void setPrompt(const std::string &strPrompt)
Set the prompt string.
Definition: ReadLine.h:413
int numOfForms() const
Get the total number command forms.
Definition: CmdDef.h:184
const std::string & getName() const
Get command line interface&#39;s name.
Definition: CommandLine.h:1040
bool isIdentifier(const std::string &str)
Test if string is a valid identifier.
static CmdArgDef::ArgType lookupArgType(const std::string strSymbol)
Look up argument type, given argument type symbol.
Definition: CmdArgDef.cxx:575
osManipIndent deltaindent(const long nDelta)
Set relative delta indentation level.
Definition: IOManip.cxx:99
Parsed token container class.
Definition: Token.h:84
bool parseVarType(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, CmdArgDef::ArgType &eType)
Parse variable type.
std::string getSyntax() const
Get form&#39;s extended usage syntax.
Definition: CmdFormDef.h:160
The Regular Expression Class interface.
std::string constructRangeList(const std::string sep=",") const
Construct ranges string.
Definition: CmdArgDef.cxx:270
std::string & ireadLine()
Interactively read a line of input from standard input (stdin).
Definition: ReadLine.h:313
const RangeVec & getRanges() const
Get numeric range values.
Definition: CmdArgDef.h:243
void rlTabList(const std::string &strText, std::vector< CmdArgDef * > &argdefs, str::StringVec &tabList, unsigned &uFlags)
Build TAB completion list and set appropriate readline modifiers.
DataSectMap::const_iterator DataSectCIter
const iter type
Definition: CommandLine.h:432
const std::string & lastText() const
Get the last (latest) log entry text.
Definition: LogBook.h:523
bool isEoF() const
Test if the last read operation resulted in an end-of-file condition.
Definition: ReadLine.h:472
DataSectMap m_dataSects
data sections
Definition: CommandLine.h:1196
std::string c14n(const std::string &str)
Simple canonicalization of a string.
int addDataSection(const std::string &ns, void *pData, DataSect::DeallocFunc fn=NULL)
Add a data section to the command-line interface.
bool m_bIgnoreCase
do [not] ignore case on commands
Definition: CommandLine.h:1188
RoadNarrows Robotics.
Definition: Camera.h:74
int m_nUidCnt
unique id counter
Definition: CommandLine.h:1189
std::string syntax
parsable command extended usage syntax
Definition: CmdCore.h:88
Compiled command form defintion class.
Definition: CmdFormDef.h:91
bool isValid() const
Test if in a valid state (i.e. compiled).
Definition: RegEx.h:354
any (quoted) character sequence
Definition: CmdArgDef.h:100
const std::string & getErrorStr() const
Get the most recent error.
bool tokEq(const std::string strCmp, const TokenVec &tokens, size_t &pos)
Test if token at position is equal to string.
virtual void addToHistory(const str::StringVec &argv)
Add command to history.
CommandLine class.
Definition: CommandLine.h:444
std::ostream & operator<<(std::ostream &os, const timespec &obj)
A timespec insertion operator.
Core data section type.
Definition: CmdCore.h:148
size_t gcss(const std::string &str1, const std::string &str2, const size_t pos=0)
Find the length of the Greatest Common SubString.
bool parseIdentifier(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, std::string &strIdent)
Parse identifier.
int getIndex() const
Get argument&#39;s command line index.
Definition: CmdArgDef.h:194
double max
maximum value
Definition: CmdArgDef.h:126
const std::string & getRegEx() const
Get regular expression value.
Definition: CmdArgDef.h:253
const CmdArgDef & at(const int nIndex) const
Get form&#39;s argument at index.
Definition: CmdFormDef.cxx:176
bool parseArg(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos)
Parse argument syntax.
int m_nArgcOpt
number of optional arguments
Definition: CmdFormDef.h:254
Input/Output.
Definition: IOManip.h:77
void setFlags(int nFlags)
Set new compile behavior flags.
Definition: RegEx.cxx:397
void setName(const std::string &strName)
Set argument&#39;s name.
Definition: CmdArgDef.cxx:190
const std::string emptystring
"" empty string
Definition: CmdCore.cxx:82
Simple, token container class interface.
int processInput(const std::string &strLine, CmdExtArgVec &argv)
Process a line of input.
CmdDefMap m_cmdDefs
map of added command definitions
Definition: CommandLine.h:1191
void pushToken(const std::string &strSource, const size_t start, const ssize_t cursor, TokenVec &tokens)
Push token to the end of the generated tokens.
bool isReservedDataSection(const std::string &ns) const
Test if the namespace is a command-line interface reserved name.
const char * FalseHood[]
Falsehood and truthhood strings. Each list termintate with a NULL.
const int EEoF
end of file
Definition: CmdCore.h:100
bool parseLiteralValue(CmdDef &cmddef, CmdFormDef &form, const TokenVec &tokens, size_t &pos, std::string &strValue)
Parse literal value.
any non-whitespace contiguous char sequence
Definition: CmdArgDef.h:99
void * getDataSection(const std::string &ns)
Get the section data under the specified namespace.
const std::string & getErrorStr() const
Get the most recently set error string.
Definition: ReadLine.cxx:322
int numOfArgs() const
Get the total number of arguments.
Definition: CmdFormDef.h:195