source: trunk/lib/CommandLine.cc @ 791

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

importing classes for commandline parsing from yat. This fixes #349 and #265

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 5.8 KB
Line 
1// $Id: CommandLine.cc 705 2008-11-25 23:33:26Z peter $
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 svndigest, http://dev.thep.lu.se/svndigest
8
9  svndigest is free software; you can redistribute it and/or modify it
10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 3 of the License, or
12  (at your option) any later version.
13
14  svndigest is distributed in the hope that it will be useful, but
15  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 svndigest. 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 svndigest {
42
43  CommandLine::CommandLine(std::string str)
44    : description_(str), free_arg_max_(0)
45  {}
46
47
48  CommandLine::~CommandLine(void)
49  {
50  }
51
52
53  void CommandLine::add(Option& option)
54  {
55    if (option.long_name().size()) {
56      if (long_options_.find(option.long_name())!=long_options_.end()) {
57        std::stringstream ss;
58        ss << "Commandline: two options with long_name: "
59           << option.long_name();
60        throw std::runtime_error(ss.str());
61      }
62      long_options_[option.long_name()] = &option;
63    }
64    if (option.short_name()) {
65      if (short_options_.find(option.short_name())!=short_options_.end()) {
66        std::stringstream ss;
67        ss << "Commandline: two options with short_name: "
68           << option.short_name();
69        throw std::runtime_error(ss.str());
70      }
71      short_options_[option.short_name()] = &option;
72    }
73    if (option.long_name().size() || option.short_name())
74      options_.push_back(&option);
75  }
76
77
78  void CommandLine::allow_free_args(size_t n)
79  {
80    free_arg_max_ = n;
81  }
82
83
84  const std::vector<std::string>& CommandLine::free_args(void) const
85  {
86    return free_arg_;
87  }
88
89
90  bool CommandLine::is_long_option(std::string str) const
91  {
92    return (str.size()>2 && str[0]=='-' && str[1]=='-');
93  }
94
95
96  bool CommandLine::is_short_option(std::string str) const
97  {
98    return (str.size()>=2 && str[0]=='-' && str[1]!='-');
99  }
100
101
102  void CommandLine::parse(int argc, char* argv[])
103  {   
104    using namespace std;
105    // just in case it is not pristine
106    for_each(options_.begin(), options_.end(),std::mem_fun(&Option::reset)); 
107
108    std::vector<std::string> arguments;
109    arguments.reserve(argc);
110    for (int i=0; i<argc; ++i)
111      arguments.push_back(argv[i]);
112    std::vector<std::string>::iterator arg(arguments.begin());   
113    stringstream ss(*arg++);
114    // keeping string after last /
115    while (getline(ss, program_name_,'/')) {}
116
117    try {
118      for (; arg!=arguments.end(); ++arg) {
119        if (is_long_option(*arg)) {
120          std::string key(arg->substr(2));
121          std::stringstream ss(key);
122          getline(ss, key, '=');
123          std::string value;
124          getline(ss, value, '\0');
125          if (!value.empty()){
126            *arg = value;
127            *(--arg) = std::string("--")+key;
128          }         
129          else
130            *arg = key;
131          std::map<std::string, Option*>::const_iterator
132            iter(long_options_.find(key));
133          if (iter!=long_options_.end())
134            iter->second->parse(arg, arguments.end());
135          else if (key.size()>3 && key.substr(0,3)=="no-") { 
136            iter = long_options_.find(key.substr(3));
137            if (iter!=long_options_.end())
138              iter->second->parse(arg, arguments.end());
139          }           
140          else if (iter==long_options_.end()) {
141            ss.str("");
142            ss << ": unrecognized option `" << key << "'\n" << try_help();
143            throw cmd_error(ss.str());
144          }
145        }
146        else if (is_short_option(*arg)) {
147          size_t size=arg->size();
148          for (size_t i=1; i<size; ++i){
149            std::map<char, Option*>::const_iterator
150              iter(short_options_.find((*arg)[i]));
151            if (iter==short_options_.end()) {
152              std::stringstream ss;
153              ss << ": invalid option -- " << (*arg)[i] << "\n"
154                 << try_help() << "\n";
155              throw cmd_error(ss.str());
156            }       
157            else 
158              iter->second->parse(arg, arguments.end());
159          }
160        }
161        else {
162          free_arg_.push_back(*arg);
163          if (free_arg_.size()>free_arg_max_) {
164            std::stringstream ss;
165            ss << ": invalid option -- " << *arg << "\n"
166               << try_help() << "\n";
167            throw cmd_error(ss.str());
168          }
169        }
170      }
171      for_each(options_.begin(),options_.end(),
172               std::mem_fun(&Option::validate)); 
173    }
174    catch (cmd_error& e){
175      std::stringstream ss;
176      ss << program_name_ << ": " << e.what();
177      throw cmd_error(ss.str());
178    }
179     
180  }
181
182
183  std::string CommandLine::program_name(void) const
184  {
185    return program_name_;
186  }
187
188
189  std::vector<std::string> CommandLine::split(std::string str, char del) const
190  {
191    std::vector<std::string> vec;
192    std::stringstream ss(str);
193    while (std::getline(ss, str, del)){
194      vec.push_back(str);
195    }
196    return vec;
197  }
198
199  std::string CommandLine::try_help(void) const
200  {
201    return std::string("Try `"+program_name()+" --help' for more information.");
202  }
203
204
205  std::ostream& operator<<(std::ostream& os, const CommandLine& cmd)
206  {
207    os << cmd.description_ << "\n";
208    ColumnStream cs2(os, 2);
209    std::string::size_type width = 0;
210    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
211         i!=cmd.options_.end();++i) {
212      std::stringstream ss((*i)->print());
213      std::string str;
214      getline(ss, str, '\t');
215      width = std::max(width, str.size()+3);       
216    }
217    cs2.width(0)=width;
218    cs2.width(1)=76-width;
219    cs2.margin(0)=2;
220
221    for (std::vector<Option*>::const_iterator i(cmd.options_.begin()); 
222         i!=cmd.options_.end();++i) 
223      cs2 << (*i)->print() << "\n";
224
225    return os;
226  }
227
228
229}} // of namespace svndigest and theplu
Note: See TracBrowser for help on using the repository browser.