1 /*
2   Copyright (c) 2009, Hideyuki Tanaka
3   All rights reserved.
4 
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7   * Redistributions of source code must retain the above copyright
8   notice, this list of conditions and the following disclaimer.
9   * Redistributions in binary form must reproduce the above copyright
10   notice, this list of conditions and the following disclaimer in the
11   documentation and/or other materials provided with the distribution.
12   * Neither the name of the <organization> nor the
13   names of its contributors may be used to endorse or promote products
14   derived from this software without specific prior written permission.
15 
16   THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
17   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
20   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 #pragma once
29 
30 #include <iostream>
31 #include <sstream>
32 #include <vector>
33 #include <map>
34 #include <string>
35 #include <stdexcept>
36 #include <typeinfo>
37 #include <cstring>
38 #include <algorithm>
39 #include <cxxabi.h>
40 #include <cstdlib>
41 
42 namespace cmdline{
43 
44 namespace detail{
45 
46 template <typename Target, typename Source, bool Same>
47 class lexical_cast_t{
48 public:
cast(const Source & arg)49   static Target cast(const Source &arg){
50     Target ret;
51     std::stringstream ss;
52     if (!(ss<<arg && ss>>ret && ss.eof()))
53       throw std::bad_cast();
54 
55     return ret;
56   }
57 };
58 
59 template <typename Target, typename Source>
60 class lexical_cast_t<Target, Source, true>{
61 public:
cast(const Source & arg)62   static Target cast(const Source &arg){
63     return arg;
64   }
65 };
66 
67 template <typename Source>
68 class lexical_cast_t<std::string, Source, false>{
69 public:
cast(const Source & arg)70   static std::string cast(const Source &arg){
71     std::ostringstream ss;
72     ss<<arg;
73     return ss.str();
74   }
75 };
76 
77 template <typename Target>
78 class lexical_cast_t<Target, std::string, false>{
79 public:
cast(const std::string & arg)80   static Target cast(const std::string &arg){
81     Target ret;
82     std::istringstream ss(arg);
83     if (!(ss>>ret && ss.eof()))
84       throw std::bad_cast();
85     return ret;
86   }
87 };
88 
89 template <typename T1, typename T2>
90 struct is_same {
91   static const bool value = false;
92 };
93 
94 template <typename T>
95 struct is_same<T, T>{
96   static const bool value = true;
97 };
98 
99 template<typename Target, typename Source>
100 Target lexical_cast(const Source &arg)
101 {
102   return lexical_cast_t<Target, Source, detail::is_same<Target, Source>::value>::cast(arg);
103 }
104 
105 static inline std::string demangle(const std::string &name)
106 {
107   int status=0;
108   char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status);
109   std::string ret(p);
110   free(p);
111   return ret;
112 }
113 
114 template <class T>
115 std::string readable_typename()
116 {
117   return demangle(typeid(T).name());
118 }
119 
120 template <class T>
121 std::string default_value(T def)
122 {
123   return detail::lexical_cast<std::string>(def);
124 }
125 
126 template <>
127 inline std::string readable_typename<std::string>()
128 {
129   return "string";
130 }
131 
132 } // detail
133 
134 //-----
135 
136 class cmdline_error : public std::exception {
137 public:
138   cmdline_error(const std::string &msg): msg(msg){}
139   ~cmdline_error() throw() {}
140   const char *what() const throw() { return msg.c_str(); }
141 private:
142   std::string msg;
143 };
144 
145 template <class T>
146 struct default_reader{
147   T operator()(const std::string &str){
148     return detail::lexical_cast<T>(str);
149   }
150 };
151 
152 template <class T>
153 struct range_reader{
154   range_reader(const T &low, const T &high): low(low), high(high) {}
155   T operator()(const std::string &s) const {
156     T ret=default_reader<T>()(s);
157     if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error");
158     return ret;
159   }
160 private:
161   T low, high;
162 };
163 
164 template <class T>
165 range_reader<T> range(const T &low, const T &high)
166 {
167   return range_reader<T>(low, high);
168 }
169 
170 template <class T>
171 struct oneof_reader{
172   T operator()(const std::string &s){
173     T ret=default_reader<T>()(s);
174     if (std::find(alt.begin(), alt.end(), ret)==alt.end())
175       throw cmdline_error("");
176     return ret;
177   }
178   void add(const T &v){ alt.push_back(v); }
179 private:
180   std::vector<T> alt;
181 };
182 
183 template <class T>
184 oneof_reader<T> oneof(T a1)
185 {
186   oneof_reader<T> ret;
187   ret.add(a1);
188   return ret;
189 }
190 
191 template <class T>
192 oneof_reader<T> oneof(T a1, T a2)
193 {
194   oneof_reader<T> ret;
195   ret.add(a1);
196   ret.add(a2);
197   return ret;
198 }
199 
200 template <class T>
201 oneof_reader<T> oneof(T a1, T a2, T a3)
202 {
203   oneof_reader<T> ret;
204   ret.add(a1);
205   ret.add(a2);
206   ret.add(a3);
207   return ret;
208 }
209 
210 template <class T>
211 oneof_reader<T> oneof(T a1, T a2, T a3, T a4)
212 {
213   oneof_reader<T> ret;
214   ret.add(a1);
215   ret.add(a2);
216   ret.add(a3);
217   ret.add(a4);
218   return ret;
219 }
220 
221 template <class T>
222 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5)
223 {
224   oneof_reader<T> ret;
225   ret.add(a1);
226   ret.add(a2);
227   ret.add(a3);
228   ret.add(a4);
229   ret.add(a5);
230   return ret;
231 }
232 
233 template <class T>
234 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6)
235 {
236   oneof_reader<T> ret;
237   ret.add(a1);
238   ret.add(a2);
239   ret.add(a3);
240   ret.add(a4);
241   ret.add(a5);
242   ret.add(a6);
243   return ret;
244 }
245 
246 template <class T>
247 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7)
248 {
249   oneof_reader<T> ret;
250   ret.add(a1);
251   ret.add(a2);
252   ret.add(a3);
253   ret.add(a4);
254   ret.add(a5);
255   ret.add(a6);
256   ret.add(a7);
257   return ret;
258 }
259 
260 template <class T>
261 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8)
262 {
263   oneof_reader<T> ret;
264   ret.add(a1);
265   ret.add(a2);
266   ret.add(a3);
267   ret.add(a4);
268   ret.add(a5);
269   ret.add(a6);
270   ret.add(a7);
271   ret.add(a8);
272   return ret;
273 }
274 
275 template <class T>
276 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9)
277 {
278   oneof_reader<T> ret;
279   ret.add(a1);
280   ret.add(a2);
281   ret.add(a3);
282   ret.add(a4);
283   ret.add(a5);
284   ret.add(a6);
285   ret.add(a7);
286   ret.add(a8);
287   ret.add(a9);
288   return ret;
289 }
290 
291 template <class T>
292 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10)
293 {
294   oneof_reader<T> ret;
295   ret.add(a1);
296   ret.add(a2);
297   ret.add(a3);
298   ret.add(a4);
299   ret.add(a5);
300   ret.add(a6);
301   ret.add(a7);
302   ret.add(a8);
303   ret.add(a9);
304   ret.add(a10);
305   return ret;
306 }
307 
308 //-----
309 
310 class parser{
311 public:
312   parser(){
313   }
314   ~parser(){
315     for (std::map<std::string, option_base*>::iterator p=options.begin();
316          p!=options.end(); p++)
317       delete p->second;
318   }
319 
320   void add(const std::string &name,
321            char short_name=0,
322            const std::string &desc=""){
323     if (options.count(name)) throw cmdline_error("multiple definition: "+name);
324     options[name]=new option_without_value(name, short_name, desc);
325     ordered.push_back(options[name]);
326   }
327 
328   template <class T>
329   void add(const std::string &name,
330            char short_name=0,
331            const std::string &desc="",
332            bool need=true,
333            const T def=T()){
334     add(name, short_name, desc, need, def, default_reader<T>());
335   }
336 
337   template <class T, class F>
338   void add(const std::string &name,
339            char short_name=0,
340            const std::string &desc="",
341            bool need=true,
342            const T def=T(),
343            F reader=F()){
344     if (options.count(name)) throw cmdline_error("multiple definition: "+name);
345     options[name]=new option_with_value_with_reader<T, F>(name, short_name, need, def, desc, reader);
346     ordered.push_back(options[name]);
347   }
348 
349   void footer(const std::string &f){
350     ftr=f;
351   }
352 
353   void set_program_name(const std::string &name){
354     prog_name=name;
355   }
356 
357   bool exist(const std::string &name) const {
358     if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
359     return options.find(name)->second->has_set();
360   }
361 
362   template <class T>
363   const T &get(const std::string &name) const {
364     if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
365     const option_with_value<T> *p=dynamic_cast<const option_with_value<T>*>(options.find(name)->second);
366     if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'");
367     return p->get();
368   }
369 
370   const std::vector<std::string> &rest() const {
371     return others;
372   }
373 
374   bool parse(const std::string &arg){
375     std::vector<std::string> args;
376 
377     std::string buf;
378     bool in_quote=false;
379     for (std::string::size_type i=0; i<arg.length(); i++){
380       if (arg[i]=='\"'){
381         in_quote=!in_quote;
382         continue;
383       }
384 
385       if (arg[i]==' ' && !in_quote){
386         args.push_back(buf);
387         buf="";
388         continue;
389       }
390 
391       if (arg[i]=='\\'){
392         i++;
393         if (i>=arg.length()){
394           errors.push_back("unexpected occurrence of '\\' at end of string");
395           return false;
396         }
397       }
398 
399       buf+=arg[i];
400     }
401 
402     if (in_quote){
403       errors.push_back("quote is not closed");
404       return false;
405     }
406 
407     if (buf.length()>0)
408       args.push_back(buf);
409 
410     for (size_t i=0; i<args.size(); i++)
411       std::cout<<"\""<<args[i]<<"\""<<std::endl;
412 
413     return parse(args);
414   }
415 
416   bool parse(const std::vector<std::string> &args){
417     int argc=static_cast<int>(args.size());
418     std::vector<const char*> argv(argc);
419 
420     for (int i=0; i<argc; i++)
421       argv[i]=args[i].c_str();
422 
423     return parse(argc, &argv[0]);
424   }
425 
426   bool parse(int argc, const char * const argv[]){
427     errors.clear();
428     others.clear();
429 
430     if (argc<1){
431       errors.push_back("argument number must be longer than 0");
432       return false;
433     }
434     if (prog_name=="")
435       prog_name=argv[0];
436 
437     std::map<char, std::string> lookup;
438     for (std::map<std::string, option_base*>::iterator p=options.begin();
439          p!=options.end(); p++){
440       if (p->first.length()==0) continue;
441       char initial=p->second->short_name();
442       if (initial){
443         if (lookup.count(initial)>0){
444           lookup[initial]="";
445           errors.push_back(std::string("short option '")+initial+"' is ambiguous");
446           return false;
447         }
448         else lookup[initial]=p->first;
449       }
450     }
451 
452     for (int i=1; i<argc; i++){
453       if (strncmp(argv[i], "--", 2)==0){
454         const char *p=strchr(argv[i]+2, '=');
455         if (p){
456           std::string name(argv[i]+2, p);
457           std::string val(p+1);
458           set_option(name, val);
459         }
460         else{
461           std::string name(argv[i]+2);
462           if (options.count(name)==0){
463             errors.push_back("undefined option: --"+name);
464             continue;
465           }
466           if (options[name]->has_value()){
467             if (i+1>=argc){
468               errors.push_back("option needs value: --"+name);
469               continue;
470             }
471             else{
472               i++;
473               set_option(name, argv[i]);
474             }
475           }
476           else{
477             set_option(name);
478           }
479         }
480       }
481       else if (strncmp(argv[i], "-", 1)==0){
482         if (!argv[i][1]) continue;
483         char last=argv[i][1];
484         for (int j=2; argv[i][j]; j++){
485           last=argv[i][j];
486           if (lookup.count(argv[i][j-1])==0){
487             errors.push_back(std::string("undefined short option: -")+argv[i][j-1]);
488             continue;
489           }
490           if (lookup[argv[i][j-1]]==""){
491             errors.push_back(std::string("ambiguous short option: -")+argv[i][j-1]);
492             continue;
493           }
494           set_option(lookup[argv[i][j-1]]);
495         }
496 
497         if (lookup.count(last)==0){
498           errors.push_back(std::string("undefined short option: -")+last);
499           continue;
500         }
501         if (lookup[last]==""){
502           errors.push_back(std::string("ambiguous short option: -")+last);
503           continue;
504         }
505 
506         if (i+1<argc && options[lookup[last]]->has_value()){
507           set_option(lookup[last], argv[i+1]);
508           i++;
509         }
510         else{
511           set_option(lookup[last]);
512         }
513       }
514       else{
515         others.push_back(argv[i]);
516       }
517     }
518 
519     for (std::map<std::string, option_base*>::iterator p=options.begin();
520          p!=options.end(); p++)
521       if (!p->second->valid())
522         errors.push_back("need option: --"+std::string(p->first));
523 
524     return errors.size()==0;
525   }
526 
527   void parse_check(const std::string &arg){
528     if (!options.count("help"))
529       add("help", '?', "print this message");
530     check(0, parse(arg));
531   }
532 
533   void parse_check(const std::vector<std::string> &args){
534     if (!options.count("help"))
535       add("help", '?', "print this message");
536     check(args.size(), parse(args));
537   }
538 
539   void parse_check(int argc, char *argv[]){
540     if (!options.count("help"))
541       add("help", '?', "print this message");
542     check(argc, parse(argc, argv));
543   }
544 
545   std::string error() const{
546     return errors.size()>0?errors[0]:"";
547   }
548 
549   std::string error_full() const{
550     std::ostringstream oss;
551     for (size_t i=0; i<errors.size(); i++)
552       oss<<errors[i]<<std::endl;
553     return oss.str();
554   }
555 
556   std::string usage() const {
557     std::ostringstream oss;
558     oss<<"usage: "<<prog_name<<" ";
559     for (size_t i=0; i<ordered.size(); i++){
560       if (ordered[i]->must())
561         oss<<ordered[i]->short_description()<<" ";
562     }
563 
564     oss<<"[options] ... "<<ftr<<std::endl;
565     oss<<"options:"<<std::endl;
566 
567     size_t max_width=0;
568     for (size_t i=0; i<ordered.size(); i++){
569       max_width=std::max(max_width, ordered[i]->name().length());
570     }
571     for (size_t i=0; i<ordered.size(); i++){
572       if (ordered[i]->short_name()){
573         oss<<"  -"<<ordered[i]->short_name()<<", ";
574       }
575       else{
576         oss<<"      ";
577       }
578 
579       oss<<"--"<<ordered[i]->name();
580       for (size_t j=ordered[i]->name().length(); j<max_width+4; j++)
581         oss<<' ';
582       oss<<ordered[i]->description()<<std::endl;
583     }
584     return oss.str();
585   }
586 
587 private:
588 
589   void check(int argc, bool ok){
590     if ((argc==1 && !ok) || exist("help")){
591       std::cerr<<usage();
592       exit(0);
593     }
594 
595     if (!ok){
596       std::cerr<<error()<<std::endl<<usage();
597       exit(1);
598     }
599   }
600 
601   void set_option(const std::string &name){
602     if (options.count(name)==0){
603       errors.push_back("undefined option: --"+name);
604       return;
605     }
606     if (!options[name]->set()){
607       errors.push_back("option needs value: --"+name);
608       return;
609     }
610   }
611 
612   void set_option(const std::string &name, const std::string &value){
613     if (options.count(name)==0){
614       errors.push_back("undefined option: --"+name);
615       return;
616     }
617     if (!options[name]->set(value)){
618       errors.push_back("option value is invalid: --"+name+"="+value);
619       return;
620     }
621   }
622 
623   class option_base{
624   public:
625     virtual ~option_base(){}
626 
627     virtual bool has_value() const=0;
628     virtual bool set()=0;
629     virtual bool set(const std::string &value)=0;
630     virtual bool has_set() const=0;
631     virtual bool valid() const=0;
632     virtual bool must() const=0;
633 
634     virtual const std::string &name() const=0;
635     virtual char short_name() const=0;
636     virtual const std::string &description() const=0;
637     virtual std::string short_description() const=0;
638   };
639 
640   class option_without_value : public option_base {
641   public:
642     option_without_value(const std::string &name,
643                          char short_name,
644                          const std::string &desc)
645       :nam(name), snam(short_name), desc(desc), has(false){
646     }
647     ~option_without_value(){}
648 
649     bool has_value() const { return false; }
650 
651     bool set(){
652       has=true;
653       return true;
654     }
655 
656     bool set(const std::string &){
657       return false;
658     }
659 
660     bool has_set() const {
661       return has;
662     }
663 
664     bool valid() const{
665       return true;
666     }
667 
668     bool must() const{
669       return false;
670     }
671 
672     const std::string &name() const{
673       return nam;
674     }
675 
676     char short_name() const{
677       return snam;
678     }
679 
680     const std::string &description() const {
681       return desc;
682     }
683 
684     std::string short_description() const{
685       return "--"+nam;
686     }
687 
688   private:
689     std::string nam;
690     char snam;
691     std::string desc;
692     bool has;
693   };
694 
695   template <class T>
696   class option_with_value : public option_base {
697   public:
698     option_with_value(const std::string &name,
699                       char short_name,
700                       bool need,
701                       const T &def,
702                       const std::string &desc)
703       : nam(name), snam(short_name), need(need), has(false)
704       , def(def), actual(def) {
705       this->desc=full_description(desc);
706     }
707     ~option_with_value(){}
708 
709     const T &get() const {
710       return actual;
711     }
712 
713     bool has_value() const { return true; }
714 
715     bool set(){
716       return false;
717     }
718 
719     bool set(const std::string &value){
720       try{
721         actual=read(value);
722         has=true;
723       }
724       catch(const std::exception &e){
725         return false;
726       }
727       return true;
728     }
729 
730     bool has_set() const{
731       return has;
732     }
733 
734     bool valid() const{
735       if (need && !has) return false;
736       return true;
737     }
738 
739     bool must() const{
740       return need;
741     }
742 
743     const std::string &name() const{
744       return nam;
745     }
746 
747     char short_name() const{
748       return snam;
749     }
750 
751     const std::string &description() const {
752       return desc;
753     }
754 
755     std::string short_description() const{
756       return "--"+nam+"="+detail::readable_typename<T>();
757     }
758 
759   protected:
760     std::string full_description(const std::string &desc){
761       return
762         desc+" ("+detail::readable_typename<T>()+
763         (need?"":" [="+detail::default_value<T>(def)+"]")
764         +")";
765     }
766 
767     virtual T read(const std::string &s)=0;
768 
769     std::string nam;
770     char snam;
771     bool need;
772     std::string desc;
773 
774     bool has;
775     T def;
776     T actual;
777   };
778 
779   template <class T, class F>
780   class option_with_value_with_reader : public option_with_value<T> {
781   public:
782     option_with_value_with_reader(const std::string &name,
783                                   char short_name,
784                                   bool need,
785                                   const T def,
786                                   const std::string &desc,
787                                   F reader)
788       : option_with_value<T>(name, short_name, need, def, desc), reader(reader){
789     }
790 
791   private:
792     T read(const std::string &s){
793       return reader(s);
794     }
795 
796     F reader;
797   };
798 
799   std::map<std::string, option_base*> options;
800   std::vector<option_base*> ordered;
801   std::string ftr;
802 
803   std::string prog_name;
804   std::vector<std::string> others;
805 
806   std::vector<std::string> errors;
807 };
808 
809 } // cmdline
810