1/*
2 Copyright (c) 2009, Kevin Rogovin All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are
6met:
7
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution. Neither the name of the Kevin Rogovin or
14 * kRogue Technologies nor the names of its contributors may
15 * be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*/
30
31
32/** \file generic_command_line.hpp */
33#ifndef FASTUIDRAW_DEMO_GENERIC_COMMAND_LINE_HPP
34#define FASTUIDRAW_DEMO_GENERIC_COMMAND_LINE_HPP
35
36#include <iostream>
37#include <vector>
38#include <algorithm>
39#include <map>
40#include <list>
41#include <set>
42#include <string>
43#include <sstream>
44#include <ciso646>
45#include <fastuidraw/util/util.hpp>
46
47class command_line_register;
48class command_line_argument;
49
50
51//!\class command_line_register
52/*!
53 A command_line_register walks the a command
54 line argument list, calling its command_line_argument
55 children's check_arg() method to get the values
56 from a command line argument list.
57 */
58class command_line_register:fastuidraw::noncopyable
59{
60public:
61 command_line_register(void)
62 {}
63
64 ~command_line_register();
65
66 void
67 parse_command_line(int argc, char **argv);
68
69 void
70 parse_command_line(const std::vector<std::string> &strings);
71
72
73 void
74 print_help(std::ostream&) const;
75
76 void
77 print_detailed_help(std::ostream&) const;
78
79private:
80 friend class command_line_argument;
81 std::vector<command_line_argument*> m_children;
82};
83
84//!\class command_line_argument
85/*!
86 A command_line_argument reads from an argument
87 list to set a value.
88 */
89class command_line_argument:fastuidraw::noncopyable
90{
91public:
92 explicit
93 command_line_argument(command_line_register &parent):
94 m_parent(&parent)
95 {
96 m_location=parent.m_children.size();
97 parent.m_children.push_back(this);
98 }
99
100 command_line_argument(void):
101 m_location(-1),
102 m_parent(nullptr)
103 {}
104
105 virtual
106 ~command_line_argument();
107
108 void
109 attach(command_line_register &p)
110 {
111 FASTUIDRAWassert(m_parent==nullptr);
112
113 m_parent=&p;
114 m_location=m_parent->m_children.size();
115 m_parent->m_children.push_back(this);
116 }
117
118 command_line_register*
119 parent(void)
120 {
121 return m_parent;
122 }
123 //!\fn check_arg
124 /*!
125 To be implemented by a dervied class.
126
127 \param args the argv argument of main(int argc, char **argv)
128 as an array of std::string, note that
129 args.size() equals argc
130 \param location current index into args[] being examined
131
132 returns the number of arguments consumed, 0 is allowed.
133
134 */
135 virtual
136 int
137 check_arg(const std::vector<std::string> &args,
138 int location)=0;
139
140 virtual
141 void
142 print_command_line_description(std::ostream&) const=0;
143
144 virtual
145 void
146 print_detailed_description(std::ostream&) const=0;
147
148 static
149 std::string
150 format_description_string(const std::string &name, const std::string &desc);
151
152 static
153 std::string
154 produce_formatted_detailed_description(const std::string &cmd,
155 const std::string &desc);
156
157 static
158 std::string
159 tabs_to_spaces(const std::string &pin);
160
161private:
162 friend class command_line_register;
163 int m_location;
164 command_line_register *m_parent;
165};
166
167/*
168 not a command line option, but prints a separator for detailed help
169 */
170class command_separator:public command_line_argument
171{
172public:
173 command_separator(const std::string &label,
174 command_line_register &parent):
175 command_line_argument(parent),
176 m_label(label)
177 {}
178
179 virtual
180 int
181 check_arg(const std::vector<std::string> &, int)
182 {
183 return 0;
184 }
185
186 virtual
187 void
188 print_command_line_description(std::ostream&) const
189 {}
190
191 virtual
192 void
193 print_detailed_description(std::ostream &ostr) const
194 {
195 ostr << "\n\n---------- " << m_label << " ------------------\n";
196 }
197
198private:
199 std::string m_label;
200};
201
202/*
203 not a command line option, but prints an about
204 */
205class command_about:public command_line_argument
206{
207public:
208 command_about(const std::string &label,
209 command_line_register &parent):
210 command_line_argument(parent),
211 m_label(tabs_to_spaces(format_description_string("", label)))
212 {}
213
214 virtual
215 int
216 check_arg(const std::vector<std::string> &, int)
217 {
218 return 0;
219 }
220
221 virtual
222 void
223 print_command_line_description(std::ostream&) const
224 {}
225
226 virtual
227 void
228 print_detailed_description(std::ostream &ostr) const
229 {
230 ostr << "\n\n " << m_label << "\n";
231 }
232
233private:
234 std::string m_label;
235};
236
237template<typename T>
238void
239readvalue_from_string(T &value,
240 const std::string &value_string)
241{
242 std::istringstream istr(value_string);
243 istr >> value;
244}
245
246template<>
247inline
248void
249readvalue_from_string(std::string &value,
250 const std::string &value_string)
251{
252 value=value_string;
253}
254
255template<>
256inline
257void
258readvalue_from_string(bool &value, const std::string &value_string)
259{
260 if (value_string == std::string("on")
261 || value_string == std::string("true"))
262 {
263 value = true;
264 }
265 else if (value_string==std::string("off")
266 || value_string==std::string("false"))
267 {
268 value = false;
269 }
270}
271
272template<typename T>
273void
274writevalue_to_stream(const T&value, std::ostream &ostr)
275{
276 ostr << value;
277}
278
279template<>
280inline
281void
282writevalue_to_stream(const bool &value, std::ostream &ostr)
283{
284 if (value)
285 {
286 ostr << "on/true";
287 }
288 else
289 {
290 ostr << "off/false";
291 }
292}
293
294class LabelDescription
295{
296public:
297 void
298 set_description(const std::string &desc)
299 {
300 m_description = desc;
301 }
302
303 void
304 add_value(const std::string &v)
305 {
306 m_values_as_set.insert(v);
307 m_values_as_list.push_back(v);
308 }
309
310 const std::string&
311 description(void) const
312 {
313 return m_description;
314 }
315
316 const std::list<std::string>&
317 values(void) const
318 {
319 return m_values_as_list;
320 }
321
322 bool
323 has_value(const std::string &v)
324 {
325 return m_values_as_set.find(v) != m_values_as_set.end();
326 }
327
328private:
329 std::string m_description;
330 std::set<std::string> m_values_as_set;
331 std::list<std::string> m_values_as_list;
332};
333
334template<typename T>
335class enumerated_string_type
336{
337public:
338 std::map<std::string, T> m_value_strings;
339 std::map<T, LabelDescription> m_value_Ts;
340
341 enumerated_string_type&
342 add_entry(const std::string &label, T v, const std::string &description)
343 {
344 LabelDescription &L(m_value_Ts[v]);
345
346 m_value_strings[label] = v;
347 m_value_Ts[v].set_description(description);
348 m_value_Ts[v].add_value(label);
349 return *this;
350 }
351
352 enumerated_string_type&
353 add_entry_alias(const std::string &label, T v)
354 {
355 m_value_strings[label] = v;
356 m_value_Ts[v].add_value(label);
357 return *this;
358 }
359};
360
361
362template<typename T>
363class enumerated_type
364{
365public:
366 T m_value;
367 enumerated_string_type<T> m_label_set;
368
369 enumerated_type(T v, const enumerated_string_type<T> &L):
370 m_value(v),
371 m_label_set(L)
372 {}
373};
374
375
376
377template<typename T>
378class command_line_argument_value:public command_line_argument
379{
380public:
381
382 command_line_argument_value(T v, const std::string &nm,
383 const std::string &desc,
384 command_line_register &p,
385 bool print_at_set=true):
386 command_line_argument(p),
387 m_name(nm),
388 m_set_by_command_line(false),
389 m_print_at_set(print_at_set),
390 m_value(v)
391 {
392 std::ostringstream ostr;
393 ostr << "\n\t"
394 << m_name << " (default value=";
395
396 writevalue_to_stream(m_value, ostr);
397 ostr << ") " << format_description_string(m_name, desc);
398 m_description=tabs_to_spaces(ostr.str());
399 }
400
401 const std::string&
402 label(void) const
403 {
404 return m_name;
405 }
406
407 bool
408 set_by_command_line(void) const
409 {
410 return m_set_by_command_line;
411 }
412
413 virtual
414 void
415 on_set_by_command_line(void)
416 {}
417
418 virtual
419 int
420 check_arg(const std::vector<std::string> &argv, int location)
421 {
422 const std::string &str(argv[location]);
423 std::string::const_iterator iter;
424 int argc(argv.size());
425
426 iter = std::find(str.begin(), str.end(), '=');
427 if (iter == str.end())
428 {
429 iter = std::find(str.begin(), str.end(), ':');
430 }
431
432 if (iter != str.end() && m_name == std::string(str.begin(), iter) )
433 {
434 std::string val(iter+1, str.end());
435
436 readvalue_from_string(m_value, val);
437 if (m_print_at_set)
438 {
439 std::cout << "\n\t" << m_name
440 << " set to ";
441 writevalue_to_stream(m_value, std::cout);
442 }
443
444 m_set_by_command_line = true;
445 on_set_by_command_line();
446 return 1;
447 }
448 else if (location < argc-1 && str == m_name)
449 {
450 const std::string &val(argv[location+1]);
451
452 readvalue_from_string(m_value, val);
453
454 if (m_print_at_set)
455 {
456 std::cout << "\n\t" << m_name
457 << " set to ";
458 writevalue_to_stream(m_value, std::cout);
459 }
460 m_set_by_command_line = true;
461 on_set_by_command_line();
462 return 2;
463 }
464 return 0;
465 }
466
467 virtual
468 void
469 print_command_line_description(std::ostream &ostr) const
470 {
471 ostr << "[" << m_name << "=value] "
472 << "[" << m_name << ":value] "
473 << "[" << m_name << " value]";
474 }
475
476 virtual
477 void
478 print_detailed_description(std::ostream &ostr) const
479 {
480 ostr << m_description;
481 }
482
483 const T&
484 value(void) const
485 {
486 return m_value;
487 }
488
489 T&
490 value(void)
491 {
492 return m_value;
493 }
494
495 const std::string&
496 name(void) const
497 {
498 return m_name;
499 }
500
501private:
502 std::string m_name;
503 std::string m_description;
504 bool m_set_by_command_line, m_print_at_set;
505 T m_value;
506};
507
508
509template<typename T>
510class enumerated_command_line_argument_value:public command_line_argument
511{
512public:
513 enumerated_command_line_argument_value(T v, const enumerated_string_type<T> &L,
514 const std::string &nm, const std::string &desc,
515 command_line_register &p,
516 bool print_at_set=true):
517 command_line_argument(p),
518 m_name(nm),
519 m_set_by_command_line(false),
520 m_print_at_set(print_at_set),
521 m_value(v,L)
522 {
523 std::ostringstream ostr;
524 typename std::map<T, LabelDescription>::const_iterator iter, end;
525
526 ostr << "\n\t"
527 << m_name << " (default value=";
528
529 iter = m_value.m_label_set.m_value_Ts.find(v);
530 if (iter != m_value.m_label_set.m_value_Ts.end())
531 {
532 ostr << iter->second.values().front();
533 }
534 else
535 {
536 ostr << v;
537 }
538
539 ostr << ")";
540 std::ostringstream ostr_desc;
541 ostr_desc << desc << " Possible values:\n\n";
542 for(iter = m_value.m_label_set.m_value_Ts.begin(),
543 end = m_value.m_label_set.m_value_Ts.end();
544 iter != end; ++iter)
545 {
546 const std::list<std::string> &contents(iter->second.values());
547 for (std::list<std::string>::const_iterator siter = contents.begin();
548 siter != contents.end(); ++siter)
549 {
550 if (siter != contents.begin())
551 {
552 ostr_desc << "/";
553 }
554 ostr_desc << *siter;
555 }
556 ostr_desc << ": " << iter->second.description() << "\n\n";
557 }
558 ostr << format_description_string(m_name, ostr_desc.str());
559 m_description = tabs_to_spaces(ostr.str());
560 }
561
562 bool
563 set_by_command_line(void)
564 {
565 return m_set_by_command_line;
566 }
567
568 virtual
569 int
570 check_arg(const std::vector<std::string> &argv, int location)
571 {
572 const std::string &str(argv[location]);
573 std::string::const_iterator iter;
574 int argc(argv.size());
575
576 iter = std::find(str.begin(), str.end(), '=');
577 if (iter == str.end())
578 {
579 iter = std::find(str.begin(), str.end(), ':');
580 }
581
582 if (iter != str.end() && m_name == std::string(str.begin(), iter) )
583 {
584 std::string val(iter+1, str.end());
585 typename std::map<std::string, T>::const_iterator iter;
586
587 iter = m_value.m_label_set.m_value_strings.find(val);
588 if (iter != m_value.m_label_set.m_value_strings.end())
589 {
590 m_value.m_value = iter->second;
591 if (m_print_at_set)
592 {
593 std::cout << "\n\t" << m_name
594 << " set to " << val;
595 m_set_by_command_line = true;
596 }
597 }
598 return 1;
599 }
600 else if (location < argc-1 && str == m_name)
601 {
602 const std::string &val(argv[location+1]);
603 typename std::map<std::string, T>::const_iterator iter;
604
605 iter = m_value.m_label_set.m_value_strings.find(val);
606 if (iter != m_value.m_label_set.m_value_strings.end())
607 {
608 m_value.m_value = iter->second;
609 if (m_print_at_set)
610 {
611 std::cout << "\n\t" << m_name
612 << " set to " << val;
613 m_set_by_command_line = true;
614 }
615 }
616 return 2;
617 }
618 return 0;
619 }
620
621 virtual
622 void
623 print_command_line_description(std::ostream &ostr) const
624 {
625 ostr << "[" << m_name << "=value] "
626 << "[" << m_name << ":value] "
627 << "[" << m_name << " value]";
628 }
629
630 virtual
631 void
632 print_detailed_description(std::ostream &ostr) const
633 {
634 ostr << m_description;
635 }
636
637 T
638 value(void) const
639 {
640 return m_value.m_value;
641 }
642
643 T&
644 value(void)
645 {
646 return m_value.m_value;
647 }
648
649 const std::string&
650 name(void) const
651 {
652 return m_name;
653 }
654
655private:
656 std::string m_name;
657 std::string m_description;
658 bool m_set_by_command_line, m_print_at_set;
659 enumerated_type<T> m_value;
660};
661
662#endif
663