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