lemon/arg_parser.cc
author deba
Wed, 07 Mar 2007 11:57:23 +0000
changeset 2398 99b999e7b775
parent 2390 8450951a8e2d
child 2402 da8eb8f4ea41
permissions -rw-r--r--
Little interface improvment
alpar@2389
     1
/* -*- C++ -*-
alpar@2389
     2
 *
alpar@2391
     3
 * This file is a part of LEMON, a generic C++ optimization library
alpar@2391
     4
 *
alpar@2391
     5
 * Copyright (C) 2003-2007
alpar@2391
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
alpar@2389
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
alpar@2389
     8
 *
alpar@2389
     9
 * Permission to use, modify and distribute this software is granted
alpar@2389
    10
 * provided that this copyright notice appears in all copies. For
alpar@2389
    11
 * precise terms see the accompanying LICENSE file.
alpar@2389
    12
 *
alpar@2389
    13
 * This software is provided "AS IS" with no warranty of any kind,
alpar@2389
    14
 * express or implied, and with no claim as to its suitability for any
alpar@2389
    15
 * purpose.
alpar@2389
    16
 *
alpar@2389
    17
 */
alpar@2389
    18
alpar@2389
    19
#include <lemon/arg_parser.h>
alpar@2389
    20
alpar@2389
    21
namespace lemon {
alpar@2389
    22
alpar@2389
    23
  void ArgParser::_showHelp(void *p)
alpar@2389
    24
  {
alpar@2390
    25
    (static_cast<ArgParser*>(p))->showHelp();
alpar@2389
    26
    exit(1);
alpar@2389
    27
  }
alpar@2389
    28
alpar@2389
    29
  ArgParser::ArgParser(int argc, char **argv) :_argc(argc), _argv(argv),
alpar@2389
    30
					       _command_name(argv[0]) {
alpar@2389
    31
    option("-help","Print a short help message",_showHelp,this);
alpar@2389
    32
    synonym("help","-help");
alpar@2389
    33
    synonym("h","-help");
alpar@2389
    34
alpar@2389
    35
  }
alpar@2389
    36
alpar@2389
    37
  ArgParser &ArgParser::option(const std::string &name,
alpar@2389
    38
			       const std::string &help,
alpar@2389
    39
			       int &value, bool obl)
alpar@2389
    40
  {
alpar@2389
    41
    ParData p;
alpar@2389
    42
    p.int_p=&value;
alpar@2389
    43
    p.help=help;
alpar@2389
    44
    p.type=INTEGER;
alpar@2389
    45
    p.mandatory=obl;
alpar@2389
    46
    _opts[name]=p;
alpar@2389
    47
    return *this;
alpar@2389
    48
  }
alpar@2389
    49
alpar@2389
    50
  ArgParser &ArgParser::option(const std::string &name,
alpar@2389
    51
			       const std::string &help,
alpar@2389
    52
			       double &value, bool obl)
alpar@2389
    53
  {
alpar@2389
    54
    ParData p;
alpar@2389
    55
    p.double_p=&value;
alpar@2389
    56
    p.help=help;
alpar@2389
    57
    p.type=DOUBLE;
alpar@2389
    58
    p.mandatory=obl;
alpar@2389
    59
    _opts[name]=p;
alpar@2389
    60
    return *this;
alpar@2389
    61
  }
alpar@2389
    62
alpar@2389
    63
  ArgParser &ArgParser::option(const std::string &name,
alpar@2389
    64
			       const std::string &help,
alpar@2389
    65
			       bool &value, bool obl)
alpar@2389
    66
  {
alpar@2389
    67
    ParData p;
alpar@2389
    68
    p.bool_p=&value;
alpar@2389
    69
    p.help=help;
alpar@2389
    70
    p.type=BOOL;
alpar@2389
    71
    p.mandatory=obl;
alpar@2389
    72
    _opts[name]=p;
alpar@2389
    73
alpar@2389
    74
    value = false;
alpar@2389
    75
alpar@2389
    76
    return *this;
alpar@2389
    77
  }
alpar@2389
    78
alpar@2389
    79
  ArgParser &ArgParser::option(const std::string &name,
alpar@2389
    80
			       const std::string &help,
alpar@2389
    81
			       std::string &value, bool obl)
alpar@2389
    82
  {
alpar@2389
    83
    ParData p;
alpar@2389
    84
    p.string_p=&value;
alpar@2389
    85
    p.help=help;
alpar@2389
    86
    p.type=STRING;
alpar@2389
    87
    p.mandatory=obl;
alpar@2389
    88
    _opts[name]=p;
alpar@2389
    89
    return *this;
alpar@2389
    90
  }
alpar@2389
    91
alpar@2389
    92
  ArgParser &ArgParser::option(const std::string &name,
alpar@2389
    93
			       const std::string &help,
alpar@2389
    94
			       void (*func)(void *),void *data)
alpar@2389
    95
  {
alpar@2389
    96
    ParData p;
alpar@2389
    97
    p.func_p.p=func;
alpar@2389
    98
    p.func_p.data=data;
alpar@2389
    99
    p.help=help;
alpar@2389
   100
    p.type=FUNC;
alpar@2389
   101
    p.mandatory=false;
alpar@2389
   102
    _opts[name]=p;
alpar@2389
   103
    return *this;
alpar@2389
   104
  }
alpar@2389
   105
  ArgParser &ArgParser::optionGroup(const std::string &group,
alpar@2389
   106
				    const std::string &opt)
alpar@2389
   107
  {
alpar@2389
   108
    Opts::iterator i = _opts.find(opt);
alpar@2389
   109
    if(i==_opts.end()) exit(3); ///\todo throw exception instead
alpar@2389
   110
    else if(i->second.ingroup) exit(3); ///\todo throw exception instead
alpar@2389
   111
    else {
alpar@2389
   112
      GroupData &g=_groups[group];
alpar@2389
   113
      g.opts.push_back(opt);
alpar@2389
   114
      i->second.ingroup=true;
alpar@2389
   115
    }
alpar@2389
   116
    return *this;
alpar@2389
   117
  }
alpar@2389
   118
alpar@2389
   119
  ArgParser &ArgParser::onlyOneGroup(const std::string &group)
alpar@2389
   120
  {
alpar@2389
   121
    GroupData &g=_groups[group];
alpar@2389
   122
    g.only_one=true;
alpar@2389
   123
    return *this;
alpar@2389
   124
  }
alpar@2389
   125
alpar@2389
   126
  ArgParser &ArgParser::synonym(const std::string &syn,
alpar@2389
   127
				const std::string &opt)
alpar@2389
   128
  {
alpar@2389
   129
    Opts::iterator o = _opts.find(opt);
alpar@2389
   130
    Opts::iterator s = _opts.find(syn);
alpar@2389
   131
    if(o==_opts.end()||s!=_opts.end())
alpar@2389
   132
      exit(3); ///\todo throw exception instead
alpar@2389
   133
    else {
alpar@2389
   134
      ParData p;
alpar@2389
   135
      p.help=opt;
alpar@2389
   136
      p.mandatory=false;
alpar@2389
   137
      p.syn=true;
alpar@2389
   138
      _opts[syn]=p;
alpar@2389
   139
      o->second.has_syn=true;
alpar@2389
   140
    }
alpar@2389
   141
    return *this;
alpar@2389
   142
  }
alpar@2389
   143
alpar@2389
   144
  ArgParser &ArgParser::mandatoryGroup(const std::string &group)
alpar@2389
   145
  {
alpar@2389
   146
    GroupData &g=_groups[group];
alpar@2389
   147
    g.mandatory=true;
alpar@2389
   148
    return *this;
alpar@2389
   149
  }
alpar@2389
   150
alpar@2389
   151
  ArgParser &ArgParser::other(const std::string &name,
alpar@2389
   152
			      const std::string &help)
alpar@2389
   153
  {
alpar@2389
   154
    _others_help.push_back(OtherArg(name,help));
alpar@2389
   155
    return *this;
alpar@2389
   156
  }
alpar@2389
   157
alpar@2389
   158
  void ArgParser::show(std::ostream &os,Opts::iterator i)
alpar@2389
   159
  {
alpar@2389
   160
    os << "-" << i->first;
alpar@2389
   161
    if(i->second.has_syn)
alpar@2389
   162
      for(Opts::iterator j=_opts.begin();j!=_opts.end();++j)
alpar@2389
   163
	if(j->second.syn&&j->second.help==i->first)
alpar@2389
   164
	  os << "|-" << j->first;
alpar@2389
   165
    switch(i->second.type) {
alpar@2389
   166
    case STRING:
alpar@2389
   167
      os << " str";
alpar@2389
   168
      break;
alpar@2389
   169
    case INTEGER:
alpar@2389
   170
      os << " int";
alpar@2389
   171
      break;
alpar@2389
   172
    case DOUBLE:
alpar@2389
   173
      os << " num";
alpar@2389
   174
      break;
alpar@2389
   175
    default:
alpar@2389
   176
      break;
alpar@2389
   177
    }
alpar@2389
   178
  }
alpar@2389
   179
alpar@2389
   180
  void ArgParser::show(std::ostream &os,Groups::iterator i)
alpar@2389
   181
  {
alpar@2389
   182
    GroupData::Opts::iterator o=i->second.opts.begin();
alpar@2389
   183
    while(o!=i->second.opts.end()) {
alpar@2389
   184
      show(os,_opts.find(*o));
alpar@2389
   185
      ++o;
alpar@2389
   186
      if(o!=i->second.opts.end()) os<<'|';
alpar@2389
   187
    }
alpar@2389
   188
  }
alpar@2389
   189
    
alpar@2389
   190
  void ArgParser::showHelp(Opts::iterator i)
alpar@2389
   191
  {
alpar@2389
   192
    if(i->second.help.size()==0||i->second.syn) return;
alpar@2389
   193
    std::cerr << "  ";
alpar@2389
   194
    show(std::cerr,i);
alpar@2389
   195
    std::cerr << std::endl;
alpar@2389
   196
    std::cerr << "     " << i->second.help << std::endl;
alpar@2389
   197
  }
alpar@2389
   198
  void ArgParser::showHelp(std::vector<ArgParser::OtherArg>::iterator i)
alpar@2389
   199
  {
alpar@2389
   200
    if(i->help.size()==0) return;
alpar@2389
   201
    std::cerr << "  " << i->name << std::endl
alpar@2389
   202
	      << "     " << i->help << std::endl;
alpar@2389
   203
  }
alpar@2389
   204
    
alpar@2389
   205
  void ArgParser::shortHelp()
alpar@2389
   206
  {
alpar@2389
   207
    const unsigned int LINE_LEN=77;
alpar@2389
   208
    const std::string indent("    ");
alpar@2389
   209
    std::cerr << "Usage:\n  " << _command_name;
alpar@2389
   210
    int pos=_command_name.size()+2;
alpar@2389
   211
    for(Groups::iterator g=_groups.begin();g!=_groups.end();++g) {
alpar@2389
   212
      std::ostringstream cstr;
alpar@2389
   213
      cstr << ' ';
alpar@2389
   214
      if(!g->second.mandatory) cstr << '[';
alpar@2389
   215
      show(cstr,g);
alpar@2389
   216
      if(!g->second.mandatory) cstr << ']';
alpar@2389
   217
      if(pos+cstr.str().size()>LINE_LEN) {
alpar@2389
   218
	std::cerr << std::endl << indent;
alpar@2389
   219
	pos=indent.size();
alpar@2389
   220
      }
alpar@2389
   221
      std::cerr << cstr.str();
alpar@2389
   222
      pos+=cstr.str().size();
alpar@2389
   223
    }
alpar@2389
   224
    for(Opts::iterator i=_opts.begin();i!=_opts.end();++i)
alpar@2389
   225
      if(!i->second.ingroup&&!i->second.syn) {
alpar@2389
   226
	std::ostringstream cstr;
alpar@2389
   227
	cstr << ' ';
alpar@2389
   228
	if(!i->second.mandatory) cstr << '[';
alpar@2389
   229
	show(cstr,i);
alpar@2389
   230
	if(!i->second.mandatory) cstr << ']';
alpar@2389
   231
	if(pos+cstr.str().size()>LINE_LEN) {
alpar@2389
   232
	  std::cerr << std::endl << indent;
alpar@2389
   233
	  pos=indent.size();
alpar@2389
   234
	}
alpar@2389
   235
	std::cerr << cstr.str();
alpar@2389
   236
	pos+=cstr.str().size();
alpar@2389
   237
      }
alpar@2389
   238
    for(std::vector<OtherArg>::iterator i=_others_help.begin();
alpar@2389
   239
	i!=_others_help.end();++i)
alpar@2389
   240
      {
alpar@2389
   241
	std::ostringstream cstr;
alpar@2389
   242
	cstr << ' ' << i->name;
alpar@2389
   243
      
alpar@2389
   244
	if(pos+cstr.str().size()>LINE_LEN) {
alpar@2389
   245
	  std::cerr << std::endl << indent;
alpar@2389
   246
	  pos=indent.size();
alpar@2389
   247
	}
alpar@2389
   248
	std::cerr << cstr.str();
alpar@2389
   249
	pos+=cstr.str().size();
alpar@2389
   250
      }
alpar@2389
   251
    std::cerr << std::endl;
alpar@2389
   252
  }
alpar@2389
   253
    
alpar@2389
   254
  void ArgParser::showHelp()
alpar@2389
   255
  {
alpar@2389
   256
    shortHelp();
alpar@2389
   257
    std::cerr << "Where:\n";
alpar@2389
   258
    for(std::vector<OtherArg>::iterator i=_others_help.begin();
alpar@2389
   259
	i!=_others_help.end();++i) showHelp(i);
alpar@2389
   260
    for(Opts::iterator i=_opts.begin();i!=_opts.end();++i) showHelp(i);
alpar@2389
   261
    exit(1);
alpar@2389
   262
  }
alpar@2389
   263
    
alpar@2389
   264
      
alpar@2389
   265
  void ArgParser::unknownOpt(std::string arg) 
alpar@2389
   266
  {
alpar@2389
   267
    std::cerr << "\nUnknown option: " << arg << "\n";
alpar@2389
   268
    std::cerr << "\nType '" << _command_name <<
alpar@2389
   269
      " --help' to obtain a short summary on the usage.\n\n";
alpar@2389
   270
    exit(1);
alpar@2389
   271
  }
alpar@2389
   272
    
alpar@2389
   273
  void ArgParser::requiresValue(std::string arg, OptType t) 
alpar@2389
   274
  {
alpar@2389
   275
    std::cerr << "Argument '" << arg << "' requires a";
alpar@2389
   276
    switch(t) {
alpar@2389
   277
    case STRING:
alpar@2389
   278
      std::cerr << " string";
alpar@2389
   279
      break;
alpar@2389
   280
    case INTEGER:
alpar@2389
   281
      std::cerr << "n integer";
alpar@2389
   282
      break;
alpar@2389
   283
    case DOUBLE:
alpar@2389
   284
      std::cerr << " floating point";
alpar@2389
   285
      break;
alpar@2389
   286
    default:
alpar@2389
   287
      break;
alpar@2389
   288
    }
alpar@2389
   289
    std::cerr << " value\n\n";
alpar@2389
   290
    showHelp();
alpar@2389
   291
  }
alpar@2389
   292
    
alpar@2389
   293
alpar@2389
   294
  void ArgParser::checkMandatories()
alpar@2389
   295
  {
alpar@2389
   296
    bool ok=true;
alpar@2389
   297
    for(Opts::iterator i=_opts.begin();i!=_opts.end();++i)
alpar@2389
   298
      if(i->second.mandatory&&!i->second.set) 
alpar@2389
   299
	{
alpar@2389
   300
	  if(ok)
alpar@2389
   301
	    std::cerr << _command_name 
alpar@2389
   302
		      << ": The following mandatory arguments are missing.\n";
alpar@2389
   303
	  ok=false;
alpar@2389
   304
	  showHelp(i);
alpar@2389
   305
	}
alpar@2389
   306
    for(Groups::iterator i=_groups.begin();i!=_groups.end();++i)
alpar@2389
   307
      if(i->second.mandatory||i->second.only_one)
alpar@2389
   308
	{
alpar@2389
   309
	  int set=0;
alpar@2389
   310
	  for(GroupData::Opts::iterator o=i->second.opts.begin();
alpar@2389
   311
	      o!=i->second.opts.end();++o)
alpar@2389
   312
	    if(_opts.find(*o)->second.set) ++set;
alpar@2389
   313
	  if(i->second.mandatory&&!set) {
alpar@2389
   314
	    std::cerr << _command_name 
alpar@2389
   315
		      << ": At least one of the following arguments is mandatory.\n";
alpar@2389
   316
	    ok=false;
alpar@2389
   317
	    for(GroupData::Opts::iterator o=i->second.opts.begin();
alpar@2389
   318
		o!=i->second.opts.end();++o)
alpar@2389
   319
	      showHelp(_opts.find(*o));
alpar@2389
   320
	  }
alpar@2389
   321
	  if(i->second.only_one&&set>1) {
alpar@2389
   322
	    std::cerr << _command_name 
alpar@2389
   323
		      << ": At most one of the following arguments can be given.\n";
alpar@2389
   324
	    ok=false;
alpar@2389
   325
	    for(GroupData::Opts::iterator o=i->second.opts.begin();
alpar@2389
   326
		o!=i->second.opts.end();++o)
alpar@2389
   327
	      showHelp(_opts.find(*o));
alpar@2389
   328
	  }
alpar@2389
   329
	}
alpar@2389
   330
    if(!ok) {
alpar@2389
   331
      std::cerr << "\nType '" << _command_name <<
alpar@2389
   332
	" --help' to obtain a short summary on the usage.\n\n";
alpar@2389
   333
      exit(1);
alpar@2389
   334
    }
alpar@2389
   335
  }
alpar@2389
   336
alpar@2389
   337
  ArgParser &ArgParser::parse()
alpar@2389
   338
  {
alpar@2389
   339
    for(int ar=1; ar<_argc; ++ar) {
alpar@2389
   340
      std::string arg(_argv[ar]);
alpar@2389
   341
      if (arg[0] != '-' || arg.size() == 1) {
alpar@2389
   342
	_file_args.push_back(arg);
alpar@2389
   343
      }
alpar@2389
   344
      else {
alpar@2389
   345
	Opts::iterator i = _opts.find(arg.substr(1));
alpar@2389
   346
	if(i==_opts.end()) unknownOpt(arg);
alpar@2389
   347
	else {
alpar@2389
   348
	  if(i->second.syn) i=_opts.find(i->second.help);
alpar@2389
   349
	  ParData &p(i->second);
alpar@2389
   350
	  if (p.type==BOOL) *p.bool_p=true;
alpar@2389
   351
	  else if (p.type==FUNC) p.func_p.p(p.func_p.data);
alpar@2389
   352
	  else if(++ar==_argc) requiresValue(arg, p.type);
alpar@2389
   353
	  else {
alpar@2389
   354
	    std::string val(_argv[ar]);
alpar@2389
   355
	    std::istringstream vals(val);
alpar@2389
   356
	    switch(p.type) {
alpar@2389
   357
	    case STRING:
alpar@2389
   358
	      *p.string_p=val;
alpar@2389
   359
	      break;
alpar@2389
   360
	    case INTEGER:
alpar@2389
   361
	      vals >> *p.int_p;
alpar@2389
   362
	      break;
alpar@2389
   363
	    case DOUBLE:
alpar@2389
   364
	      vals >> *p.double_p;
alpar@2389
   365
	      break;
alpar@2389
   366
	    default:
alpar@2389
   367
	      break;
alpar@2389
   368
	    }
alpar@2389
   369
	    if(p.type!=STRING&&(!vals||!vals.eof()))
alpar@2389
   370
	      requiresValue(arg, p.type);
alpar@2389
   371
	  }
alpar@2389
   372
	  p.set = true;
alpar@2389
   373
	}
alpar@2389
   374
      }
alpar@2389
   375
    }
alpar@2389
   376
    checkMandatories();
alpar@2389
   377
alpar@2389
   378
    return *this;
alpar@2389
   379
  }  
alpar@2389
   380
    
alpar@2389
   381
}