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