PageRenderTime 71ms CodeModel.GetById 33ms app.highlight 32ms RepoModel.GetById 2ms app.codeStats 0ms

/src/apps/share/argparse.cpp

http://github.com/imageworks/OpenColorIO
C++ | 528 lines | 341 code | 109 blank | 78 comment | 103 complexity | 7ec7946fb32a0b8a64ab84f04cf1cc30 MD5 | raw file
  1/*
  2  Copyright 2008 Larry Gritz and the other authors and contributors.
  3  All Rights Reserved.
  4  Based on BSD-licensed software Copyright 2004 NVIDIA Corp.
  5
  6  Redistribution and use in source and binary forms, with or without
  7  modification, are permitted provided that the following conditions are
  8  met:
  9  * Redistributions of source code must retain the above copyright
 10    notice, this list of conditions and the following disclaimer.
 11  * Redistributions in binary form must reproduce the above copyright
 12    notice, this list of conditions and the following disclaimer in the
 13    documentation and/or other materials provided with the distribution.
 14  * Neither the name of the software's owners nor the names of its
 15    contributors may be used to endorse or promote products derived from
 16    this software without specific prior written permission.
 17  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28
 29  (This is the Modified BSD License)
 30*/
 31
 32
 33#include <cstring>
 34#include <cctype>
 35#include <cassert>
 36#include <cstdlib>
 37#include <iostream>
 38#include <cstdarg>
 39#include <iterator>
 40
 41#include "strutil.h"
 42#include "argparse.h"
 43
 44/*
 45OIIO_NAMESPACE_ENTER
 46{
 47*/
 48
 49class ArgOption {
 50public:
 51    typedef int (*callback_t) (int, const char**);
 52
 53    ArgOption (const char *str);
 54    ~ArgOption () { }
 55    
 56    int initialize ();
 57    
 58    int parameter_count () const { return m_count; }
 59    const std::string & name() const { return m_flag; }
 60
 61    const std::string & fmt() const { return m_format; }
 62
 63    bool is_flag () const { return m_type == Flag; }
 64    bool is_sublist () const { return m_type == Sublist; }
 65    bool is_regular () const { return m_type == Regular; }
 66    
 67    void add_parameter (int i, void *p);
 68
 69    void set_parameter (int i, const char *argv);
 70
 71    void add_argument (const char *argv);
 72    int invoke_callback () const;
 73
 74    int invoke_callback (int argc, const char **argv) const {
 75        return m_callback ? m_callback (argc, argv) : 0;
 76    }
 77
 78    void set_callback (callback_t cb) { m_callback = cb; }
 79
 80    void found_on_command_line () { m_repetitions++; }
 81    int parsed_count () const { return m_repetitions; }
 82
 83    void description (const char *d) { m_descript = d; }
 84    const std::string & description() const { return m_descript; }
 85
 86private:
 87    enum OptionType { None, Regular, Flag, Sublist };
 88
 89    std::string m_format;                         // original format string
 90    std::string m_flag;                           // just the -flag_foo part
 91    std::string m_code;                           // paramter types, eg "df"
 92    std::string m_descript;
 93    OptionType m_type;                    
 94    int m_count;                                  // number of parameters
 95    std::vector<void *> m_param;                  // pointers to app data vars
 96    callback_t m_callback;
 97    int m_repetitions;                            // number of times on cmd line
 98    std::vector<std::string> m_argv;
 99};
100
101
102
103// Constructor.  Does not do any parsing or error checking.
104// Make sure to call initialize() right after construction.
105ArgOption::ArgOption (const char *str) 
106    : m_format(str), m_type(None), m_count(0),
107      m_callback(NULL), m_repetitions(0)
108{
109}
110
111
112
113// Parses the format string ("-option %s %d %f") to extract the
114// flag ("-option") and create a code string ("sdf").  After the
115// code string is created, the param list of void* pointers is
116// allocated to hold the argument variable pointers.
117int
118ArgOption::initialize()
119{
120    size_t n;
121    const char *s;
122
123    if (m_format.empty() || m_format == "%*") {
124        m_type = Sublist;
125        m_count = 1;                      // sublist callback function pointer
126        m_code = "*";
127        m_flag = "";
128    } else if (m_format == "<SEPARATOR>") {
129    } else {
130        // extract the flag name
131        s = &m_format[0];
132        assert(*s == '-');
133        assert(isalpha(s[1]) || (s[1] == '-' && isalpha(s[2])));
134    
135        s++;
136        if (*s == '-')
137            s++;
138
139        while (isalnum(*s) || *s == '_' || *s == '-') s++;
140
141        if (! *s) {
142            m_flag = m_format;
143            m_type = Flag;
144            m_count = 1;
145            m_code = "b";
146        } else {
147            n = s - (&m_format[0]);
148            m_flag.assign (m_format.begin(), m_format.begin()+n);
149
150            // Parse the scanf-like parameters
151
152            m_type = Regular;
153    
154            n = (m_format.length() - n) / 2;       // conservative estimate
155            m_code.clear ();
156    
157            while (*s != '\0') {
158                if (*s == '%') {
159                    s++;
160                    assert(*s != '\0');
161            
162                    m_count++;                    // adding another parameter
163            
164                    switch (*s) {
165                    case 'd':                   // 32bit int
166                    case 'g':                   // float
167                    case 'f':                   // float
168                    case 'F':                   // double
169                    case 's':                   // string
170                    case 'L':                   // vector<string>
171                        assert (m_type == Regular);
172                        m_code += *s;
173                        break;
174
175                    case '*':
176                        assert(m_count == 1);
177                        m_type = Sublist;
178                        break;
179                        
180                    default:
181                        std::cerr << "Programmer error:  Unknown option ";
182                        std::cerr << "type string \"" << *s << "\"" << "\n";
183                        abort();
184                    }
185                }
186        
187                s++;
188            }
189        }
190    }
191    
192    // Allocate space for the parameter pointers and initialize to NULL
193    m_param.resize (m_count, NULL);
194
195    return 0;
196}
197
198
199
200// Stores the pointer to an argument in the param list and
201// initializes flag options to FALSE.
202// FIXME -- there is no such initialization.  Bug?
203void
204ArgOption::add_parameter (int i, void *p)
205{
206    assert (i >= 0 && i < m_count);
207    m_param[i] = p;
208}
209
210
211
212// Given a string from argv, set the associated option parameter
213// at index i using the format conversion code in the code string.
214void
215ArgOption::set_parameter (int i, const char *argv)
216{
217    assert(i < m_count);
218    
219    switch (m_code[i]) {
220    case 'd':
221        *(int *)m_param[i] = atoi(argv);
222        break;
223
224    case 'f':
225    case 'g':
226        *(float *)m_param[i] = (float)atof(argv);
227        break;
228
229    case 'F':
230        *(double *)m_param[i] = atof(argv);
231        break;
232
233    case 's':
234        *(std::string *)m_param[i] = argv;
235        break;
236
237    case 'S':
238        *(std::string *)m_param[i] = argv;
239        break;
240
241    case 'L':
242        ((std::vector<std::string> *)m_param[i])->push_back (argv);
243        break;
244
245    case 'b':
246        *(bool *)m_param[i] = true;
247        break;
248        
249    case '*':
250    default:
251        abort();
252    }
253}
254
255
256
257// Call the sublist callback if any arguments have been parsed
258int
259ArgOption::invoke_callback () const
260{
261    assert (m_count == 1);
262
263    int argc = (int) m_argv.size();
264    if (argc == 0)
265        return 0;
266
267    // Convert the argv's to char*[]
268    const char **myargv = (const char **) alloca (argc * sizeof(const char *));
269    for (int i = 0;  i < argc;  ++i)
270        myargv[i] = m_argv[i].c_str();
271    return invoke_callback (argc, myargv);
272}
273
274
275
276// Add an argument to this sublist option
277void
278ArgOption::add_argument (const char *argv)
279{
280    m_argv.push_back (argv);
281}
282
283
284
285
286
287ArgParse::ArgParse (int argc, const char **argv)
288    : m_argc(argc), m_argv(argv), m_global(NULL)
289{
290}
291
292
293
294ArgParse::~ArgParse()
295{
296    for (unsigned int i=0; i<m_option.size(); ++i) {
297        ArgOption *opt = m_option[i];
298        delete opt;
299    }
300}
301
302
303
304// Top level command line parsing function called after all options
305// have been parsed and created from the format strings.  This function
306// parses the command line (argc,argv) stored internally in the constructor.
307// Each command line argument is parsed and checked to see if it matches an
308// existing option.  If there is no match, and error is reported and the
309// function returns early.  If there is a match, all the arguments for
310// that option are parsed and the associated variables are set.
311int
312ArgParse::parse (int xargc, const char **xargv)
313{
314    m_argc = xargc;
315    m_argv = xargv;
316
317    for (int i = 1; i < m_argc; i++) {
318        if (m_argv[i][0] == '-' && 
319              (isalpha (m_argv[i][1]) || m_argv[i][1] == '-')) {         // flag
320            ArgOption *option = find_option (m_argv[i]);
321            if (option == NULL) {
322                error ("Invalid option \"%s\"", m_argv[i]);
323                return -1;
324            }
325
326            option->found_on_command_line();
327            
328            if (option->is_flag()) {
329                option->set_parameter(0, NULL);
330            } else {
331                assert (option->is_regular());
332                for (int j = 0; j < option->parameter_count(); j++) {
333                    if (j+i+1 >= m_argc) {
334                        error ("Missing parameter %d from option "
335                                      "\"%s\"", j+1, option->name().c_str());
336                        return -1;
337                    }
338                    option->set_parameter (j, m_argv[i+j+1]);
339                }
340                i += option->parameter_count();
341            }
342        } else {
343            // not an option nor an option parameter, glob onto global list
344            if (m_global)
345                m_global->invoke_callback (1, m_argv+i);
346            else {
347                error ("Argument \"%s\" does not have an associated "
348                    "option", m_argv[i]);
349                return -1;
350            }
351        }
352    }
353
354    return 0;
355}
356
357
358
359// Primary entry point.  This function accepts a set of format strings
360// and variable pointers.  Each string contains an option name and a
361// scanf-like format string to enumerate the arguments of that option
362// (eg. "-option %d %f %s").  The format string is followed by a list
363// of pointers to the argument variables, just like scanf.  All format
364// strings and arguments are parsed to create a list of ArgOption objects.
365// After all ArgOptions are created, the command line is parsed and
366// the sublist option callbacks are invoked.
367int
368ArgParse::options (const char *intro, ...)
369{
370    va_list ap;
371    va_start (ap, intro);
372
373    m_intro = intro;
374    for (const char *cur = va_arg(ap, char *); cur; cur = va_arg(ap, char *)) {
375        if (find_option (cur) &&
376                strcmp(cur, "<SEPARATOR>")) {
377            error ("Option \"%s\" is multiply defined", cur);
378            return -1;
379        }
380        
381        // Build a new option and then parse the values
382        ArgOption *option = new ArgOption (cur);
383        if (option->initialize() < 0) {
384            return -1;
385        }
386
387        if (cur[0] == '\0' ||
388            (cur[0] == '%' && cur[1] == '*' && cur[2] == '\0')) {
389            // set default global option
390            m_global = option;
391        }
392
393        // Grab any parameters and store them with this option
394        for (int i = 0; i < option->parameter_count(); i++) {
395            void *p = va_arg (ap, void *);
396            if (p == NULL) {
397                error ("Missing argument parameter for \"%s\"",
398                              option->name().c_str());
399                return -1;
400            }
401            
402            option->add_parameter (i, p);
403
404            if (option == m_global)
405                option->set_callback ((ArgOption::callback_t)p);
406        }
407
408        // Last argument is description
409        option->description ((const char *) va_arg (ap, const char *));
410        m_option.push_back(option);
411    }
412
413    va_end (ap);
414    return 0;
415}
416
417
418
419// Find an option by name in the option vector
420ArgOption *
421ArgParse::find_option (const char *name)
422{
423    for (std::vector<ArgOption *>::const_iterator i = m_option.begin();
424         i != m_option.end(); i++) {
425        const char *opt = (*i)->name().c_str();
426        if (! strcmp(name, opt))
427            return *i;
428        // Match even if the user mixes up one dash or two
429        if (name[0] == '-' && name[1] == '-' && opt[0] == '-' && opt[1] != '-')
430            if (! strcmp (name+1, opt))
431                return *i;
432        if (name[0] == '-' && name[1] != '-' && opt[0] == '-' && opt[1] == '-')
433            if (! strcmp (name, opt+1))
434                return *i;
435    }
436
437    return NULL;
438}
439
440
441
442int
443ArgParse::found (const char *option_name)
444{
445    ArgOption *option = find_option(option_name);
446    if (option == NULL) return 0;
447    return option->parsed_count();
448}
449
450
451
452void
453ArgParse::error (const char *format, ...)
454{
455    va_list ap;
456    va_start (ap, format);
457    m_errmessage = Strutil::vformat (format, ap);
458    va_end (ap);
459}
460
461std::string
462ArgParse::geterror () const
463{
464    std::string e = m_errmessage;
465    m_errmessage.clear ();
466    return e;
467}
468
469
470void
471ArgParse::usage () const
472{
473    const size_t longline = 40;
474    std::cout << m_intro << '\n';
475    size_t maxlen = 0;
476    
477    for (unsigned int i=0; i<m_option.size(); ++i) {
478        ArgOption *opt = m_option[i];
479        size_t fmtlen = opt->fmt().length();
480        // Option lists > 40 chars will be split into multiple lines
481        if (fmtlen < longline)
482            maxlen = std::max (maxlen, fmtlen);
483    }
484    
485    for (unsigned int i=0; i<m_option.size(); ++i) {
486        ArgOption *opt = m_option[i];
487        if (opt->description().length()) {
488            size_t fmtlen = opt->fmt().length();
489            if (opt->fmt() == "<SEPARATOR>")
490                std::cout << opt->description() << '\n';
491            else if (fmtlen < longline)
492                std::cout << "    " << opt->fmt() 
493                          << std::string (maxlen + 2 - fmtlen, ' ')
494                          << opt->description() << '\n';
495            else
496                std::cout << "    " << opt->fmt() << "\n    "
497                          << std::string (maxlen + 2, ' ')
498                          << opt->description() << '\n';
499        }
500    }
501}
502
503
504
505std::string
506ArgParse::command_line () const
507{
508    std::string s;
509    for (int i = 0;  i < m_argc;  ++i) {
510        if (strchr (m_argv[i], ' ')) {
511            s += '\"';
512            s += m_argv[i];
513            s += '\"';
514        } else {
515            s += m_argv[i];
516        }
517        if (i < m_argc-1)
518            s += ' ';
519    }
520    return s;
521}
522
523
524/*
525}
526OIIO_NAMESPACE_EXIT
527*/
528