appkit  1.5.1
RoadNarrows Robotics Application Kit
rnr_eg_cli.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: appkit
4 //
5 // Program: rnr_eg_cli
6 //
7 // File: rnr_eg_cli.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2015-11-09 17:38:34 -0700 (Mon, 09 Nov 2015) $
12  * $Rev: 4195 $
13  *
14  * \brief Command line interface example program.
15  *
16  * Demonstrates the usage of librnr_appkit's CommandLine, ReadLine, and
17  * LogBook classes to build a command line interface.
18  *
19  * \author Robin Knight (robin.knight@roadnarrows.com)
20  *
21  * \par Copyright
22  * \h_copy 2016-2017. RoadNarrows LLC.\n
23  * http://www.roadnarrows.com\n
24  * All Rights Reserved
25  */
26 /*
27  * @EulaBegin@
28  * @EulaEnd@
29  */
30 ////////////////////////////////////////////////////////////////////////////////
31 
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 
39 #include <iostream>
40 #include <fstream>
41 #include <sstream>
42 #include <string>
43 #include <map>
44 
45 #include "rnr/rnrconfig.h"
46 #include "rnr/log.h"
47 #include "rnr/opts.h"
48 #include "rnr/pkg.h"
49 
50 #include "rnr/appkit/Time.h"
51 #include "rnr/appkit/CmdExtArg.h"
52 #include "rnr/appkit/CommandLine.h"
53 #include "rnr/appkit/CmdAddOns.h"
54 
55 #include "version.h"
56 
57 using namespace std;
58 using namespace rnr;
59 using namespace rnr::chronos;
60 using namespace rnr::cmd;
61 
62 /*!
63  * \ingroup apps
64  * \defgroup appkit_eg rnr_eg_cli
65  * \{
66  */
67 
68 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69 // Shell Command-Line
70 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71 
72 #define APP_EC_OK 0 ///< success exit code
73 #define APP_EC_ARGS 2 ///< command-line options/arguments error exit code
74 #define APP_EC_EXEC 4 ///< execution exit code
75 
76 static char *Argv0; ///< the command
77 
78 /*!
79  * \brief Program information.
80  */
81 static OptsPgmInfo_T PgmInfo =
82 {
83  // usage_args
84  NULL,
85 
86  // synopsis
87  "A RoadNarrows Robotics librnr_appkit CLI example program.",
88 
89  // long_desc =
90  "The %P command demonstrates the use of librnr_appkit's CommandLine, "
91  "ReadLine, LogBook, RegEx classes to build a command line interface.",
92 
93  // diagnostics
94  NULL
95 };
96 
97 /*!
98  * \brief Command line options information.
99  */
100 static OptsInfo_T OptsInfo[] =
101 {
102  {NULL, }
103 };
104 
105 
106 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
107 // ASCII Art and Functions
108 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
109 
110 #if 0 // SHOW_VERBATIM
111 /* Aardvark
112  _.---._ /\\
113  ./' "--`\//
114  ./ o \
115  /./\ )______ \__ \
116  ./ / /\ \ | \ \ \ \
117  / / \ \ | |\ \ \7
118  " " " "
119 */
120 #endif // SHOW_VERBATIM
121 
122 /*!
123  * \brief ASCII ardvaark.
124  */
125 static const char *AsciiAardvark[] =
126 {
127 " _.---._ /\\\\",
128 " ./' \"--`\\//",
129 " ./ o \\",
130 " /./\\ )______ \\__ \\",
131 " ./ / /\\ \\ | \\ \\ \\ \\",
132 " / / \\ \\ | |\\ \\ \\7",
133 " \" \" \" \"",
134 };
135 
136 #if 0 // SHOW_VERBATIM
137 /* Mandrill
138  :: ,-, ::
139  :: :o~o: ::
140  ::.___.-:{|}:--..-::;
141  "=___ '_' ,..-"
142  :-, ' :__
143  \ \ / :
144  \ :. .:,__:
145 */
146 #endif // SHOW_VERBATIM
147 
148 /*!
149  * \brief ASCII Mandrill.
150  */
151 static const char *AsciiMandrill[] =
152 {
153 " :: ,-, ::",
154 " :: :o~o: ::",
155 " ::.___.-:{|}:--..-::;",
156 " \"=___ '_' ,..-\"",
157 " :-, ' :__",
158 " \\ \\ / :",
159 " \\ :. .:,__:",
160 NULL
161 };
162 
163 #if 0 // SHOW_VERBATIM
164 /* Numbat
165  _ ____ ~^
166  \`.|\..----''` / /`'--.,.,, ~'',
167  / ' ` / /,/ / / ,,. - ~,,-
168  )/' _/ \ / `-_,/ / - ",,,' -
169  | /' `"\_ ,_.-;_.-\_ ', '',,,'`
170  |/ _.-'_/ <__.' ; /
171  {_.-``-' <__/
172 */
173 #endif // SHOW_VERBATIM
174 
175 /*!
176  * \brief ASCII numbat.
177  */
178 static const char *AsciiNumbat[] =
179 {
180 " _ ____ ~^",
181 " \\`.|\\..----''` / /`'--.,.,, ~''",
182 " / ' ` / /,/ / / ,,. - ~,,",
183 " )/' _/ \\ / `-_,/ / - \",,,' ",
184 " | /' `\"\\_ ,_.-;_.-\\_ ', '',,,'",
185 " |/ _.-'_/ <__.' ; ",
186 " {_.-``-' <__",
187 NULL
188 };
189 
190 #if 0 // SHOW_VERBATIM
191 /* Zebra
192  ,,_
193  =/.-"
194  ~._ =//
195  _(||||\|_
196  / /
197 
198  \\/),
199  ,'.' /,
200  (_)- / /,
201  /\_/ |__..--, *
202  (\ _ /\ \ \ / ).'
203  \(-'./ / (_ //
204  \\ \,'--'\_(
205  )(_/ )_/ )_)
206  (_,' (_.'(_.'
207 */
208 #endif // SHOW_VERBATIM
209 
210 /*!
211  * \brief ASCII small zebra.
212  */
213 static const char *AsciiZebraSmall[] =
214 {
215 " ,,_ ",
216 " =/.-\" ",
217 " ~._ =// ",
218 " _(||||\\|_ ",
219 " / / ",
220 NULL
221 };
222 
223 /*!
224  * \brief ASCII zebra.
225  */
226 static const char *AsciiZebra[] =
227 {
228 " \\\\/),",
229 " ,'.' /,",
230 " (_)- / /,",
231 " /\\_/ |__..--, *",
232 " (\\ _ /\\ \\ \\ / ).'",
233 " \\(-'./ / (_ //",
234 " \\\\ \\,'--'\\_(",
235 " )(_/ )_/ )_)",
236 " (_,' (_.'(_.'",
237 NULL
238 };
239 
240 #if 0 // SHOW_VERBATIM
241 /* Ant
242  '\__
243  (o ) ___
244  <>(_)(_)(___)
245  < < > >
246  ' ' ` `
247 
248  Grub
249  \
250  '-.__.-'
251  /oo |--.--,--,--.
252  \_.-'._i__i__i_.'
253  """""""""
254 */
255 #endif // SHOW_VERBATIM
256 
257 /*!
258  * \brief ASCII ant.
259  */
260 static const char *AsciiAnt[] =
261 {
262 " '\\__",
263 " (o ) ___",
264 " <>(_)(_)(___)",
265 " < < > >",
266 " ' ' ` `",
267 NULL
268 };
269 
270 /*!
271  * \brief ASCII grub.
272  */
273 static const char *AsciiGrub[] =
274 {
275 " \\",
276 " '-.__.-'",
277 " /oo |--.--,--,--.",
278 " \\_.-'._i__i__i_.'",
279 " \"\"\"\"\"\"\"\"\"",
280 NULL
281 };
282 
283 #if 0 // SHOW_VERBATIM
284 /* Carrot
285  \/'
286  \/'
287  _/'
288  (,;)
289  (,.)
290  (,/
291  |/
292 */
293 #endif // SHOW_VERBATIM
294 
295 /*!
296  * \brief ASCII carrot.
297  */
298 static const char *AsciiCarrot[] =
299 {
300 " \\/'",
301 " \\/'",
302 " _/'",
303 " (,;)",
304 " (,.)",
305 " (,/",
306 " |/",
307 NULL
308 };
309 
310 #if 0 // SHOW_VERBATIM
311 /* Vitamins
312  _____
313  _(_|_|_)_
314  / _ \
315  ' (C) ( \ '
316  | ___ \B\ |
317  |(_A_) \ )|
318  '___________'
319 */
320 #endif // SHOW_VERBATIM
321 
322 /*!
323  * \brief ASCII bottle of vitamins.
324  */
325 static const char *AsciiVitamins[] =
326 {
327 " _____",
328 " _(_|_|_)_",
329 " / _ \\",
330 " ' (C) ( \\ '",
331 " | ___ \\B\\ |",
332 " |(_A_) \\ )|",
333 " '___________'",
334 NULL
335 };
336 
337 #if 0 // SHOW_VERBATIM
338 /* Walking
339 
340  |''''|">
341  |\'''|\">
342  /\'''/\">
343  /|''/|">
344 
345  */
346 #endif // SHOW_VERBATIM
347 
348 /*!
349  * \brief ASCII walk sequence.
350  */
351 static const char *AsciiWalking[][2] =
352 {
353  {" |''''|\"> ", NULL},
354  {" |\\'''|\\\">", NULL},
355  {" /\\'''/\\\">", NULL},
356  {" /|''/|\"> ", NULL}
357 };
358 
359 /*!
360  * \brief ASCII art output stream operator.
361  *
362  * \param os Output stream.
363  * \param art Art to insert.
364  *
365  * \return Reference to output stream.
366  */
367 static ostream &operator<<(ostream &os, const char *art[])
368 {
369  for(int i = 0; art[i] != NULL; ++i)
370  {
371  os << art[i] << endl;
372  }
373  return os;
374 }
375 
376 /*!
377  * \brief Write a set of ASCII art to cout.
378  *
379  * The art written left to right, bottom justified.
380  *
381  * \param os Output stream.
382  * \param art0 Required art 0.
383  * \param art1 Required art 1.
384  * \param art2 Optional art 2.
385  * \param art3 Optional art 3.
386  */
387 static void showAsciiArt(ostream &os,
388  const char *art0[],
389  const char *art1[],
390  const char *art2[] = NULL,
391  const char *art3[] = NULL)
392 {
393  int nPieces = 2;
394  int linecnt[4];
395  int maxlen[4];
396  int maxlinecnt = 0;
397  int len;
398  int i, j;
399 
400  const char **pieces[4];
401 
402  pieces[0] = art0;
403  pieces[1] = art1;
404 
405  if( art2 != NULL )
406  {
407  ++nPieces;
408  pieces[2] = art2;
409  }
410 
411  if( art3 != NULL )
412  {
413  ++nPieces;
414  pieces[3] = art3;
415  }
416 
417  // gather stats
418  for(i = 0; i < nPieces; ++i)
419  {
420  linecnt[i] = 0;
421  maxlen[i] = 0;
422 
423  for(j = 0; pieces[i][j] != NULL; ++j)
424  {
425  len = strlen(pieces[i][j]);
426  if( len > maxlen[i] )
427  {
428  maxlen[i] = len;
429  }
430  linecnt[i] += 1;
431  }
432 
433  if( linecnt[i] > maxlinecnt )
434  {
435  maxlinecnt = linecnt[i];
436  }
437  }
438 
439  char ofill = os.fill();
440  size_t owidth = os.width();
441 
442  os.fill(' ');
443 
444  // show
445  for(int line = 0; line < maxlinecnt; ++line)
446  {
447  for(i = 0; i < nPieces; ++i)
448  {
449  if( linecnt[i] >= maxlinecnt-line )
450  {
451  j = linecnt[i] - (maxlinecnt - line);
452  // os << "line " << line << ", art " << i << " j " << j << endl;
453  len = strlen(pieces[i][j]);
454  os << pieces[i][j] << setw(maxlen[i]-len+1) << "";
455  }
456  else
457  {
458  os << setw(maxlen[i]) << "";
459  }
460  }
461  os << endl;
462  }
463 
464  os.fill(ofill);
465  os.width(owidth);
466 }
467 
468 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
469 // The Animals
470 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
471 
472 /*!
473  * \brief Animal activities.
474  */
476 {
477  ActIdle, ///< idle, doing nothing
478  ActSleeping, ///< sleeping
479  ActEating, ///< eating
480  ActWalking ///< walking
481 };
482 
483 /*
484  * \brief Animal Information type.
485  */
487 {
488  // fixed
489  const char *m_sCommonName; ///< command name
490  const char *m_sScientificName; ///< scientific name
491  const char **m_sSelfie; ///< ASCII selfie
492 
493  // state
494  string m_strGivenName; ///< adopted given name by owner
495  bool m_bIsAdopted; ///< is [not] adopted
496  Activity m_eActivity; ///< current activity
497  double m_fActStart; ///< activity start time
498  double m_fActDuration; ///< activitiy uninterrupted duration
499 };
500 
501 const char *NoName = "No given name"; ///< 'no name' name
502 
503 /*!
504  * \brief The animals.
505  */
507 {
508  { "aardvark", "orycteropus afer", AsciiAardvark,
509  NoName, false, ActIdle, 0.0, 0.0
510  },
511 
512  { "mandrill", "mandrillus sphinx", AsciiMandrill,
513  NoName, false, ActIdle, 0.0, 0.0
514  },
515 
516  { "numbat", "myrmecobius fasciatus", AsciiNumbat,
517  NoName, false, ActIdle, 0.0, 0.0
518  },
519 
520  { "zebra", "equus quagga", AsciiZebra,
521  NoName, false, ActIdle, 0.0, 0.0
522  }
523 };
524 
525 size_t NumOfAnimals = arraysize(Animals); ///< number of animals
526 
527 /*!
528  * \brief Find the animal with the common name.
529  *
530  * \param strCommonName Common name.
531  *
532  * \return
533  * On succes, returns pointer to the animal's info.
534  * On failure, NULL is returned.
535  */
536 static AnimalInfo *findAnimal(const string &strCommonName)
537 {
538  for(size_t i = 0; i < NumOfAnimals; ++i)
539  {
540  if( strCommonName == Animals[i].m_sCommonName )
541  {
542  return &Animals[i];
543  }
544  }
545  return NULL;
546 }
547 
548 Time ActivityTime; ///< activity time
549 
550 
551 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
552 // The CommandLine Interface.
553 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
554 
555 
556 const string CliName("Adopt-An-Animal"); ///< CLI name
557 const string CliPrompt("aaa> "); ///< CLI prompt
558 CommandLine Cli(CliName, CliPrompt); ///< the CLI
559 
560 /*!
561  * \{
562  * \brief Error and warning printing macros.
563  */
564 #define PERROR(_err) \
565  cout << CliName << ": " << "Error: " << _err << endl
566 
567 #define PWARN(_warn) \
568  cout << CliName << ": " << _warn << endl
569 
570 #define PCMDERROR(_cmd, _err) \
571  cout << CliName << ": " << _cmd << ": " << "Error: " << _err << endl
572 
573 #define PCMDWARN(_cmd, _warn) \
574  cout << CliName << ": " << _cmd << ": " << _warn << endl
575 /*
576  * \}
577  */
578 
579 /*!
580  * \brief Check if command input is matched correctly with the command
581  * execution.
582  *
583  * \note This should never happen if the command definitions are well defined
584  * and unambiguous.
585  *
586  * \param argv0 Input argument 0. (Any extended argument actually works).
587  * \param argc Number of input arguments.
588  * \param strTgtName Expected target name of execution.
589  *
590  * \return OK(0) on success, negative value on failure.
591  */
592 int checkCmd(const CmdExtArg &argv0, int argc, const string strTgtName = "")
593 {
594  const string &strDefName = Cli.at(argv0.uid()).getName();
595 
596  if( !strTgtName.empty() && (strTgtName != strDefName) )
597  {
598  PERROR("Command execution is for '" << strTgtName << "' "
599  << "not input command '" << strDefName << "'");
600  return RC_ERROR;
601  }
602  else if( argc < Cli.numOfRequiredArgs(argv0) )
603  {
604  PERROR("Command '" << strDefName << "' has missing arguments.");
605  return RC_ERROR;
606  }
607  else
608  {
609  return OK;
610  }
611 }
612 
613 
614 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
615 // CommandLine Exec2 Command Definitions
616 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
617 
618 /*!
619  * \brief Execute 'adopt' command.
620  *
621  * \param argv Command line arguments.
622  *
623  * \return OK(0) on success, negative value on failure.
624  */
625 static int execAdopt(const CmdExtArgVec &argv)
626 {
627  static const char *cmdname = "adopt";
628 
629  size_t argc = argv.size();
630  size_t n;
631 
632  if( checkCmd(argv[0], argc, cmdname) != OK )
633  {
634  return RC_ERROR;
635  }
636 
637  n = 1; // skip argv0
638 
639  //
640  // Required arguments.
641  //
642  string animal = argv[n].s();
643 
644  AnimalInfo *p = findAnimal(animal);
645 
646  if( p == NULL )
647  {
648  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
649  return RC_ERROR;
650  }
651 
652  else if( p->m_bIsAdopted )
653  {
654  PCMDWARN(cmdname, "The " << animal << " is already adopted.");
655  return RC_ERROR;
656  }
657 
658  else
659  {
660  p->m_bIsAdopted = true;
661  return OK;
662  }
663 }
664 
665 /*!
666  * \brief Execute 'sleep' command.
667  *
668  * \param argv Command line arguments.
669  *
670  * \return OK(0) on success, negative value on failure.
671  */
672 static int execSleep(const CmdExtArgVec &argv)
673 {
674  static const char *cmdname = "sleep";
675 
676  size_t argc = argv.size();
677  size_t n;
678 
679  // optional defaults
680  long secs = 10;
681 
682  if( checkCmd(argv[0], argc, cmdname) != OK )
683  {
684  return RC_ERROR;
685  }
686 
687  n = 1; // skip argv0
688 
689  //
690  // Required arguments.
691  //
692 
693  string animal = argv[n].s();
694 
695  AnimalInfo *p = findAnimal(animal);
696 
697  if( p == NULL )
698  {
699  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
700  return RC_ERROR;
701  }
702 
703  else if( !p->m_bIsAdopted )
704  {
705  PCMDWARN(cmdname, "The " << animal << " is not yours to rock to sleep.");
706  return OK;
707  }
708 
709  ++n;
710 
711  //
712  // Optional arguments
713  //
714 
715  if( n < argc )
716  {
717  secs = argv[n].i();
718  }
719 
721  p->m_fActStart = ActivityTime.now();
722  p->m_fActDuration = (double)secs;
723 
724  cout << "The " << p->m_sCommonName << " is sleeping for " << secs
725  << " seconds." << endl;
726 
727  return OK;
728 }
729 
730 /*!
731  * \brief Execute 'name' command.
732  *
733  * \param argv Command line arguments.
734  *
735  * \return OK(0) on success, negative value on failure.
736  */
737 static int execNameAnimal(const CmdExtArgVec &argv)
738 {
739  static const char *cmdname = "name";
740 
741  size_t argc = argv.size();
742  size_t n;
743 
744  if( checkCmd(argv[0], argc, cmdname) != OK )
745  {
746  return RC_ERROR;
747  }
748 
749  n = 1; // skip argv0
750 
751  //
752  // Required arguments.
753  //
754 
755  string animal = argv[n].s();
756 
757  AnimalInfo *p = findAnimal(animal);
758 
759  if( p == NULL )
760  {
761  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
762  return RC_ERROR;
763  }
764 
765  else if( !p->m_bIsAdopted )
766  {
767  PCMDWARN(cmdname, "You have not adopted the " << animal << ".");
768  return OK;
769  }
770 
771  ++n; // next argument
772 
773  p->m_strGivenName = argv[n].s();
774 
775  return OK;
776 }
777 
778 /*!
779  * \brief Execute 'namaste' command.
780  *
781  * \param argv Command line arguments.
782  *
783  * \return OK(0) on success, negative value on failure.
784  */
785 static int execNamaste(const CmdExtArgVec &argv)
786 {
787  static const char *cmdname = "namaste";
788 
789  cout << "Aye. Baaaa to the divine Ewe." << endl;
790 
791  return OK;
792 }
793 
794 /*!
795  * \brief Execute 'feed' command.
796  *
797  * \param argv Command line arguments.
798  *
799  * \return OK(0) on success, negative value on failure.
800  */
801 static int execFeedAnimal(const CmdExtArgVec &argv)
802 {
803  static const char *cmdname = "feed";
804 
805  size_t argc = argv.size();
806  size_t n;
807 
808  if( checkCmd(argv[0], argc, cmdname) != OK )
809  {
810  return RC_ERROR;
811  }
812 
813  n = 1; // skip argv0
814 
815  //
816  // Required arguments.
817  //
818 
819  string animal = argv[n].s();
820 
821  AnimalInfo *p = findAnimal(animal);
822 
823  if( p == NULL )
824  {
825  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
826  return RC_ERROR;
827  }
828 
829  else if( !p->m_bIsAdopted )
830  {
831  PCMDWARN(cmdname, "The " << animal << " is not yours to feed.");
832  return OK;
833  }
834 
835  ++n; // next argument
836 
837  const char **food = NULL;
838  double fSecs;
839 
840  if( argv[n] == "ants" )
841  {
842  food = AsciiAnt;
843  fSecs = 30.0;
844  }
845  else if( argv[n] == "carrots" )
846  {
847  food = AsciiCarrot;
848  fSecs = 10.0;
849  }
850  else if( argv[n] == "grubs" )
851  {
852  food = AsciiGrub;
853  fSecs = 20.0;
854  }
855  else if( argv[n] == "vitamins" )
856  {
857  food = AsciiVitamins;
858  fSecs = 5.0;
859  }
860  else
861  {
862  PCMDERROR(cmdname, "Unknown food '" << argv[n] << "'.");
863  return RC_ERROR;
864  }
865 
866  if( (p->m_sCommonName == "numbat") || (p->m_sCommonName == "zebra") )
867  {
868  showAsciiArt(cout, food, p->m_sSelfie);
869  }
870  else
871  {
872  showAsciiArt(cout, p->m_sSelfie, food);
873  }
874  cout << "Delicious! Give me " << fSecs << " seconds to enjoy." << endl;
875 
876  p->m_eActivity = ActEating;
877  p->m_fActStart = ActivityTime.now();
878  p->m_fActDuration = fSecs;
879 
880  return OK;
881 }
882 
883 /*!
884  * \brief Execute 'walk' command.
885  *
886  * \param argv Command line arguments.
887  *
888  * \return OK(0) on success, negative value on failure.
889  */
890 static int execWalkAnimal(const CmdExtArgVec &argv)
891 {
892  static const char *cmdname = "walk";
893 
894  size_t argc = argv.size();
895  size_t n;
896 
897  if( checkCmd(argv[0], argc, cmdname) != OK )
898  {
899  return RC_ERROR;
900  }
901 
902  n = 1; // skip argv0
903 
904  //
905  // Required arguments.
906  //
907 
908  string animal = argv[n].s();
909 
910  AnimalInfo *p = findAnimal(animal);
911 
912  if( p == NULL )
913  {
914  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
915  return RC_ERROR;
916  }
917 
918  else if( !p->m_bIsAdopted )
919  {
920  PCMDWARN(cmdname, "The " << animal << " is not yours to walk.");
921  return OK;
922  }
923 
924  ++n; // next argument
925 
926  double minutes = argv[n].f();
927 
928  p->m_eActivity = ActWalking;
929  p->m_fActStart = ActivityTime.now();
930  p->m_fActDuration = (double)minutes * 60.0;
931 
932  cout << "You are walking the " << p->m_sCommonName << " for " << minutes
933  << " minutes." << endl;
934 
935  //
936  // Fun stuff
937  //
938  const char *gait;
939 
940  //
941  // I used printf here, instead of cout insertion. Lot easier. But then, I've
942  // always preferred stdio over fstream.
943  //
944  for(int i = 0; i < 40; ++i)
945  {
946  gait = AsciiWalking[i%4][0];
947  printf("%*s%s\r", i, " ", gait);
948  fflush(stdout);
949  usleep(50000);
950  }
951 
952  usleep(250000);
953  printf("%*s\r", 80, " ");
954  fflush(stdout);
955 
956  printf("\n");
957 
958  return OK;
959 }
960 
961 /*!
962  * \brief Execute 'list' command.
963  *
964  * \param argv Command line arguments.
965  *
966  * \return OK(0) on success, negative value on failure.
967  */
968 static int execListAnimals(const CmdExtArgVec &argv)
969 {
970  static const char *cmdname = "list";
971 
972  for(size_t i = 0; i < NumOfAnimals; ++i)
973  {
974  cout << Animals[i].m_sCommonName << " ";
975  }
976 
977  cout << endl;
978 
979  return OK;
980 }
981 
982 /*!
983  * \brief Execute 'get' command.
984  *
985  * \param argv Command line arguments.
986  *
987  * \return OK(0) on success, negative value on failure.
988  */
989 static int execGetPetsState(const CmdExtArgVec &argv)
990 {
991  static const char *cmdname = "get";
992 
993  size_t argc = argv.size();
994  size_t n;
995 
996  // optional arguments defaults
997  long eState = 0;
998 
999  if( checkCmd(argv[0], argc, cmdname) != OK )
1000  {
1001  return RC_ERROR;
1002  }
1003 
1004  n = 1; // skip argv0
1005 
1006  //
1007  // Required arguments.
1008  //
1009 
1010  string animal = argv[n].s();
1011 
1012  AnimalInfo *p = findAnimal(animal);
1013 
1014  if( p == NULL )
1015  {
1016  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
1017  return RC_ERROR;
1018  }
1019 
1020  string strAdoptState;
1021  string strActivity;
1022 
1023  strAdoptState = p->m_bIsAdopted? "Adopted": "Not adopted";
1024 
1025  switch( p->m_eActivity )
1026  {
1027  case ActIdle:
1028  strActivity = "Just chill'n dude.";
1029  break;
1030  case ActSleeping:
1031  strActivity = "Sleeping, don't wake me.";
1032  break;
1033  case ActEating:
1034  strActivity = "Num, num, num.";
1035  break;
1036  case ActWalking:
1037  strActivity = "And I'm walking, and walking, walking...";
1038  break;
1039  default:
1040  strActivity = "I dunno what I'm doing.";
1041  break;
1042  }
1043 
1044  ++n; // next argument
1045 
1046  //
1047  // Optional arguments.
1048  //
1049 
1050  //
1051  // Argument 2
1052  //
1053  // State: all | adoption | given | activity | species | selfie
1054  //
1055  if( n < argc )
1056  {
1057  // convert to enum index
1058  eState = argv[n].e();
1059  }
1060 
1061  switch( eState )
1062  {
1063  case 1: // adoption
1064  cout << strAdoptState << endl;
1065  break;
1066  case 2: // given
1067  cout << p->m_strGivenName << endl;
1068  break;
1069  case 3: // activity
1070  cout << strActivity << endl;
1071  break;
1072  case 4: // species
1073  cout << p->m_sCommonName << " (" << p->m_sScientificName << ")" << endl;
1074  break;
1075  case 5: // selfie
1076  cout << p->m_sSelfie;
1077  break;
1078  case 0: // all
1079  cout << p->m_sSelfie;
1080  cout << p->m_sCommonName << " (" << p->m_sScientificName << ")" << endl;
1081  cout << strAdoptState << endl;
1082  cout << p->m_strGivenName << endl;
1083  cout << strActivity << endl;
1084  break;
1085  default:
1086  PCMDERROR(cmdname, "Unknown state '" << argv[n].arg() << "'.");
1087  return RC_ERROR;
1088  break;
1089  }
1090 
1091  return OK;
1092 }
1093 
1094 /*!
1095  * \brief Execute 'reward' command.
1096  *
1097  * \param argv Command line arguments.
1098  *
1099  * \return OK(0) on success, negative value on failure.
1100  */
1101 static int execReward(const CmdExtArgVec &argv)
1102 {
1103  static const char *cmdname = "reward";
1104 
1105  size_t argc = argv.size();
1106  size_t n;
1107 
1108  if( checkCmd(argv[0], argc, cmdname) != OK )
1109  {
1110  return RC_ERROR;
1111  }
1112 
1113  n = 1; // skip argv0
1114 
1115  //
1116  // Required Arguments
1117  //
1118 
1119  string animal = argv[n].s();
1120 
1121  AnimalInfo *p = findAnimal(animal);
1122 
1123  if( p == NULL )
1124  {
1125  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
1126  return RC_ERROR;
1127  }
1128 
1129  else if( !p->m_bIsAdopted )
1130  {
1131  PCMDWARN(cmdname, "The " << animal << " is not yours to reward.");
1132  return OK;
1133  }
1134 
1135  ++n; // next argument
1136 
1137  bool isGood = argv[n].b();
1138 
1139  cout << "Your " << p->m_sCommonName << " ";
1140 
1141  if( p->m_sCommonName == "aardvark" )
1142  {
1143  if( isGood )
1144  {
1145  cout << "is a good girl." << endl;
1146  cout << "She ate all of those pesky ants pilfering food in the kitchen."
1147  << endl;
1148  }
1149  else
1150  {
1151  cout << "was a bad girl." << endl;
1152  cout << "She dug under the foundation again." << endl;
1153  }
1154  }
1155 
1156  else if( p->m_sCommonName == "mandrill" )
1157  {
1158  if( isGood )
1159  {
1160  cout << "is a good boy." << endl;
1161  cout << "You are now well groomed and free of fleas." << endl;
1162  }
1163  else
1164  {
1165  cout << "is a mischieveus boy." << endl;
1166  cout << "Why does he love reality tv so much?" << endl;
1167  }
1168  }
1169 
1170  else if( p->m_sCommonName == "numbat" )
1171  {
1172  if( isGood )
1173  {
1174  cout << "is a good lad." << endl;
1175  cout << "Those termites won't be invading any time soon." << endl;
1176  }
1177  else
1178  {
1179  cout << "is a snarky lad." << endl;
1180  cout << "He needs to quit putting his long, narrow tongue where it "
1181  << "doesn't belong!" << endl;
1182  }
1183  }
1184 
1185  else if( p->m_sCommonName == "zebra" )
1186  {
1187  if( isGood )
1188  {
1189  cout << "is a good lass." << endl;
1190  cout << "You rode her all the way to town this time without being "
1191  << "thrown." << endl;
1192  }
1193  else
1194  {
1195  cout << "is a very naughty lass." << endl;
1196  cout << "She ate all of the apples ripening on the tree." << endl;
1197  }
1198  }
1199 
1200  else
1201  {
1202  cout << " is " << (isGood? "good": "not good") << "." << endl;
1203  }
1204 
1205  return OK;
1206 }
1207 
1208 /*!
1209  * \brief Execute 'save' command.
1210  *
1211  * \param argv Command line arguments.
1212  *
1213  * \return OK(0) on success, negative value on failure.
1214  */
1215 static int execSave(const CmdExtArgVec &argv)
1216 {
1217  static const char *cmdname = "save";
1218 
1219  size_t argc = argv.size();
1220  size_t n;
1221 
1222  if( checkCmd(argv[0], argc, cmdname) != OK )
1223  {
1224  return RC_ERROR;
1225  }
1226 
1227  n = 1; // skip argv0
1228 
1229  //
1230  // Required arguments.
1231  //
1232 
1233  string animal = argv[n].s();
1234 
1235  AnimalInfo *p = findAnimal(animal);
1236 
1237  if( p == NULL )
1238  {
1239  PCMDERROR(cmdname, "Unknown animal '" << animal << "'.");
1240  return RC_ERROR;
1241  }
1242 
1243  ++n; // next argument
1244 
1245  string filename = argv[n].s();
1246  bool bSave = false;
1247 
1248  if( access(filename.c_str(), F_OK) == 0 )
1249  {
1250  string line;
1251  string ans;
1252 
1253  cout << "Overwrite file '" << argv[n] << "'? [ny] ";
1254 
1255  if( std::getline(cin, line) )
1256  {
1257  istringstream iss(line);
1258  if( (iss >> ans) && (ans == "y") )
1259  {
1260  bSave = true;
1261  }
1262  }
1263  }
1264  else
1265  {
1266  bSave = true;
1267  }
1268 
1269  if( bSave )
1270  {
1271  ofstream selfie;
1272 
1273  selfie.open(filename.c_str());
1274 
1275  if( selfie.is_open() )
1276  {
1277  selfie << p->m_sSelfie;
1278  selfie.close();
1279  cout << "A " << p->m_sCommonName << " selfie writen to file '"
1280  << argv[n] << "'." << endl;
1281  }
1282  else
1283  {
1284  PCMDERROR(cmdname, "Failed to open file '" << argv[n] << "'.");
1285  }
1286  }
1287 
1288  return OK;
1289 }
1290 
1291 /*!
1292  * \brief Command description and exectuion structure.
1293  */
1295 {
1296  CmdDesc m_desc; ///< command description and syntax specification
1297  CmdExec2Func m_fnExec; ///< command execution function
1298 };
1299 
1300 /*!
1301  * \brief The command descriptions.
1302  */
1304 {
1305  { { "adopt",
1306  "adopt <animal>",
1307  "Adopt an available animal",
1308  "Adopt a cuddly animal. In order to name, feed, walk, sleep, or reward "
1309  "an animal, "
1310  "you must adopt first. See the 'list' command for list of animals and "
1311  "the 'get' command for an animal's current state of being.\n\n"
1312  "Demonstrates a variable with default 'word' type."
1313  },
1314  execAdopt
1315  },
1316 
1317  { { "sleep",
1318  "sleep <animal> [<seconds:int>]",
1319  "Sleep for some <seconds> because, why not?",
1320  "Put your adopted animal to sleep (no, not kill you brute). "
1321  "The <animal> goes to sleep for the given seconds.\n"
1322  " Default: 10 seconds\n\n"
1323  "Demonstrates an optional integer variable argument."
1324  },
1325  execSleep
1326  },
1327 
1328  { { "name",
1329  "name <animal> <itsname:multiword>",
1330  "Name your cute adopted pet.",
1331  "Name your adopted animal. Double qoute '\"' the name if it contains "
1332  "any whitespace.\n\n"
1333  "Demonstrates a multword variable argument type."
1334  },
1336  },
1337 
1338  { { "namaste",
1339  "namaste",
1340  "Bow to the divine ewe.",
1341  "Say namaste.\n\n"
1342  "Demonstrates commands with similar names such as 'name' and 'namaste'."
1343  },
1344  execNamaste
1345  },
1346 
1347  // feed, 3 forms
1348  { { "feed",
1349  "feed {aardvark | mandrill | numbat} {ants | grubs}\n"
1350  "feed {mandrill | zebra} carrots\n"
1351  "feed <animal:re(^a.+k$|^m.+l$|^n.+t$|^z.+a$)> vitamins",
1352  "Feed your pet some nutritious food.",
1353  "Feed your adopted animal. Of course, the food has to match the "
1354  "animal's diet.\n\n"
1355  "Demonstrates multi-form commands and enumerated literal arguments.\n"
1356  "Demonstrates a regular expresson variable argument type."
1357  },
1359  },
1360 
1361  { { "walk",
1362  "walk <animal> <minutes:fpn(0.5,1:3)>",
1363  "Walk your adopted pet for some fun-filled <minutes>",
1364  "Walk your adopted animal for the specified minutes.\n\n"
1365  "Demonstrates a floating-point number variable argument."
1366  },
1368  },
1369 
1370  { { "list",
1371  "list",
1372  "Get the list of animals.",
1373  },
1375  },
1376 
1377  { { "get",
1378  "get <animal> [{all | adoption | given | activity | species | selfie}]",
1379  "Get an animal's current (partial) state.",
1380  "Get the animal's state of being.\n"
1381  " Default: all\n\n"
1382  "Demonstrates optional enumerated literal arguments."
1383  },
1385  },
1386 
1387  { { "reward",
1388  "reward <animal:identifier> <good:bool>",
1389  "Do [not] reward your pet for its behaviour.",
1390  "Reward your adopted animal or not.\n\n"
1391  "Demonstrates a identifier variable argument.\n"
1392  "Demonstrates a boolean variable argument."
1393  },
1394  execReward
1395  },
1396 
1397  { { "save",
1398  "save <animal:word> <fname:file>",
1399  "Save ascii animal to file.",
1400  "Save an animal's ASCII redention to a file. The art is fantastic btw."
1401  "If the file exist, you will be prompted if you wish to overwrite.\n\n"
1402  "Demonstrates a file variable argument type."
1403  },
1404  execSave
1405  }
1406 };
1407 
1408 /*!
1409  * \brief Number of commands.
1410  */
1411 const size_t NumOfCmds = arraysize(Commands);
1412 
1413 /*!
1414  * \brief Find command by name.
1415  *
1416  * \param strName Command to find.
1417  *
1418  * \return On succes, returns index to Commands[], otherwise -1 is returned.
1419  */
1420 static int findCommand(const std::string &strName)
1421 {
1422  for(int i = 0; i < NumOfCmds; ++i)
1423  {
1424  if( strName == Commands[i].m_desc.name )
1425  {
1426  return i;
1427  }
1428  }
1429  return -1;
1430 }
1431 
1432 
1433 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1434 // CommandLine Exec3 Command Definitions
1435 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1436 
1437 static const char *CliTestCmdName = "clitest";
1438 
1439 /*!
1440  * \brief Execute clitest tadd subcommand.
1441  *
1442  * Add a new command to interface.
1443  *
1444  * \param cli Command line interface.
1445  * \param argv Command line arguments.
1446  *
1447  * \return OK(0) on success, negative value on failure.
1448  */
1449 static int execTAdd(CommandLine &cli, const CmdExtArgVec &argv)
1450 {
1451  size_t nCmd = 0; // index to command name
1452  size_t nArg = 1; // index to current working argument
1453  int i;
1454  int nUid;
1455  int rc = RC_ERROR;
1456 
1457  if( nArg >= argv.size() )
1458  {
1459  PCMDERROR(CliTestCmdName, argv[nCmd] << ": No <cmd> specified.");
1460  }
1461  else if( (i = findCommand(argv[nArg].s())) < 0 )
1462  {
1463  PCMDERROR(CliTestCmdName, argv[nCmd]
1464  << ": Unknown <cmd> '" << argv[nArg] << "'.");
1465  }
1466  else if( cli.hasCmd(argv[nArg].s()) )
1467  {
1468  PCMDERROR(CliTestCmdName, argv[nCmd]
1469  << ": The <cmd> '" << argv[nArg]
1470  << "' is already present in the interface.");
1471  }
1472  else if( (nUid = cli.addCommand(Commands[i].m_desc, Commands[i].m_fnExec)) ==
1473  NoUid )
1474  {
1475  PCMDERROR(CliTestCmdName, argv[nCmd]
1476  << ": Failed to add <cmd> '" << argv[nArg] << "': "
1477  << cli.getErrorStr() << ".");
1478  }
1479  else if( (rc = cli.compile()) != OK )
1480  {
1481  PCMDERROR(CliTestCmdName, argv[nCmd]
1482  << ": Failed to (re)compile interface: "
1483  << cli.getErrorStr() << ".");
1484  }
1485  else
1486  {
1487  cout << "Command '" << argv[nArg] << ", uid(" << nUid << ") added."
1488  << endl;
1489  rc = OK;
1490  }
1491 
1492  return rc;
1493 }
1494 
1495 /*!
1496  * \brief Execute clitest tbt subcommand.
1497  *
1498  * Print interface attribute.
1499  *
1500  * \param cli Command line interface.
1501  * \param argv Command line arguments.
1502  *
1503  * \return OK(0) on success, negative value on failure.
1504  */
1505 static int execTBt(CommandLine &cli, const CmdExtArgVec &argv)
1506 {
1507  cli.backtrace(cout, true);
1508 
1509  return OK;
1510 }
1511 
1512 /*!
1513  * \brief Execute clitest tcompile subcommand.
1514  *
1515  * (Re)compile the interface.
1516  *
1517  * \param cli Command line interface.
1518  * \param argv Command line arguments.
1519  *
1520  * \return OK(0) on success, negative value on failure.
1521  */
1522 static int execTCompile(CommandLine &cli, const CmdExtArgVec &argv)
1523 {
1524  size_t nCmd = 0; // index to command name
1525  int rc;
1526 
1527  if( (rc = cli.compile()) != OK )
1528  {
1529  PCMDERROR(CliTestCmdName, argv[nCmd]
1530  << ": Failed to (re)compile interface: "
1531  << cli.getErrorStr() << ".");
1532  rc = RC_ERROR;
1533  }
1534  else
1535  {
1536  cout << "Compiled " << cli.numOfCmds() << " commands." << endl;
1537  rc = OK;
1538  }
1539 
1540  return rc;
1541 }
1542 
1543 /*!
1544  * \brief Execute clitest tdump subcommand.
1545  *
1546  * Dump specified command or full command-line interface.
1547  *
1548  * \param cli Command line interface.
1549  * \param argv Command line arguments.
1550  *
1551  * \return OK(0) on success, negative value on failure.
1552  */
1553 static int execTDump(CommandLine &cli, const CmdExtArgVec &argv)
1554 {
1555  size_t nCmd = 0; // index to command name
1556  size_t nArg = 1; // index to current working argument
1557  int rc;
1558 
1559  // dump command definition
1560  if( nArg < argv.size() )
1561  {
1562  if( cli.at(argv[nArg].s()).getUid() == NoUid )
1563  {
1564  PCMDERROR(CliTestCmdName, argv[nCmd]
1565  << ": The <cmd> '" << argv[nArg]
1566  << "' is not present in the interface.");
1567  rc = RC_ERROR;
1568  }
1569  else
1570  {
1571  cout << cli.at(argv[nArg].s()) << endl; // dump command
1572  rc = OK;
1573  }
1574  }
1575 
1576  // dump full interface
1577  else
1578  {
1579  cout << cli << endl;
1580  rc = OK;
1581  }
1582 
1583  return rc;
1584 }
1585 
1586 /*!
1587  * \brief Execute clitest tkbhit subcommand.
1588  *
1589  * Loop to check non-blocking input.
1590  *
1591  * \param cli Command line interface.
1592  * \param argv Command line arguments.
1593  *
1594  * \return OK(0) on success, negative value on failure.
1595  */
1596 static int execTKbhit(CommandLine &cli, const CmdExtArgVec &argv)
1597 {
1598  int c = 0;
1599 
1600  cout << "Press any key, 's' to stop test." << endl;
1601 
1602  while( c != 's' )
1603  {
1604  if( cli.kbhit() )
1605  {
1606  c = getchar();
1607  cout << " \r" << std::flush;
1608  switch( c )
1609  {
1610  case '\n':
1611  cout << "\\n";
1612  break;
1613  case '\t':
1614  cout << "\\t";
1615  break;
1616  default:
1617  cout << (char)c;
1618  break;
1619  }
1620  cout << "\r" << std::flush;
1621  }
1622  }
1623 
1624  cout << endl;
1625 
1626  return OK;
1627 }
1628 
1629 /*!
1630  * \brief Execute clitest tprint subcommand.
1631  *
1632  * Print interface attribute.
1633  *
1634  * \param cli Command line interface.
1635  * \param argv Command line arguments.
1636  *
1637  * \return OK(0) on success, negative value on failure.
1638  */
1639 static int execTPrint(CommandLine &cli, const CmdExtArgVec &argv)
1640 {
1641  size_t nCmd = 0; // index to command name
1642  size_t nArg = 1; // index to current working argument
1643  int rc = OK;
1644 
1645  if( nArg >= argv.size() )
1646  {
1647  PCMDERROR(CliTestCmdName, argv[nCmd] << ": No <attr> specified.");
1648  rc = RC_ERROR;
1649  }
1650  else if( argv[nArg] == "name" ) // interface name
1651  {
1652  cout << cli.getName() << endl;
1653  }
1654  else if( argv[nArg] == "prompt" ) // current prompt string
1655  {
1656  cout << cli.getPrompt() << endl;
1657  }
1658  else if( argv[nArg] == "numcmds" ) // number of added commands
1659  {
1660  cout << cli.numOfCmds() << endl;
1661  }
1662  else if( argv[nArg] == "errstr" ) // last error
1663  {
1664  cout << cli.getErrorStr() << endl;
1665  }
1666  else
1667  {
1668  PCMDERROR(CliTestCmdName, argv[nCmd]
1669  << ": Unknown <attr> '" << argv[nArg] << "'.");
1670  rc = RC_ERROR;
1671  }
1672 
1673  return rc;
1674 }
1675 
1676 /*!
1677  * \brief Execute clitest tpush subcommand.
1678  *
1679  * Push new prompt.
1680  *
1681  * \param cli Command line interface.
1682  * \param argv Command line arguments.
1683  *
1684  * \return OK(0) on success, negative value on failure.
1685  */
1686 static int execTPush(CommandLine &cli, const CmdExtArgVec &argv)
1687 {
1688  size_t nCmd = 0; // index to command name
1689  size_t nArg = 1; // index to current working argument
1690  int rc;
1691 
1692  // push prompt string
1693  if( nArg < argv.size() )
1694  {
1695  cli.pushPrompt(argv[nArg].s());
1696  rc = OK;
1697  }
1698  else
1699  {
1700  PCMDERROR(CliTestCmdName, argv[nCmd] << ": No <prompt> specified.");
1701  rc = RC_ERROR;
1702  }
1703 
1704  return rc;
1705 }
1706 
1707 /*!
1708  * \brief Execute clitest tremove subcommand.
1709  *
1710  * Remove command from the interface.
1711  *
1712  * \param cli Command line interface.
1713  * \param argv Command line arguments.
1714  *
1715  * \return OK(0) on success, negative value on failure.
1716  */
1717 static int execTRemove(CommandLine &cli, const CmdExtArgVec &argv)
1718 {
1719  size_t nCmd = 0; // index to command name
1720  size_t nArg = 1; // index to current working argument
1721  int nUid;
1722  int rc;
1723 
1724  rc = RC_ERROR;
1725 
1726  if( nArg >= argv.size() )
1727  {
1728  PCMDERROR(CliTestCmdName, argv[nCmd] << ": No <cmd> specified.");
1729  }
1730  else if( (nUid = cli.at(argv[nArg].s()).getUid()) == NoUid )
1731  {
1732  PCMDERROR(CliTestCmdName, argv[nCmd]
1733  << ": The <cmd> '" << argv[nArg]
1734  << "' is not present in the interface.");
1735  }
1736  else if( (rc = cli.removeCommand(argv[nArg].s())) != OK )
1737  {
1738  PCMDERROR(CliTestCmdName, argv[nCmd]
1739  << ": Failed to remove <cmd> '" << argv[nArg] << "': "
1740  << cli.getErrorStr() << ".");
1741  }
1742  else
1743  {
1744  cout << "Command '" << argv[nArg] << "', uid(" << nUid << ") removed."
1745  << endl;
1746  rc = OK;
1747  }
1748 
1749  return rc;
1750 }
1751 
1752 /*!
1753  * \brief Execute command-line interface methods command.
1754  *
1755  * \param cli Command line interface.
1756  * \param argv Command line arguments.
1757  *
1758  * \return OK(0) on success, negative value on failure.
1759  */
1760 static int execCliTest(CommandLine &cli, const CmdExtArgVec &argv)
1761 {
1762  size_t nCmd = 0;
1763  int rc;
1764 
1765  if( checkCmd(argv[nCmd], argv.size()) != OK )
1766  {
1767  rc = RC_ERROR;
1768  }
1769 
1770  //
1771  // Add command to interface.
1772  //
1773  else if( argv[nCmd] == "tadd" )
1774  {
1775  rc = execTAdd(cli, argv);
1776  }
1777 
1778  //
1779  // Backtrace last execution sequence.
1780  //
1781  else if( argv[nCmd] == "tbt" )
1782  {
1783  rc = execTBt(cli, argv);
1784  }
1785 
1786  //
1787  // (Re)compile interface.
1788  //
1789  else if( argv[nCmd] == "tcompile" )
1790  {
1791  rc = execTCompile(cli, argv);
1792  }
1793 
1794  //
1795  // Dump interface component to output stream.
1796  //
1797  else if( argv[nCmd] == "tdump" )
1798  {
1799  rc = execTDump(cli, argv);
1800  }
1801 
1802  //
1803  // Check for keyboard hits.
1804  //
1805  else if( argv[nCmd] == "tkbhit" )
1806  {
1807  rc = execTKbhit(cli, argv);
1808  }
1809 
1810  //
1811  // Print interface attribute.
1812  //
1813  else if( argv[nCmd] == "tprint" )
1814  {
1815  rc = execTPrint(cli, argv);
1816  }
1817 
1818  //
1819  // Push new prompt.
1820  //
1821  else if( argv[nCmd] == "tpush" )
1822  {
1823  rc = execTPush(cli, argv);
1824  }
1825 
1826  //
1827  // Pop current prompt, restore previous.
1828  //
1829  else if( argv[nCmd] == "tpop" )
1830  {
1831  cli.popPrompt();
1832  rc = OK;
1833  }
1834 
1835  //
1836  // Remove command from interface.
1837  //
1838  else if( argv[nCmd] == "tremove" )
1839  {
1840  rc = execTRemove(cli, argv);
1841  }
1842 
1843  else
1844  {
1845  PCMDERROR(CliTestCmdName,
1846  "Do not know how to execute '" << argv[nCmd] << "'.");
1847  rc = RC_ERROR;
1848  }
1849 
1850  return rc;
1851 }
1852 
1853 /*!
1854  * \brief Self-Reference command description and exectuion structure.
1855  */
1857 {
1858  CmdDesc m_desc; ///< command description and syntax specification
1859  CmdExec3Func m_fnExec; ///< command execution function
1860 };
1861 
1862 /*!
1863  * \brief The command descriptions.
1864  */
1866 {
1867  { { CliTestCmdName,
1868  "<clitest:re(^t[abcdkpr].+)> [<modifier:multiword>]",
1869  "Test CommandLine interface features.",
1870  "The 'clitest' command validates command wild carding and provides test "
1871  "functions to validate the CommandLine and underlining classes.\n\n"
1872  "Supported Test Functions:\n"
1873  " tadd <cmd> - Add command to interface.\n"
1874  " tbt - Backtrace log.\n"
1875  " tcompile - (Re)compile interface.\n"
1876  " tdump [<cmd>] - Dump all or <cmd> definitions.\n"
1877  " tkbhit - Check for keyboard hits.\n"
1878  " tprint <attr> - Print attribute, where <attr> is one of:\n"
1879  " name prompt numcmds errstr.\n"
1880  " tpush <prompt> - Push new <prompt> string.\n"
1881  " tpop - Pop current prompt string.\n"
1882  " tremove <cmd> - Remove command from interface.\n\n"
1883  "Demonstrates command name wild carding.\n"
1884  "Demonstrates regular expresson variable argument type."
1885  },
1886  execCliTest
1887  }
1888 };
1889 
1890 /*!
1891  * \brief Number of commands.
1892  */
1893 const size_t NumOfCmd3s = arraysize(Command3s);
1894 
1895 
1896 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1897 // Main Functions
1898 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1899 
1900 /*!
1901  * \brief Main initialization.
1902  *
1903  * \param argc Command-line argument count.
1904  * \param argv Command-line argument list.
1905  *
1906  * \par Exits:
1907  * Program terminates on conversion error.
1908  */
1909 static void mainInit(int argc, char *argv[])
1910 {
1911  // name of this process
1912  Argv0 = basename(argv[0]);
1913 
1914  // parse input options
1915  argv = OptsGet(Argv0, &PkgInfo, &PgmInfo, OptsInfo, true, &argc, argv);
1916 }
1917 
1918 /*!
1919  * \brief Update all animals' state data.
1920  */
1921 static void updateAnimals()
1922 {
1923  double fNow;
1924 
1925  fNow = ActivityTime.now();
1926 
1927  for(size_t i = 0; i < NumOfAnimals; ++i)
1928  {
1929  if( Animals[i].m_eActivity == ActIdle )
1930  {
1931  continue;
1932  }
1933 
1934  if( (fNow - Animals[i].m_fActStart) >= Animals[i].m_fActDuration )
1935  {
1936  Animals[i].m_eActivity = ActIdle;
1937  Animals[i].m_fActStart = 0.0;
1938  Animals[i].m_fActDuration = 0.0;
1939  }
1940  }
1941 }
1942 
1943 /*!
1944  * \brief Load commands into command line.
1945  *
1946  * Loading involves adding all commands and then compiling.
1947  *
1948  * \param cli Command line interface.
1949  *
1950  * \return OK(0) on success, negative value on failure.
1951  */
1952 static int loadCommands(CommandLine &cli)
1953 {
1954  int nUid;
1955  int rc;
1956 
1957  //
1958  // Add "normal" commands to command-line interface.
1959  //
1960  for(size_t i = 0; i < NumOfCmds; ++i)
1961  {
1962  nUid = cli.addCommand(Commands[i].m_desc, Commands[i].m_fnExec);
1963 
1964  if( nUid == NoUid )
1965  {
1966  PERROR("Failed to add command '" << Commands[i].m_desc.name << "'.");
1967  return RC_ERROR;
1968  }
1969  }
1970 
1971  //
1972  // Add "self-referenced" commands to command-line interface.
1973  //
1974  for(size_t i = 0; i < NumOfCmd3s; ++i)
1975  {
1976  nUid = cli.addCommand(Command3s[i].m_desc, Command3s[i].m_fnExec);
1977 
1978  if( nUid == NoUid )
1979  {
1980  PERROR("Failed to add command '" << Command3s[i].m_desc.name << "'.");
1981  return RC_ERROR;
1982  }
1983  }
1984 
1985  //
1986  // Add built-in commands to interface.
1987  //
1991 
1992  //
1993  // Now compile comamnd-line interace.
1994  //
1995  if( (rc = cli.compile()) != OK )
1996  {
1997  PERROR("Compile failed.");
1998  cerr << "(backtrace)" << endl;
1999  cli.backtrace(cerr, true);
2000  }
2001 
2002  return rc;
2003 }
2004 
2005 /*!
2006  * \brief Command line interface main loop.
2007  *
2008  * \param cli Command line interface.
2009  *
2010  * \return OK(0) on success, negative value on failure.
2011  */
2012 static int run(CommandLine &cli)
2013 {
2014  CmdExtArgVec argv; // vector of string input arguments
2015  int rc; // return code
2016 
2017  while( cli.ok() )
2018  {
2019  rc = cli.readCommand(argv);
2020 
2021  updateAnimals();
2022 
2023  if( rc == OK )
2024  {
2025  // see the results of a good command match
2026  //cli.backtrace(cerr);
2027 
2028  if( argv.size() > 0 )
2029  {
2030  rc = cli.execute(argv);
2031 
2032  if( rc == OK )
2033  {
2034  cli.addToHistory(argv);
2035  }
2036  }
2037  }
2038  else
2039  {
2040  PERROR("Bad command.");
2041  // cerr << "backtrace:" << endl;
2042  // cli.backtrace(cerr);
2043  }
2044  }
2045 
2046  return OK;
2047 }
2048 
2049 /*!
2050  * \brief Main.
2051  *
2052  * \param argc Command-line argument count.
2053  * \param argv Command-line argument list.
2054  *
2055  * \return Returns 0 on succes, non-zero on failure.
2056  */
2057 int main(int argc, char* argv[])
2058 {
2059  mainInit(argc, argv);
2060 
2061  if( loadCommands(Cli) != OK )
2062  {
2063  PERROR("Failed to load commands.");
2064  return APP_EC_EXEC;
2065  }
2066 
2067  // debug
2068  //cerr << Cli << endl;
2069 
2070  cout << AsciiAardvark;
2071  cout << CliName << " CommandLine/ReadLine/LogBook/RegEx Example" << endl;
2072  cout << " (enter 'help' for list of commands)" << endl << endl;
2073 
2074  if( run(Cli) != OK )
2075  {
2076  PERROR("Failed to run commands.");
2077  return APP_EC_EXEC;
2078  }
2079 
2080  return APP_EC_OK;
2081 }
2082 
2083 /*!
2084  * \}
2085  */
void popPrompt()
Pop prompt string from stack of prompts.
AnimalInfo Animals[]
The animals.
Definition: rnr_eg_cli.cxx:506
const char ** m_sSelfie
ASCII selfie.
Definition: rnr_eg_cli.cxx:491
Command line extended argument interface.
static OptsInfo_T OptsInfo[]
Command line options information.
Definition: rnr_eg_cli.cxx:100
const string CliPrompt("aaa> ")
CLI prompt.
Command EXTended ARGument class holding parsed command context and the raw and converted argmument va...
Definition: CmdExtArg.h:91
Self-Reference command description and exectuion structure.
static int execWalkAnimal(const CmdExtArgVec &argv)
Execute &#39;walk&#39; command.
Definition: rnr_eg_cli.cxx:890
static const char * AsciiZebra[]
ASCII zebra.
Definition: rnr_eg_cli.cxx:226
Command line interface command add-ons interface.
#define APP_EC_EXEC
execution exit code
Definition: rnr_eg_cli.cxx:74
bool m_bIsAdopted
is [not] adopted
Definition: rnr_eg_cli.cxx:495
virtual int execute(const str::StringVec &argv)
Execute a comamnd with the given arguments.
static const char * AsciiZebraSmall[]
ASCII small zebra.
Definition: rnr_eg_cli.cxx:213
AppCmdExec Commands[]
The command descriptions.
static ostream & operator<<(ostream &os, const char *art[])
ASCII art output stream operator.
Definition: rnr_eg_cli.cxx:367
static void showAsciiArt(ostream &os, const char *art0[], const char *art1[], const char *art2[]=NULL, const char *art3[]=NULL)
Write a set of ASCII art to cout.
Definition: rnr_eg_cli.cxx:387
const size_t NumOfCmd3s
Number of commands.
CmdExec2Func m_fnExec
command execution function
int checkCmd(const CmdExtArg &argv0, int argc, const string strTgtName="")
Check if command input is matched correctly with the command execution.
Definition: rnr_eg_cli.cxx:592
static int execTRemove(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tremove subcommand.
Activity
Animal activities.
Definition: rnr_eg_cli.cxx:475
static int execTDump(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tdump subcommand.
Time functions and class interfaces.
virtual int removeCommand(const int uid)
Remove command from command line interface.
Time ActivityTime
activity time
Definition: rnr_eg_cli.cxx:548
const char * NoName
&#39;no name&#39; name
Definition: rnr_eg_cli.cxx:501
Chronos - God of Time.
Definition: Time.h:63
CmdExec3Func m_fnExec
command execution function
Time class.
Definition: Time.h:192
int(* CmdExec2Func)(const CmdExtArgVec &argv)
Command execution function type, variant 2.
Definition: CommandLine.h:139
static int execAdopt(const CmdExtArgVec &argv)
Execute &#39;adopt&#39; command.
Definition: rnr_eg_cli.cxx:625
static int execTKbhit(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tkbhit subcommand.
void pushPrompt(const std::string &strPrompt)
Push prompt string onto stack of prompts.
virtual int compile()
Compile all added commands.
const int NoUid
Special values.
Definition: CmdCore.h:116
int numOfCmds() const
Get the total number of added commands.
Definition: CommandLine.h:1108
User available command description structure.
Definition: CmdCore.h:85
static int execNameAnimal(const CmdExtArgVec &argv)
Execute &#39;name&#39; command.
Definition: rnr_eg_cli.cxx:737
const char * m_sCommonName
command name
Definition: rnr_eg_cli.cxx:489
int uid() const
Get the argument&#39;s associated parsed command unique id.
Definition: CmdExtArg.h:158
AppCmdExec3 Command3s[]
The command descriptions.
static const char * AsciiAardvark[]
ASCII ardvaark.
Definition: rnr_eg_cli.cxx:125
static const char * AsciiAnt[]
ASCII ant.
Definition: rnr_eg_cli.cxx:260
std::ostream & backtrace(std::ostream &os, const bool bAll=false) const
Insert trace and error log backtrace into output stream.
const char * m_sScientificName
scientific name
Definition: rnr_eg_cli.cxx:490
const CmdDef & at(const int uid) const
Get the command definition with the unique id.
double m_fActDuration
activitiy uninterrupted duration
Definition: rnr_eg_cli.cxx:498
Command line interface class interface.
Commands.
Definition: CmdAddOns.h:81
std::vector< CmdExtArg > CmdExtArgVec
vector of ext args type
Definition: CmdExtArg.h:397
static int run(CommandLine &cli)
Command line interface main loop.
virtual int addCommand(const CmdDesc &desc)
Add a command to the command line interface.
#define APP_EC_OK
success exit code
Definition: rnr_eg_cli.cxx:72
walking
Definition: rnr_eg_cli.cxx:480
static int execSave(const CmdExtArgVec &argv)
Execute &#39;save&#39; command.
int numOfRequiredArgs(int uid, int iform) const
Get the total number of required arguments.
static void mainInit(int argc, char *argv[])
Main initialization.
double now()
Get the current time, indentified by CLOCK_REALTIME, since the last Epoch.
Definition: Time.cxx:208
static int loadCommands(CommandLine &cli)
Load commands into command line.
Activity m_eActivity
current activity
Definition: rnr_eg_cli.cxx:496
static const char * AsciiGrub[]
ASCII grub.
Definition: rnr_eg_cli.cxx:273
static const PkgInfo_T PkgInfo
Definition: version.h:45
static const char * AsciiCarrot[]
ASCII carrot.
Definition: rnr_eg_cli.cxx:298
static char * Argv0
the command
Definition: rnr_eg_cli.cxx:76
static int execTBt(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tbt subcommand.
static const char * AsciiVitamins[]
ASCII bottle of vitamins.
Definition: rnr_eg_cli.cxx:325
CommandLine Cli(CliName, CliPrompt)
the CLI
int addBtEnableCommand(CommandLine &cli, const std::string &strName="bt")
Add the core &#39;bt&#39; command to the command-line interface.
static int execGetPetsState(const CmdExtArgVec &argv)
Execute &#39;get&#39; command.
Definition: rnr_eg_cli.cxx:989
static int execCliTest(CommandLine &cli, const CmdExtArgVec &argv)
Execute command-line interface methods command.
size_t NumOfAnimals
number of animals
Definition: rnr_eg_cli.cxx:525
static int execTPrint(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tprint subcommand.
static int execTAdd(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tadd subcommand.
static AnimalInfo * findAnimal(const string &strCommonName)
Find the animal with the common name.
Definition: rnr_eg_cli.cxx:536
static int execTCompile(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tcompile subcommand.
Package version information.
static int execListAnimals(const CmdExtArgVec &argv)
Execute &#39;list&#39; command.
Definition: rnr_eg_cli.cxx:968
int(* CmdExec3Func)(CommandLine &cli, const CmdExtArgVec &argv)
Command execution function type, variant 3.
Definition: CommandLine.h:154
double m_fActStart
activity start time
Definition: rnr_eg_cli.cxx:497
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
Command description and exectuion structure.
int addHelpCommand(CommandLine &cli, const std::string &strName="help")
Add the core &#39;help&#39; command to the command-line interface.
CmdDesc m_desc
command description and syntax specification
idle, doing nothing
Definition: rnr_eg_cli.cxx:477
bool ok() const
Test if command-line interface is ok to continue.
Definition: CommandLine.h:481
bool hasCmd(const int uid) const
Test if command exists.
eating
Definition: rnr_eg_cli.cxx:479
int main(int argc, char *argv[])
Main.
#define PERROR(_err)
Error and warning printing macros.
Definition: rnr_eg_cli.cxx:564
static int execSleep(const CmdExtArgVec &argv)
Execute &#39;sleep&#39; command.
Definition: rnr_eg_cli.cxx:672
sleeping
Definition: rnr_eg_cli.cxx:478
static int execNamaste(const CmdExtArgVec &argv)
Execute &#39;namaste&#39; command.
Definition: rnr_eg_cli.cxx:785
const std::string & getPrompt() const
Get the current prompt string.
const std::string & getName() const
Get command line interface&#39;s name.
Definition: CommandLine.h:1040
static const char * AsciiNumbat[]
ASCII numbat.
Definition: rnr_eg_cli.cxx:178
static const char * AsciiMandrill[]
ASCII Mandrill.
Definition: rnr_eg_cli.cxx:151
static OptsPgmInfo_T PgmInfo
Program information.
Definition: rnr_eg_cli.cxx:81
static int execTPush(CommandLine &cli, const CmdExtArgVec &argv)
Execute clitest tpush subcommand.
const size_t NumOfCmds
Number of commands.
const string CliName("Adopt-An-Animal")
CLI name.
static int findCommand(const std::string &strName)
Find command by name.
RoadNarrows Robotics.
Definition: Camera.h:74
string m_strGivenName
adopted given name by owner
Definition: rnr_eg_cli.cxx:494
const std::string & getErrorStr() const
Get the most recent error.
virtual void addToHistory(const str::StringVec &argv)
Add command to history.
CommandLine class.
Definition: CommandLine.h:444
CmdDesc m_desc
command description and syntax specification
static const char * AsciiWalking[][2]
ASCII walk sequence.
Definition: rnr_eg_cli.cxx:351
static void updateAnimals()
Update all animals&#39; state data.
static int execReward(const CmdExtArgVec &argv)
Execute &#39;reward&#39; command.
static int execFeedAnimal(const CmdExtArgVec &argv)
Execute &#39;feed&#39; command.
Definition: rnr_eg_cli.cxx:801
int addQuitCommand(CommandLine &cli, const std::string &strName="quit")
Add the core &#39;quit&#39; command to the command-line interface.