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