source: trunk/yat/CommandLine.cc @ 1556

Last change on this file since 1556 was 1556, checked in by Peter Johansson, 9 years ago

update to latest yat

  • Property svn:eol-style set to native
File size: 7.1 KB
Line 
1// $Id: CommandLine.cc 2881 2012-11-18 01:28:05Z peter $
2
3/*
4  Copyright (C) 2007 Jari Häkkinen, Peter Johansson, Markus Ringnér
5  Copyright (C) 2008 Jari Häkkinen, Peter Johansson
6  Copyright (C) 2009, 2010, 2011, 2012 Peter Johansson
7
8  This file is part of the yat library, http://dev.thep.lu.se/yat
9
10  The yat library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU General Public License as
12  published by the Free Software Foundation; either version 3 of the
13  License, or (at your option) any later version.
14
15  The yat library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with yat. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include <config.h>
25
26#include "CommandLine.h"
27
28#include "ColumnStream.h"
29#include "Exception.h"
30#include "Option.h"
31#include "OptionSwitch.h"
32#include "utility.h"
33
34#include <algorithm>
35#include <cassert>
36#include <functional>
37#include <fstream>
38#include <ostream>
39#include <sstream>
40#include <stdexcept>
41#include <string>
42#include <vector>
43
44namespace theplu {
45namespace yat {
46namespace utility {
47
48  CommandLine::CommandLine(std::string str)
49    : description_(str), free_arg_max_(0), parsed_(false)
50  {}
51
52
53  CommandLine::~CommandLine(void)
54  {
55  }
56
57
58  void CommandLine::add(Option& option)
59  {
60    if (option.long_name().size()) {
61      if (long_options_.find(option.long_name())!=long_options_.end()) {
62        std::stringstream ss;
63        ss << "yat::utility::Commandline: two options with long_name: "
64           << option.long_name();
65        throw runtime_error(ss.str());
66      }
67      long_options_[option.long_name()] = &option;
68    }
69    if (option.short_name()) {
70      if (short_options_.find(option.short_name())!=short_options_.end()) {
71        std::stringstream ss;
72        ss << "yat::utility::Commandline: two options with short_name: "
73           << option.short_name();
74        throw runtime_error(ss.str());
75      }
76      short_options_[option.short_name()] = &option;
77    }
78    if (option.long_name().size() || option.short_name())
79      options_.push_back(&option);
80  }
81
82
83  void CommandLine::allow_free_args(size_t n)
84  {
85    free_arg_max_ = n;
86  }
87
88
89  const std::vector<std::string>& CommandLine::free_args(void) const
90  {
91    return free_arg_;
92  }
93
94
95  bool CommandLine::is_long_option(std::string str) const
96  {
97    return (str.size()>2 && str[0]=='-' && str[1]=='-');
98  }
99
100
101  bool CommandLine::is_short_option(std::string str) const
102  {
103    return (str.size()>=2 && str[0]=='-' && str[1]!='-');
104  }
105
106
107  void CommandLine::parse(int argc, char* argv[])
108  {   
109    parsed_=true;
110    using namespace std;
111    // just in case it is not pristine
112    for_each(options_.begin(), options_.end(),std::mem_fun(&Option::reset)); 
113
114    std::vector<std::string> arguments;
115    arguments.reserve(argc);
116    for (int i=0; i<argc; ++i)
117      arguments.push_back(argv[i]);
118    std::vector<std::string>::iterator arg(arguments.begin());   
119    std::vector<std::string>::iterator end(arguments.end());   
120    stringstream ss(*arg++);
121    // keeping string after last /
122    while (getline(ss, program_name_,'/')) {}
123
124    bool ok=true;
125    std::string error_message;
126    for (; arg!=end; ++arg) {
127      try {
128        parse(arg, end);
129      }
130      catch (cmd_error& e) {
131        if (ok) { // we only store first error
132          ok = false;
133          error_message = e.what();
134        }
135      }
136    }
137    // validate all options
138    try {
139      for_each(options_.begin(), options_.end(),
140               std::mem_fun(&Option::validate)); 
141    }
142    catch (cmd_error& e) {
143      if (ok) { // we only store first error
144        ok = false;
145        error_message = e.what();
146      }
147    }
148   
149    if (!ok) {
150      std::stringstream ss;
151      ss << program_name_ << ": " << error_message;
152      throw cmd_error(ss.str());
153    }
154  }
155 
156
157  void CommandLine::parse_long(std::vector<std::string>::iterator& arg,
158                               std::vector<std::string>::iterator& last)
159  {
160    std::string key(arg->substr(2));
161    std::stringstream ss2(key);
162    getline(ss2, key, '=');
163    std::string value;
164    getline(ss2, value, '\0');
165    if (!value.empty()){
166      *arg = value;
167      *(--arg) = std::string("--")+key;
168    }         
169    else
170      *arg = key;
171    std::map<std::string, Option*>::const_iterator
172      iter(long_options_.find(key));
173    if (iter!=long_options_.end())
174      iter->second->parse(arg, last);
175    else if (key.size()>3 && key.substr(0,3)=="no-") { 
176      iter = long_options_.find(key.substr(3));
177      if (iter!=long_options_.end())
178        iter->second->parse(arg, last);
179    }           
180    if (iter==long_options_.end()) {
181      std::stringstream ss3;
182      ss3 << "unrecognized option `" << key << "'\n" << try_help();
183      throw cmd_error(ss3.str());
184    }
185  }
186
187  void CommandLine::parse_short(std::vector<std::string>::iterator& arg,
188                                std::vector<std::string>::iterator& last)
189  {
190    size_t size=arg->size();
191    for (size_t i=1; i<size; ++i){
192      std::map<char, Option*>::const_iterator
193        iter(short_options_.find((*arg)[i]));
194      if (iter==short_options_.end()) {
195        std::stringstream ss2;
196        ss2 << ": invalid option -- " << (*arg)[i] << "\n"
197            << try_help();
198        throw cmd_error(ss2.str());
199      }       
200      else 
201        iter->second->parse(arg, last);
202    }
203  }
204
205
206  void CommandLine::parse_free_arg(std::vector<std::string>::iterator& arg,
207                                   std::vector<std::string>::iterator& last)
208  {
209    free_arg_.push_back(*arg);
210    if (free_arg_.size()>free_arg_max_) {
211      std::stringstream ss2;
212      ss2 << ": invalid option -- " << *arg << "\n"
213          << try_help();
214      throw cmd_error(ss2.str());
215    }
216  }
217
218 
219  void CommandLine::parse(std::vector<std::string>::iterator& first,
220                          std::vector<std::string>::iterator& last)
221  {
222    if (is_long_option(*first))
223      parse_long(first, last);
224    else if (is_short_option(*first))
225      parse_short(first, last);
226    else
227      parse_free_arg(first, last);
228
229  }
230 
231
232  bool CommandLine::parsed(void) const
233  {
234    return parsed_;
235  }
236
237
238  std::string CommandLine::program_name(void) const
239  {
240    return program_name_;
241  }
242
243
244  void CommandLine::sort(void)
245  {
246    sort(OptionCompare());
247  }
248
249
250  std::string CommandLine::try_help(void) const
251  {
252    return std::string("Try `"+program_name()+" --help' for more information.");
253  }
254
255
256  std::ostream& operator<<(std::ostream& os, const CommandLine& cmd)
257  {
258    os << cmd.description_ << "\n";
259    ColumnStream cs2(os, 2);
260    std::string::size_type width = 0;
261    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
262         i!=cmd.options_.end();++i) {
263      std::stringstream ss((*i)->print());
264      std::string str;
265      getline(ss, str, '\t');
266      width = std::max(width, str.size()+3);       
267    }
268    cs2.width(0)=width;
269    cs2.width(1)=76-width;
270    cs2.margin(0)=2;
271
272    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
273         i!=cmd.options_.end();++i) 
274      cs2 << (*i)->print() << "\n";
275
276    return os;
277  }
278
279
280  bool CommandLine::OptionCompare::operator()(const Option* lhs, 
281                                              const Option* rhs) const
282  {
283    assert(lhs);
284    assert(rhs);
285    std::string lhs_str = lhs->long_name();
286    if (lhs_str.empty())
287      lhs_str = lhs->short_name();
288    std::string rhs_str = rhs->long_name();
289    if (rhs_str.empty())
290      rhs_str = rhs->short_name();
291    return lhs_str < rhs_str;
292  }
293
294
295}}} // of namespace utility, yat, and theplu
Note: See TracBrowser for help on using the repository browser.