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

Last change on this file since 1566 was 1487, checked in by Jari Häkkinen, 13 years ago

Addresses #436. GPL license copy reference should also be updated.

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