source: trunk/yat/utility/CommandLine.cc @ 2347

Last change on this file since 2347 was 2347, checked in by Peter, 13 years ago

avoid double colon (: :) in unrecognized option error message

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