]> git.tdb.fi Git - libs/core.git/blob - source/core/getopt.cpp
Merge branch 'strings-master'
[libs/core.git] / source / core / getopt.cpp
1 /* $Id$
2
3 This file is part of libmspcore
4 Copyright © 2006-2009, 2011 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include "getopt.h"
9
10 using namespace std;
11
12 namespace Msp {
13
14 GetOpt::~GetOpt()
15 {
16         for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
17                 delete *i;
18 }
19
20 GetOpt::OptBase &GetOpt::get_option(char s)
21 {
22         for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
23                 if((*i)->get_short()==s)
24                         return **i;
25         throw UsageError(string("Unknown option -")+s);
26 }
27
28 GetOpt::OptBase &GetOpt::get_option(const string &l)
29 {
30         for(list<OptBase *>::iterator i=opts.begin(); i!=opts.end(); ++i)
31                 if((*i)->get_long()==l)
32                         return **i;
33         throw UsageError(string("Unknown option --")+l);
34 }
35
36 void GetOpt::operator()(unsigned argc, const char *const *argv)
37 {
38         unsigned i = 1;
39         for(; i<argc;)
40         {
41                 if(argv[i][0]=='-')
42                 {
43                         if(argv[i][1]=='-')
44                         {
45                                 if(!argv[i][2])
46                                         break;
47
48                                 i += process_long(argv+i);
49                         }
50                         else
51                                 i += process_short(argv+i);
52                 }
53                 else
54                         args.push_back(argv[i++]);
55         }
56         
57         for(; i<argc; ++i)
58                 args.push_back(argv[i]);
59 }
60
61 unsigned GetOpt::process_long(const char *const *argp)
62 {
63         // Skip the --
64         const char *arg = argp[0]+2;
65
66         // See if the argument contains an =
67         unsigned equals = 0;
68         for(; arg[equals] && arg[equals]!='='; ++equals) ;
69         
70         OptBase &opt = get_option(string(arg, equals));
71         
72         if(arg[equals])
73                 // Process the part after the = as option argument
74                 opt.process(arg+equals+1);
75         else if(opt.get_arg_type()==REQUIRED_ARG)
76         {
77                 if(!argp[1])
78                         throw UsageError("Premature end of arguments");
79
80                 // Process the next argument as option argument
81                 opt.process(argp[1]);
82                 return 2;
83         }
84         else
85                 opt.process();
86         
87         return 1;
88 }
89
90 unsigned GetOpt::process_short(const char *const *argp)
91 {
92         // Skip the -
93         const char *arg = argp[0]+1;
94
95         // Loop through all characters in the argument
96         for(; *arg; ++arg)
97         {
98                 OptBase &opt = get_option(*arg);
99
100                 if(arg[1] && opt.get_arg_type()!=NO_ARG)
101                 {
102                         // Need an option argument and we have characters left - use them
103                         opt.process(arg+1);
104                         return 1;
105                 }
106                 else if(opt.get_arg_type()==REQUIRED_ARG)
107                 {
108                         if(!argp[1])
109                                 throw UsageError("Premature end of arguments");
110                         
111                         // Use the next argument as option argument
112                         opt.process(argp[1]);
113                         return 2;
114                 }
115                 else
116                         opt.process();
117         }
118
119         return 1;
120 }
121
122 string GetOpt::generate_usage(const string &argv0) const
123 {
124         ostringstream line;
125         
126         line<<argv0;
127         for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i)
128         {
129                 line<<" [";
130                 if((*i)->get_short())
131                 {
132                         line<<'-'<<(*i)->get_short();
133                         if(!(*i)->get_long().empty())
134                                 line<<'|';
135                         else if((*i)->get_arg_type()==OPTIONAL_ARG)
136                                 line<<'['<<(*i)->get_metavar()<<']';
137                         else if((*i)->get_arg_type()==REQUIRED_ARG)
138                                 line<<' '<<(*i)->get_metavar();
139                 }
140                 if(!(*i)->get_long().empty())
141                 {
142                         line<<"--"<<(*i)->get_long();
143
144                         if((*i)->get_arg_type()==OPTIONAL_ARG)
145                                 line<<"[="<<(*i)->get_metavar()<<']';
146                         else if((*i)->get_arg_type()==REQUIRED_ARG)
147                                 line<<'='<<(*i)->get_metavar();
148                 }
149                 line<<']';
150         }
151
152         return line.str();
153 }
154
155 string GetOpt::generate_help() const
156 {
157         bool any_short = false;
158         for(list<OptBase *>::const_iterator i=opts.begin(); (!any_short && i!=opts.end()); ++i)
159                 any_short = (*i)->get_short();
160
161         string::size_type maxw = 0;
162         list<string> switches;
163         for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i)
164         {
165                 ostringstream swtch;
166                 if((*i)->get_short())
167                 {
168                         swtch<<'-'<<(*i)->get_short();
169                         if(!(*i)->get_long().empty())
170                                 swtch<<", ";
171                         else if((*i)->get_arg_type()==OPTIONAL_ARG)
172                                 swtch<<'['<<(*i)->get_metavar()<<']';
173                         else if((*i)->get_arg_type()==REQUIRED_ARG)
174                                 swtch<<' '<<(*i)->get_metavar();
175                 }
176                 else if(any_short)
177                         swtch<<"    ";
178                 if(!(*i)->get_long().empty())
179                 {
180                         swtch<<"--"<<(*i)->get_long();
181
182                         if((*i)->get_arg_type()==OPTIONAL_ARG)
183                                 swtch<<"[="<<(*i)->get_metavar()<<']';
184                         else if((*i)->get_arg_type()==REQUIRED_ARG)
185                                 swtch<<'='<<(*i)->get_metavar();
186                 }
187                 switches.push_back(swtch.str());
188                 maxw = max(maxw, switches.back().size());
189         }
190
191         string result;
192         list<string>::const_iterator j = switches.begin();
193         for(list<OptBase *>::const_iterator i=opts.begin(); i!=opts.end(); ++i, ++j)
194         {
195                 result += "  "+*j;
196                 result += string(maxw+2-j->size(), ' ');
197                 result += (*i)->get_help();
198                 result += '\n';
199         }
200         
201         return result;
202 }
203
204
205 GetOpt::OptBase::OptBase(char s, const std::string &l, ArgType a):
206         shrt(s),
207         lng(l),
208         arg_type(a),
209         seen_count(0),
210         metavar("ARG")
211 { }
212
213 GetOpt::OptBase &GetOpt::OptBase::set_help(const string &h)
214 {
215         help = h;
216         return *this;
217 }
218
219 GetOpt::OptBase &GetOpt::OptBase::set_help(const string &h, const string &m)
220 {
221         help = h;
222         metavar = m;
223         return *this;
224 }
225
226 void GetOpt::OptBase::process()
227 {
228         if(arg_type==REQUIRED_ARG)
229                 throw UsageError("--"+lng+" requires an argument");
230         ++seen_count;
231
232         store();
233 }
234
235 void GetOpt::OptBase::process(const string &arg)
236 {
237         if(arg_type==NO_ARG)
238                 throw UsageError("--"+lng+" takes no argument");
239         ++seen_count;
240
241         store(arg);
242 }
243
244 } // namespace Msp