1*6b3a42afSjmmv // Copyright 2010 Google Inc.
2*6b3a42afSjmmv // All rights reserved.
3*6b3a42afSjmmv //
4*6b3a42afSjmmv // Redistribution and use in source and binary forms, with or without
5*6b3a42afSjmmv // modification, are permitted provided that the following conditions are
6*6b3a42afSjmmv // met:
7*6b3a42afSjmmv //
8*6b3a42afSjmmv // * Redistributions of source code must retain the above copyright
9*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer.
10*6b3a42afSjmmv // * Redistributions in binary form must reproduce the above copyright
11*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer in the
12*6b3a42afSjmmv // documentation and/or other materials provided with the distribution.
13*6b3a42afSjmmv // * Neither the name of Google Inc. nor the names of its contributors
14*6b3a42afSjmmv // may be used to endorse or promote products derived from this software
15*6b3a42afSjmmv // without specific prior written permission.
16*6b3a42afSjmmv //
17*6b3a42afSjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6b3a42afSjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6b3a42afSjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6b3a42afSjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6b3a42afSjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6b3a42afSjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6b3a42afSjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6b3a42afSjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6b3a42afSjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6b3a42afSjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6b3a42afSjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6b3a42afSjmmv
29*6b3a42afSjmmv #include "utils/cmdline/parser.hpp"
30*6b3a42afSjmmv
31*6b3a42afSjmmv #if defined(HAVE_CONFIG_H)
32*6b3a42afSjmmv # include "config.h"
33*6b3a42afSjmmv #endif
34*6b3a42afSjmmv
35*6b3a42afSjmmv extern "C" {
36*6b3a42afSjmmv #include <getopt.h>
37*6b3a42afSjmmv }
38*6b3a42afSjmmv
39*6b3a42afSjmmv #include <cstdlib>
40*6b3a42afSjmmv #include <cstring>
41*6b3a42afSjmmv #include <limits>
42*6b3a42afSjmmv
43*6b3a42afSjmmv #include "utils/auto_array.ipp"
44*6b3a42afSjmmv #include "utils/cmdline/exceptions.hpp"
45*6b3a42afSjmmv #include "utils/cmdline/options.hpp"
46*6b3a42afSjmmv #include "utils/format/macros.hpp"
47*6b3a42afSjmmv #include "utils/noncopyable.hpp"
48*6b3a42afSjmmv #include "utils/sanity.hpp"
49*6b3a42afSjmmv
50*6b3a42afSjmmv namespace cmdline = utils::cmdline;
51*6b3a42afSjmmv
52*6b3a42afSjmmv namespace {
53*6b3a42afSjmmv
54*6b3a42afSjmmv
55*6b3a42afSjmmv /// Auxiliary data to call getopt_long(3).
56*6b3a42afSjmmv struct getopt_data : utils::noncopyable {
57*6b3a42afSjmmv /// Plain-text representation of the short options.
58*6b3a42afSjmmv ///
59*6b3a42afSjmmv /// This string follows the syntax expected by getopt_long(3) in the
60*6b3a42afSjmmv /// argument to describe the short options.
61*6b3a42afSjmmv std::string short_options;
62*6b3a42afSjmmv
63*6b3a42afSjmmv /// Representation of the long options as expected by getopt_long(3).
64*6b3a42afSjmmv utils::auto_array< ::option > long_options;
65*6b3a42afSjmmv
66*6b3a42afSjmmv /// Auto-generated identifiers to be able to parse long options.
67*6b3a42afSjmmv std::map< int, const cmdline::base_option* > ids;
68*6b3a42afSjmmv };
69*6b3a42afSjmmv
70*6b3a42afSjmmv
71*6b3a42afSjmmv /// Converts a cmdline::options_vector to a getopt_data.
72*6b3a42afSjmmv ///
73*6b3a42afSjmmv /// \param options The high-level definition of the options.
74*6b3a42afSjmmv /// \param [out] data An object containing the necessary data to call
75*6b3a42afSjmmv /// getopt_long(3) and interpret its results.
76*6b3a42afSjmmv static void
options_to_getopt_data(const cmdline::options_vector & options,getopt_data & data)77*6b3a42afSjmmv options_to_getopt_data(const cmdline::options_vector& options,
78*6b3a42afSjmmv getopt_data& data)
79*6b3a42afSjmmv {
80*6b3a42afSjmmv data.short_options.clear();
81*6b3a42afSjmmv data.long_options.reset(new ::option[options.size() + 1]);
82*6b3a42afSjmmv
83*6b3a42afSjmmv int cur_id = 512;
84*6b3a42afSjmmv
85*6b3a42afSjmmv for (cmdline::options_vector::size_type i = 0; i < options.size(); i++) {
86*6b3a42afSjmmv const cmdline::base_option* option = options[i];
87*6b3a42afSjmmv ::option& long_option = data.long_options[i];
88*6b3a42afSjmmv
89*6b3a42afSjmmv long_option.name = option->long_name().c_str();
90*6b3a42afSjmmv if (option->needs_arg())
91*6b3a42afSjmmv long_option.has_arg = required_argument;
92*6b3a42afSjmmv else
93*6b3a42afSjmmv long_option.has_arg = no_argument;
94*6b3a42afSjmmv
95*6b3a42afSjmmv int id = -1;
96*6b3a42afSjmmv if (option->has_short_name()) {
97*6b3a42afSjmmv data.short_options += option->short_name();
98*6b3a42afSjmmv if (option->needs_arg())
99*6b3a42afSjmmv data.short_options += ':';
100*6b3a42afSjmmv id = option->short_name();
101*6b3a42afSjmmv } else {
102*6b3a42afSjmmv id = cur_id++;
103*6b3a42afSjmmv }
104*6b3a42afSjmmv long_option.flag = NULL;
105*6b3a42afSjmmv long_option.val = id;
106*6b3a42afSjmmv data.ids[id] = option;
107*6b3a42afSjmmv }
108*6b3a42afSjmmv
109*6b3a42afSjmmv ::option& last_long_option = data.long_options[options.size()];
110*6b3a42afSjmmv last_long_option.name = NULL;
111*6b3a42afSjmmv last_long_option.has_arg = 0;
112*6b3a42afSjmmv last_long_option.flag = NULL;
113*6b3a42afSjmmv last_long_option.val = 0;
114*6b3a42afSjmmv }
115*6b3a42afSjmmv
116*6b3a42afSjmmv
117*6b3a42afSjmmv /// Converts an argc/argv pair to an args_vector.
118*6b3a42afSjmmv ///
119*6b3a42afSjmmv /// \param argc The value of argc as passed to main().
120*6b3a42afSjmmv /// \param argv The value of argv as passed to main().
121*6b3a42afSjmmv ///
122*6b3a42afSjmmv /// \return An args_vector with the same contents of argc/argv.
123*6b3a42afSjmmv static cmdline::args_vector
argv_to_vector(int argc,const char * const argv[])124*6b3a42afSjmmv argv_to_vector(int argc, const char* const argv[])
125*6b3a42afSjmmv {
126*6b3a42afSjmmv PRE(argv[argc] == NULL);
127*6b3a42afSjmmv cmdline::args_vector args;
128*6b3a42afSjmmv for (int i = 0; i < argc; i++)
129*6b3a42afSjmmv args.push_back(argv[i]);
130*6b3a42afSjmmv return args;
131*6b3a42afSjmmv }
132*6b3a42afSjmmv
133*6b3a42afSjmmv
134*6b3a42afSjmmv /// Creates a mutable version of argv.
135*6b3a42afSjmmv ///
136*6b3a42afSjmmv /// \param argc The value of argc as passed to main().
137*6b3a42afSjmmv /// \param argv The value of argv as passed to main().
138*6b3a42afSjmmv ///
139*6b3a42afSjmmv /// \return A new argv, with mutable buffers. The returned array must be
140*6b3a42afSjmmv /// released using the free_mutable_argv() function.
141*6b3a42afSjmmv static char**
make_mutable_argv(const int argc,const char * const * argv)142*6b3a42afSjmmv make_mutable_argv(const int argc, const char* const* argv)
143*6b3a42afSjmmv {
144*6b3a42afSjmmv char** mutable_argv = new char*[argc + 1];
145*6b3a42afSjmmv for (int i = 0; i < argc; i++)
146*6b3a42afSjmmv mutable_argv[i] = ::strdup(argv[i]);
147*6b3a42afSjmmv mutable_argv[argc] = NULL;
148*6b3a42afSjmmv return mutable_argv;
149*6b3a42afSjmmv }
150*6b3a42afSjmmv
151*6b3a42afSjmmv
152*6b3a42afSjmmv /// Releases the object returned by make_mutable_argv().
153*6b3a42afSjmmv ///
154*6b3a42afSjmmv /// \param argv A dynamically-allocated argv as returned by make_mutable_argv().
155*6b3a42afSjmmv static void
free_mutable_argv(char ** argv)156*6b3a42afSjmmv free_mutable_argv(char** argv)
157*6b3a42afSjmmv {
158*6b3a42afSjmmv char** ptr = argv;
159*6b3a42afSjmmv while (*ptr != NULL) {
160*6b3a42afSjmmv ::free(*ptr);
161*6b3a42afSjmmv ptr++;
162*6b3a42afSjmmv }
163*6b3a42afSjmmv delete [] argv;
164*6b3a42afSjmmv }
165*6b3a42afSjmmv
166*6b3a42afSjmmv
167*6b3a42afSjmmv /// Finds the name of the offending option after a getopt_long error.
168*6b3a42afSjmmv ///
169*6b3a42afSjmmv /// \param data Our internal getopt data used for the call to getopt_long.
170*6b3a42afSjmmv /// \param getopt_optopt The value of getopt(3)'s optopt after the error.
171*6b3a42afSjmmv /// \param argv The argv passed to getopt_long.
172*6b3a42afSjmmv /// \param getopt_optind The value of getopt(3)'s optind after the error.
173*6b3a42afSjmmv ///
174*6b3a42afSjmmv /// \return A fully-specified option name (i.e. an option name prefixed by
175*6b3a42afSjmmv /// either '-' or '--').
176*6b3a42afSjmmv static std::string
find_option_name(const getopt_data & data,const int getopt_optopt,char ** argv,const int getopt_optind)177*6b3a42afSjmmv find_option_name(const getopt_data& data, const int getopt_optopt,
178*6b3a42afSjmmv char** argv, const int getopt_optind)
179*6b3a42afSjmmv {
180*6b3a42afSjmmv PRE(getopt_optopt >= 0);
181*6b3a42afSjmmv
182*6b3a42afSjmmv if (getopt_optopt == 0) {
183*6b3a42afSjmmv return argv[getopt_optind - 1];
184*6b3a42afSjmmv } else if (getopt_optopt < std::numeric_limits< char >::max()) {
185*6b3a42afSjmmv INV(getopt_optopt > 0);
186*6b3a42afSjmmv const char ch = static_cast< char >(getopt_optopt);
187*6b3a42afSjmmv return F("-%s") % ch;
188*6b3a42afSjmmv } else {
189*6b3a42afSjmmv for (const ::option* opt = &data.long_options[0]; opt->name != NULL;
190*6b3a42afSjmmv opt++) {
191*6b3a42afSjmmv if (opt->val == getopt_optopt)
192*6b3a42afSjmmv return F("--%s") % opt->name;
193*6b3a42afSjmmv }
194*6b3a42afSjmmv UNREACHABLE;
195*6b3a42afSjmmv }
196*6b3a42afSjmmv }
197*6b3a42afSjmmv
198*6b3a42afSjmmv
199*6b3a42afSjmmv } // anonymous namespace
200*6b3a42afSjmmv
201*6b3a42afSjmmv
202*6b3a42afSjmmv /// Constructs a new parsed_cmdline.
203*6b3a42afSjmmv ///
204*6b3a42afSjmmv /// Use the cmdline::parse() free functions to construct.
205*6b3a42afSjmmv ///
206*6b3a42afSjmmv /// \param option_values_ A mapping of long option names to values. This
207*6b3a42afSjmmv /// contains a representation of the options provided by the user. Note
208*6b3a42afSjmmv /// that each value is actually a collection values: a user may specify a
209*6b3a42afSjmmv /// flag multiple times, and depending on the case we want to honor one or
210*6b3a42afSjmmv /// the other. For those options that support no argument, the argument
211*6b3a42afSjmmv /// value is the empty string.
212*6b3a42afSjmmv /// \param arguments_ The list of non-option arguments in the command line.
parsed_cmdline(const std::map<std::string,std::vector<std::string>> & option_values_,const cmdline::args_vector & arguments_)213*6b3a42afSjmmv cmdline::parsed_cmdline::parsed_cmdline(
214*6b3a42afSjmmv const std::map< std::string, std::vector< std::string > >& option_values_,
215*6b3a42afSjmmv const cmdline::args_vector& arguments_) :
216*6b3a42afSjmmv _option_values(option_values_),
217*6b3a42afSjmmv _arguments(arguments_)
218*6b3a42afSjmmv {
219*6b3a42afSjmmv }
220*6b3a42afSjmmv
221*6b3a42afSjmmv
222*6b3a42afSjmmv /// Checks if the given option has been given in the command line.
223*6b3a42afSjmmv ///
224*6b3a42afSjmmv /// \param name The long option name to check for presence.
225*6b3a42afSjmmv ///
226*6b3a42afSjmmv /// \return True if the option has been given; false otherwise.
227*6b3a42afSjmmv bool
has_option(const std::string & name) const228*6b3a42afSjmmv cmdline::parsed_cmdline::has_option(const std::string& name) const
229*6b3a42afSjmmv {
230*6b3a42afSjmmv return _option_values.find(name) != _option_values.end();
231*6b3a42afSjmmv }
232*6b3a42afSjmmv
233*6b3a42afSjmmv
234*6b3a42afSjmmv /// Gets the raw value of an option.
235*6b3a42afSjmmv ///
236*6b3a42afSjmmv /// The raw value of an option is a collection of strings that represent all the
237*6b3a42afSjmmv /// values passed to the option on the command line. It is up to the consumer
238*6b3a42afSjmmv /// if he wants to honor only the last value or all of them.
239*6b3a42afSjmmv ///
240*6b3a42afSjmmv /// The caller has to use get_option() instead; this function is internal.
241*6b3a42afSjmmv ///
242*6b3a42afSjmmv /// \pre has_option(name) must be true.
243*6b3a42afSjmmv ///
244*6b3a42afSjmmv /// \param name The option to query.
245*6b3a42afSjmmv ///
246*6b3a42afSjmmv /// \return The value of the option as a plain string.
247*6b3a42afSjmmv const std::vector< std::string >&
get_option_raw(const std::string & name) const248*6b3a42afSjmmv cmdline::parsed_cmdline::get_option_raw(const std::string& name) const
249*6b3a42afSjmmv {
250*6b3a42afSjmmv std::map< std::string, std::vector< std::string > >::const_iterator iter =
251*6b3a42afSjmmv _option_values.find(name);
252*6b3a42afSjmmv INV_MSG(iter != _option_values.end(), F("Undefined option --%s") % name);
253*6b3a42afSjmmv return (*iter).second;
254*6b3a42afSjmmv }
255*6b3a42afSjmmv
256*6b3a42afSjmmv
257*6b3a42afSjmmv /// Returns the non-option arguments found in the command line.
258*6b3a42afSjmmv ///
259*6b3a42afSjmmv /// \return The arguments, if any.
260*6b3a42afSjmmv const cmdline::args_vector&
arguments(void) const261*6b3a42afSjmmv cmdline::parsed_cmdline::arguments(void) const
262*6b3a42afSjmmv {
263*6b3a42afSjmmv return _arguments;
264*6b3a42afSjmmv }
265*6b3a42afSjmmv
266*6b3a42afSjmmv
267*6b3a42afSjmmv /// Parses a command line.
268*6b3a42afSjmmv ///
269*6b3a42afSjmmv /// \param args The command line to parse, broken down by words.
270*6b3a42afSjmmv /// \param options The description of the supported options.
271*6b3a42afSjmmv ///
272*6b3a42afSjmmv /// \return The parsed command line.
273*6b3a42afSjmmv ///
274*6b3a42afSjmmv /// \pre args[0] must be the program or command name.
275*6b3a42afSjmmv ///
276*6b3a42afSjmmv /// \throw cmdline::error See the description of parse(argc, argv, options) for
277*6b3a42afSjmmv /// more details on the raised errors.
278*6b3a42afSjmmv cmdline::parsed_cmdline
parse(const cmdline::args_vector & args,const cmdline::options_vector & options)279*6b3a42afSjmmv cmdline::parse(const cmdline::args_vector& args,
280*6b3a42afSjmmv const cmdline::options_vector& options)
281*6b3a42afSjmmv {
282*6b3a42afSjmmv PRE_MSG(args.size() >= 1, "No progname or command name found");
283*6b3a42afSjmmv
284*6b3a42afSjmmv utils::auto_array< const char* > argv(new const char*[args.size() + 1]);
285*6b3a42afSjmmv for (args_vector::size_type i = 0; i < args.size(); i++)
286*6b3a42afSjmmv argv[i] = args[i].c_str();
287*6b3a42afSjmmv argv[args.size()] = NULL;
288*6b3a42afSjmmv return parse(static_cast< int >(args.size()), argv.get(), options);
289*6b3a42afSjmmv }
290*6b3a42afSjmmv
291*6b3a42afSjmmv
292*6b3a42afSjmmv /// Parses a command line.
293*6b3a42afSjmmv ///
294*6b3a42afSjmmv /// \param argc The number of arguments in argv, without counting the
295*6b3a42afSjmmv /// terminating NULL.
296*6b3a42afSjmmv /// \param argv The arguments to parse. The array is NULL-terminated.
297*6b3a42afSjmmv /// \param options The description of the supported options.
298*6b3a42afSjmmv ///
299*6b3a42afSjmmv /// \return The parsed command line.
300*6b3a42afSjmmv ///
301*6b3a42afSjmmv /// \pre args[0] must be the program or command name.
302*6b3a42afSjmmv ///
303*6b3a42afSjmmv /// \throw cmdline::missing_option_argument_error If the user specified an
304*6b3a42afSjmmv /// option that requires an argument, but no argument was provided.
305*6b3a42afSjmmv /// \throw cmdline::unknown_option_error If the user specified an unknown
306*6b3a42afSjmmv /// option (i.e. an option not defined in options).
307*6b3a42afSjmmv /// \throw cmdline::option_argument_value_error If the user passed an invalid
308*6b3a42afSjmmv /// argument to a supported option.
309*6b3a42afSjmmv cmdline::parsed_cmdline
parse(const int argc,const char * const * argv,const cmdline::options_vector & options)310*6b3a42afSjmmv cmdline::parse(const int argc, const char* const* argv,
311*6b3a42afSjmmv const cmdline::options_vector& options)
312*6b3a42afSjmmv {
313*6b3a42afSjmmv PRE_MSG(argc >= 1, "No progname or command name found");
314*6b3a42afSjmmv
315*6b3a42afSjmmv getopt_data data;
316*6b3a42afSjmmv options_to_getopt_data(options, data);
317*6b3a42afSjmmv
318*6b3a42afSjmmv std::map< std::string, std::vector< std::string > > option_values;
319*6b3a42afSjmmv
320*6b3a42afSjmmv for (cmdline::options_vector::const_iterator iter = options.begin();
321*6b3a42afSjmmv iter != options.end(); iter++) {
322*6b3a42afSjmmv const cmdline::base_option* option = *iter;
323*6b3a42afSjmmv if (option->needs_arg() && option->has_default_value())
324*6b3a42afSjmmv option_values[option->long_name()].push_back(
325*6b3a42afSjmmv option->default_value());
326*6b3a42afSjmmv }
327*6b3a42afSjmmv
328*6b3a42afSjmmv args_vector args;
329*6b3a42afSjmmv
330*6b3a42afSjmmv int mutable_argc = argc;
331*6b3a42afSjmmv char** mutable_argv = make_mutable_argv(argc, argv);
332*6b3a42afSjmmv const int old_opterr = ::opterr;
333*6b3a42afSjmmv try {
334*6b3a42afSjmmv int ch;
335*6b3a42afSjmmv
336*6b3a42afSjmmv ::opterr = 0;
337*6b3a42afSjmmv
338*6b3a42afSjmmv while ((ch = ::getopt_long(mutable_argc, mutable_argv,
339*6b3a42afSjmmv ("+:" + data.short_options).c_str(),
340*6b3a42afSjmmv data.long_options.get(), NULL)) != -1) {
341*6b3a42afSjmmv if (ch == ':' ) {
342*6b3a42afSjmmv const std::string name = find_option_name(
343*6b3a42afSjmmv data, ::optopt, mutable_argv, ::optind);
344*6b3a42afSjmmv throw cmdline::missing_option_argument_error(name);
345*6b3a42afSjmmv } else if (ch == '?') {
346*6b3a42afSjmmv const std::string name = find_option_name(
347*6b3a42afSjmmv data, ::optopt, mutable_argv, ::optind);
348*6b3a42afSjmmv throw cmdline::unknown_option_error(name);
349*6b3a42afSjmmv }
350*6b3a42afSjmmv
351*6b3a42afSjmmv const std::map< int, const cmdline::base_option* >::const_iterator
352*6b3a42afSjmmv id = data.ids.find(ch);
353*6b3a42afSjmmv INV(id != data.ids.end());
354*6b3a42afSjmmv const cmdline::base_option* option = (*id).second;
355*6b3a42afSjmmv
356*6b3a42afSjmmv if (option->needs_arg()) {
357*6b3a42afSjmmv if (::optarg != NULL) {
358*6b3a42afSjmmv option->validate(::optarg);
359*6b3a42afSjmmv option_values[option->long_name()].push_back(::optarg);
360*6b3a42afSjmmv } else
361*6b3a42afSjmmv INV(option->has_default_value());
362*6b3a42afSjmmv } else {
363*6b3a42afSjmmv option_values[option->long_name()].push_back("");
364*6b3a42afSjmmv }
365*6b3a42afSjmmv }
366*6b3a42afSjmmv args = argv_to_vector(mutable_argc - optind, mutable_argv + optind);
367*6b3a42afSjmmv
368*6b3a42afSjmmv ::opterr = old_opterr;
369*6b3a42afSjmmv ::optind = GETOPT_OPTIND_RESET_VALUE;
370*6b3a42afSjmmv #if defined(HAVE_GETOPT_WITH_OPTRESET)
371*6b3a42afSjmmv ::optreset = 1;
372*6b3a42afSjmmv #endif
373*6b3a42afSjmmv } catch (...) {
374*6b3a42afSjmmv free_mutable_argv(mutable_argv);
375*6b3a42afSjmmv ::opterr = old_opterr;
376*6b3a42afSjmmv ::optind = GETOPT_OPTIND_RESET_VALUE;
377*6b3a42afSjmmv #if defined(HAVE_GETOPT_WITH_OPTRESET)
378*6b3a42afSjmmv ::optreset = 1;
379*6b3a42afSjmmv #endif
380*6b3a42afSjmmv throw;
381*6b3a42afSjmmv }
382*6b3a42afSjmmv free_mutable_argv(mutable_argv);
383*6b3a42afSjmmv
384*6b3a42afSjmmv return parsed_cmdline(option_values, args);
385*6b3a42afSjmmv }
386