1 | /* |
2 | Copyright (c) 2009, Kevin Rogovin All rights reserved. |
3 | |
4 | Redistribution and use in source and binary forms, with or without |
5 | modification, are permitted provided that the following conditions are |
6 | met: |
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 | |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | OF 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 | |
47 | class command_line_register; |
48 | class 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 | */ |
58 | class command_line_register:fastuidraw::noncopyable |
59 | { |
60 | public: |
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 | |
79 | private: |
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 | */ |
89 | class command_line_argument:fastuidraw::noncopyable |
90 | { |
91 | public: |
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 | |
161 | private: |
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 | */ |
170 | class command_separator:public command_line_argument |
171 | { |
172 | public: |
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 | |
198 | private: |
199 | std::string m_label; |
200 | }; |
201 | |
202 | /* |
203 | not a command line option, but prints an about |
204 | */ |
205 | class command_about:public command_line_argument |
206 | { |
207 | public: |
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 | |
233 | private: |
234 | std::string m_label; |
235 | }; |
236 | |
237 | template<typename T> |
238 | void |
239 | readvalue_from_string(T &value, |
240 | const std::string &value_string) |
241 | { |
242 | std::istringstream istr(value_string); |
243 | istr >> value; |
244 | } |
245 | |
246 | template<> |
247 | inline |
248 | void |
249 | readvalue_from_string(std::string &value, |
250 | const std::string &value_string) |
251 | { |
252 | value=value_string; |
253 | } |
254 | |
255 | template<> |
256 | inline |
257 | void |
258 | readvalue_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 | |
272 | template<typename T> |
273 | void |
274 | writevalue_to_stream(const T&value, std::ostream &ostr) |
275 | { |
276 | ostr << value; |
277 | } |
278 | |
279 | template<> |
280 | inline |
281 | void |
282 | writevalue_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 | |
294 | class LabelDescription |
295 | { |
296 | public: |
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 | |
328 | private: |
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 | |
334 | template<typename T> |
335 | class enumerated_string_type |
336 | { |
337 | public: |
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 | |
362 | template<typename T> |
363 | class enumerated_type |
364 | { |
365 | public: |
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 | |
377 | template<typename T> |
378 | class command_line_argument_value:public command_line_argument |
379 | { |
380 | public: |
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 | |
501 | private: |
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 | |
509 | template<typename T> |
510 | class enumerated_command_line_argument_value:public command_line_argument |
511 | { |
512 | public: |
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 | |
655 | private: |
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 | |