source: trunk/lib/yat/CommandLine.cc @ 795

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

refs #388

Create a sub-directory 'lib/yat' in which files from yat are
placed. The files may be updated via 'make fetch', see file README for
more details on how to change which files are updated/copied through
this mechanism.

The reason we do not use subversion's external mechanism is that we
don't want the files to be synchronised with yat automatically. We
want the update to be moderated by some developer.

I chose to fetch files using the svn client rather than wget because
the svn allows us to get an Id string with information from the yat
repository. 'wget' would just download the file as it appears on the
server that is the string is not expanded.

The file 'config_public.h' is created by configure just as in
yat. This implies that automake adds $(top_builddir)/lib/yat to the
include path so unless in a VPATH build $(top_srcdir)/lib/yat is in
the include path. As we have already added $(top_srcdir)/lib to the
include path, and there are both a lib/utility.h and a
lib/yat/utility.h it is not obvious which file is included when having
'include "utility.h"'. For this reason, when being in bin/ and test/
it is needed to include "../lib/utility.h" rather than "utility.h"
when we want to include 'lib/utility.h'.

  • Property svn:eol-style set to native
File size: 6.0 KB
Line 
1// $Id: CommandLine.cc 1874 2009-03-17 22:09:01Z 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
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 <functional>
34#include <fstream>
35#include <iostream>
36#include <sstream>
37#include <stdexcept>
38#include <string>
39#include <vector>
40
41namespace theplu {
42namespace yat {
43namespace utility {
44
45  CommandLine::CommandLine(std::string str)
46    : description_(str), free_arg_max_(0), parsed_(false)
47  {}
48
49
50  CommandLine::~CommandLine(void)
51  {
52  }
53
54
55  void CommandLine::add(Option& option)
56  {
57    if (option.long_name().size()) {
58      if (long_options_.find(option.long_name())!=long_options_.end()) {
59        std::stringstream ss;
60        ss << "yat::utility::Commandline: two options with long_name: "
61           << option.long_name();
62        throw std::runtime_error(ss.str());
63      }
64      long_options_[option.long_name()] = &option;
65    }
66    if (option.short_name()) {
67      if (short_options_.find(option.short_name())!=short_options_.end()) {
68        std::stringstream ss;
69        ss << "yat::utility::Commandline: two options with short_name: "
70           << option.short_name();
71        throw std::runtime_error(ss.str());
72      }
73      short_options_[option.short_name()] = &option;
74    }
75    if (option.long_name().size() || option.short_name())
76      options_.push_back(&option);
77  }
78
79
80  void CommandLine::allow_free_args(size_t n)
81  {
82    free_arg_max_ = n;
83  }
84
85
86  const std::vector<std::string>& CommandLine::free_args(void) const
87  {
88    return free_arg_;
89  }
90
91
92  bool CommandLine::is_long_option(std::string str) const
93  {
94    return (str.size()>2 && str[0]=='-' && str[1]=='-');
95  }
96
97
98  bool CommandLine::is_short_option(std::string str) const
99  {
100    return (str.size()>=2 && str[0]=='-' && str[1]!='-');
101  }
102
103
104  void CommandLine::parse(int argc, char* argv[])
105  {   
106    parsed_=true;
107    using namespace std;
108    // just in case it is not pristine
109    for_each(options_.begin(), options_.end(),std::mem_fun(&Option::reset)); 
110
111    std::vector<std::string> arguments;
112    arguments.reserve(argc);
113    for (int i=0; i<argc; ++i)
114      arguments.push_back(argv[i]);
115    std::vector<std::string>::iterator arg(arguments.begin());   
116    stringstream ss(*arg++);
117    // keeping string after last /
118    while (getline(ss, program_name_,'/')) {}
119
120    try {
121      for (; arg!=arguments.end(); ++arg) {
122        if (is_long_option(*arg)) {
123          std::string key(arg->substr(2));
124          std::stringstream ss2(key);
125          getline(ss2, key, '=');
126          std::string value;
127          getline(ss2, value, '\0');
128          if (!value.empty()){
129            *arg = value;
130            *(--arg) = std::string("--")+key;
131          }         
132          else
133            *arg = key;
134          std::map<std::string, Option*>::const_iterator
135            iter(long_options_.find(key));
136          if (iter!=long_options_.end())
137            iter->second->parse(arg, arguments.end());
138          else if (key.size()>3 && key.substr(0,3)=="no-") { 
139            iter = long_options_.find(key.substr(3));
140            if (iter!=long_options_.end())
141              iter->second->parse(arg, arguments.end());
142          }           
143          else if (iter==long_options_.end()) {
144            std::stringstream ss3;
145            ss3 << ": unrecognized option `" << key << "'\n" << try_help();
146            throw cmd_error(ss3.str());
147          }
148        }
149        else if (is_short_option(*arg)) {
150          size_t size=arg->size();
151          for (size_t i=1; i<size; ++i){
152            std::map<char, Option*>::const_iterator
153              iter(short_options_.find((*arg)[i]));
154            if (iter==short_options_.end()) {
155              std::stringstream ss2;
156              ss2 << ": invalid option -- " << (*arg)[i] << "\n"
157                  << try_help() << "\n";
158              throw cmd_error(ss2.str());
159            }       
160            else 
161              iter->second->parse(arg, arguments.end());
162          }
163        }
164        else {
165          free_arg_.push_back(*arg);
166          if (free_arg_.size()>free_arg_max_) {
167            std::stringstream ss2;
168            ss2 << ": invalid option -- " << *arg << "\n"
169                << try_help() << "\n";
170            throw cmd_error(ss2.str());
171          }
172        }
173      }
174      for_each(options_.begin(),options_.end(),
175               std::mem_fun(&Option::validate)); 
176    }
177    catch (cmd_error& e){
178      std::stringstream ss2;
179      ss2 << program_name_ << ": " << e.what();
180      throw cmd_error(ss2.str());
181    }
182     
183  }
184
185
186  bool CommandLine::parsed(void) const
187  {
188    return parsed_;
189  }
190
191
192  std::string CommandLine::program_name(void) const
193  {
194    return program_name_;
195  }
196
197
198  std::vector<std::string> CommandLine::split(std::string str, char del) const
199  {
200    std::vector<std::string> vec;
201    std::stringstream ss(str);
202    while (std::getline(ss, str, del)){
203      vec.push_back(str);
204    }
205    return vec;
206  }
207
208  std::string CommandLine::try_help(void) const
209  {
210    return std::string("Try `"+program_name()+" --help' for more information.");
211  }
212
213
214  std::ostream& operator<<(std::ostream& os, const CommandLine& cmd)
215  {
216    os << cmd.description_ << "\n";
217    ColumnStream cs2(os, 2);
218    std::string::size_type width = 0;
219    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
220         i!=cmd.options_.end();++i) {
221      std::stringstream ss((*i)->print());
222      std::string str;
223      getline(ss, str, '\t');
224      width = std::max(width, str.size()+3);       
225    }
226    cs2.width(0)=width;
227    cs2.width(1)=76-width;
228    cs2.margin(0)=2;
229
230    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
231         i!=cmd.options_.end();++i) 
232      cs2 << (*i)->print() << "\n";
233
234    return os;
235  }
236
237
238}}} // of namespace utility, yat, and theplu
Note: See TracBrowser for help on using the repository browser.