source: trunk/yat/CommandLine.cc @ 1351

Last change on this file since 1351 was 1351, checked in by Peter Johansson, 12 years ago

update to latest libyat. closes #471

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