yat/utility/CommandLine.cc

Code
Comments
Other
Rev Date Author Line
966 11 Oct 07 peter 1 // $Id$
966 11 Oct 07 peter 2
966 11 Oct 07 peter 3 /*
4359 23 Aug 23 peter 4   Copyright (C) 2007 Peter Johansson, Markus Ringnér
2119 12 Dec 09 peter 5   Copyright (C) 2008 Jari Häkkinen, Peter Johansson
4252 18 Nov 22 peter 6   Copyright (C) 2009, 2010, 2011, 2012, 2013, 2018 Peter Johansson
966 11 Oct 07 peter 7
1437 25 Aug 08 peter 8   This file is part of the yat library, http://dev.thep.lu.se/yat
966 11 Oct 07 peter 9
966 11 Oct 07 peter 10   The yat library is free software; you can redistribute it and/or
966 11 Oct 07 peter 11   modify it under the terms of the GNU General Public License as
1486 09 Sep 08 jari 12   published by the Free Software Foundation; either version 3 of the
966 11 Oct 07 peter 13   License, or (at your option) any later version.
966 11 Oct 07 peter 14
966 11 Oct 07 peter 15   The yat library is distributed in the hope that it will be useful,
966 11 Oct 07 peter 16   but WITHOUT ANY WARRANTY; without even the implied warranty of
966 11 Oct 07 peter 17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
966 11 Oct 07 peter 18   General Public License for more details.
966 11 Oct 07 peter 19
966 11 Oct 07 peter 20   You should have received a copy of the GNU General Public License
1487 10 Sep 08 jari 21   along with yat. If not, see <http://www.gnu.org/licenses/>.
966 11 Oct 07 peter 22 */
966 11 Oct 07 peter 23
2881 18 Nov 12 peter 24 #include <config.h>
2881 18 Nov 12 peter 25
966 11 Oct 07 peter 26 #include "CommandLine.h"
966 11 Oct 07 peter 27
966 11 Oct 07 peter 28 #include "ColumnStream.h"
1178 27 Feb 08 peter 29 #include "Exception.h"
966 11 Oct 07 peter 30 #include "Option.h"
966 11 Oct 07 peter 31 #include "OptionSwitch.h"
966 11 Oct 07 peter 32 #include "utility.h"
966 11 Oct 07 peter 33
966 11 Oct 07 peter 34 #include <algorithm>
2265 05 Jun 10 peter 35 #include <cassert>
966 11 Oct 07 peter 36 #include <functional>
966 11 Oct 07 peter 37 #include <fstream>
2046 02 Sep 09 peter 38 #include <ostream>
966 11 Oct 07 peter 39 #include <sstream>
966 11 Oct 07 peter 40 #include <stdexcept>
966 11 Oct 07 peter 41 #include <string>
966 11 Oct 07 peter 42 #include <vector>
966 11 Oct 07 peter 43
966 11 Oct 07 peter 44 namespace theplu {
966 11 Oct 07 peter 45 namespace yat {
966 11 Oct 07 peter 46 namespace utility {
966 11 Oct 07 peter 47
982 22 Oct 07 peter 48   CommandLine::CommandLine(std::string str)
1741 22 Jan 09 peter 49     : description_(str), free_arg_max_(0), parsed_(false)
966 11 Oct 07 peter 50   {}
966 11 Oct 07 peter 51
966 11 Oct 07 peter 52
966 11 Oct 07 peter 53   CommandLine::~CommandLine(void)
966 11 Oct 07 peter 54   {
966 11 Oct 07 peter 55   }
966 11 Oct 07 peter 56
966 11 Oct 07 peter 57
966 11 Oct 07 peter 58   void CommandLine::add(Option& option)
966 11 Oct 07 peter 59   {
1426 20 Aug 08 peter 60     if (option.long_name().size()) {
1426 20 Aug 08 peter 61       if (long_options_.find(option.long_name())!=long_options_.end()) {
1426 20 Aug 08 peter 62         std::stringstream ss;
1426 20 Aug 08 peter 63         ss << "yat::utility::Commandline: two options with long_name: "
1426 20 Aug 08 peter 64            << option.long_name();
2210 05 Mar 10 peter 65         throw runtime_error(ss.str());
1426 20 Aug 08 peter 66       }
966 11 Oct 07 peter 67       long_options_[option.long_name()] = &option;
1426 20 Aug 08 peter 68     }
1426 20 Aug 08 peter 69     if (option.short_name()) {
1426 20 Aug 08 peter 70       if (short_options_.find(option.short_name())!=short_options_.end()) {
1426 20 Aug 08 peter 71         std::stringstream ss;
1426 20 Aug 08 peter 72         ss << "yat::utility::Commandline: two options with short_name: "
1426 20 Aug 08 peter 73            << option.short_name();
2210 05 Mar 10 peter 74         throw runtime_error(ss.str());
1426 20 Aug 08 peter 75       }
966 11 Oct 07 peter 76       short_options_[option.short_name()] = &option;
1426 20 Aug 08 peter 77     }
966 11 Oct 07 peter 78     if (option.long_name().size() || option.short_name())
966 11 Oct 07 peter 79       options_.push_back(&option);
966 11 Oct 07 peter 80   }
966 11 Oct 07 peter 81
966 11 Oct 07 peter 82
1468 02 Sep 08 peter 83   void CommandLine::allow_free_args(size_t n)
1468 02 Sep 08 peter 84   {
1468 02 Sep 08 peter 85     free_arg_max_ = n;
1468 02 Sep 08 peter 86   }
1468 02 Sep 08 peter 87
1468 02 Sep 08 peter 88
1480 09 Sep 08 peter 89   const std::vector<std::string>& CommandLine::free_args(void) const
1480 09 Sep 08 peter 90   {
1480 09 Sep 08 peter 91     return free_arg_;
1480 09 Sep 08 peter 92   }
1480 09 Sep 08 peter 93
1480 09 Sep 08 peter 94
966 11 Oct 07 peter 95   bool CommandLine::is_long_option(std::string str) const
966 11 Oct 07 peter 96   {
966 11 Oct 07 peter 97     return (str.size()>2 && str[0]=='-' && str[1]=='-');
966 11 Oct 07 peter 98   }
966 11 Oct 07 peter 99
966 11 Oct 07 peter 100
966 11 Oct 07 peter 101   bool CommandLine::is_short_option(std::string str) const
966 11 Oct 07 peter 102   {
966 11 Oct 07 peter 103     return (str.size()>=2 && str[0]=='-' && str[1]!='-');
966 11 Oct 07 peter 104   }
966 11 Oct 07 peter 105
966 11 Oct 07 peter 106
966 11 Oct 07 peter 107   void CommandLine::parse(int argc, char* argv[])
3781 19 Dec 18 peter 108   {
1741 22 Jan 09 peter 109     parsed_=true;
966 11 Oct 07 peter 110     using namespace std;
966 11 Oct 07 peter 111     // just in case it is not pristine
4252 18 Nov 22 peter 112     for_each(options_.begin(), options_.end(),std::mem_fn(&Option::reset));
966 11 Oct 07 peter 113
3781 19 Dec 18 peter 114     std::vector<std::string> arguments(argv, argv+argc);
3781 19 Dec 18 peter 115     std::vector<std::string>::iterator arg(arguments.begin());
3781 19 Dec 18 peter 116     std::vector<std::string>::iterator end(arguments.end());
966 11 Oct 07 peter 117     // keeping string after last /
3781 19 Dec 18 peter 118     program_name_ = basename(*arg);
3781 19 Dec 18 peter 119     ++arg;
966 11 Oct 07 peter 120
2458 03 Apr 11 peter 121     bool ok=true;
2458 03 Apr 11 peter 122     std::string error_message;
2458 03 Apr 11 peter 123     for (; arg!=end; ++arg) {
2458 03 Apr 11 peter 124       try {
2458 03 Apr 11 peter 125         parse(arg, end);
2458 03 Apr 11 peter 126       }
2458 03 Apr 11 peter 127       catch (cmd_error& e) {
2458 03 Apr 11 peter 128         if (ok) { // we only store first error
2458 03 Apr 11 peter 129           ok = false;
2458 03 Apr 11 peter 130           error_message = e.what();
966 11 Oct 07 peter 131         }
966 11 Oct 07 peter 132       }
2458 03 Apr 11 peter 133     }
2458 03 Apr 11 peter 134     // validate all options
2458 03 Apr 11 peter 135     try {
2458 03 Apr 11 peter 136       for_each(options_.begin(), options_.end(),
4252 18 Nov 22 peter 137                std::mem_fn(&Option::validate));
966 11 Oct 07 peter 138     }
2458 03 Apr 11 peter 139     catch (cmd_error& e) {
2458 03 Apr 11 peter 140       if (ok) { // we only store first error
2458 03 Apr 11 peter 141         ok = false;
2458 03 Apr 11 peter 142         error_message = e.what();
2458 03 Apr 11 peter 143       }
2458 03 Apr 11 peter 144     }
4200 19 Aug 22 peter 145
2458 03 Apr 11 peter 146     if (!ok) {
2458 03 Apr 11 peter 147       std::stringstream ss;
2458 03 Apr 11 peter 148       ss << program_name_ << ": " << error_message;
2458 03 Apr 11 peter 149       throw cmd_error(ss.str());
2458 03 Apr 11 peter 150     }
2458 03 Apr 11 peter 151   }
2458 03 Apr 11 peter 152
4200 19 Aug 22 peter 153
2458 03 Apr 11 peter 154   void CommandLine::parse_long(std::vector<std::string>::iterator& arg,
2458 03 Apr 11 peter 155                                std::vector<std::string>::iterator& last)
2458 03 Apr 11 peter 156   {
2458 03 Apr 11 peter 157     std::string key(arg->substr(2));
2458 03 Apr 11 peter 158     std::stringstream ss2(key);
2458 03 Apr 11 peter 159     getline(ss2, key, '=');
2458 03 Apr 11 peter 160     std::string value;
2458 03 Apr 11 peter 161     getline(ss2, value, '\0');
2458 03 Apr 11 peter 162     if (!value.empty()){
2458 03 Apr 11 peter 163       *arg = value;
2458 03 Apr 11 peter 164       *(--arg) = std::string("--")+key;
2964 22 Jan 13 peter 165     }
2458 03 Apr 11 peter 166     else
2458 03 Apr 11 peter 167       *arg = key;
2458 03 Apr 11 peter 168     std::map<std::string, Option*>::const_iterator
2458 03 Apr 11 peter 169       iter(long_options_.find(key));
2458 03 Apr 11 peter 170     if (iter!=long_options_.end())
2458 03 Apr 11 peter 171       iter->second->parse(arg, last);
2964 22 Jan 13 peter 172     else if (key.size()>3 && key.substr(0,3)=="no-") {
2458 03 Apr 11 peter 173       iter = long_options_.find(key.substr(3));
2458 03 Apr 11 peter 174       if (iter!=long_options_.end())
2458 03 Apr 11 peter 175         iter->second->parse(arg, last);
2964 22 Jan 13 peter 176     }
2853 23 Sep 12 peter 177     if (iter==long_options_.end()) {
2458 03 Apr 11 peter 178       std::stringstream ss3;
2964 22 Jan 13 peter 179       ss3 << "unrecognized option '" << key << "'\n" << try_help();
2458 03 Apr 11 peter 180       throw cmd_error(ss3.str());
2458 03 Apr 11 peter 181     }
2458 03 Apr 11 peter 182   }
2458 03 Apr 11 peter 183
2458 03 Apr 11 peter 184   void CommandLine::parse_short(std::vector<std::string>::iterator& arg,
2458 03 Apr 11 peter 185                                 std::vector<std::string>::iterator& last)
2458 03 Apr 11 peter 186   {
2458 03 Apr 11 peter 187     size_t size=arg->size();
2458 03 Apr 11 peter 188     for (size_t i=1; i<size; ++i){
2458 03 Apr 11 peter 189       std::map<char, Option*>::const_iterator
2458 03 Apr 11 peter 190         iter(short_options_.find((*arg)[i]));
2458 03 Apr 11 peter 191       if (iter==short_options_.end()) {
2458 03 Apr 11 peter 192         std::stringstream ss2;
2458 03 Apr 11 peter 193         ss2 << ": invalid option -- " << (*arg)[i] << "\n"
2609 02 Nov 11 peter 194             << try_help();
2458 03 Apr 11 peter 195         throw cmd_error(ss2.str());
4200 19 Aug 22 peter 196       }
4200 19 Aug 22 peter 197       else
2458 03 Apr 11 peter 198         iter->second->parse(arg, last);
2458 03 Apr 11 peter 199     }
2458 03 Apr 11 peter 200   }
2458 03 Apr 11 peter 201
2458 03 Apr 11 peter 202
2458 03 Apr 11 peter 203   void CommandLine::parse_free_arg(std::vector<std::string>::iterator& arg,
2458 03 Apr 11 peter 204                                    std::vector<std::string>::iterator& last)
2458 03 Apr 11 peter 205   {
2458 03 Apr 11 peter 206     free_arg_.push_back(*arg);
2458 03 Apr 11 peter 207     if (free_arg_.size()>free_arg_max_) {
1874 17 Mar 09 peter 208       std::stringstream ss2;
2458 03 Apr 11 peter 209       ss2 << ": invalid option -- " << *arg << "\n"
2609 02 Nov 11 peter 210           << try_help();
1874 17 Mar 09 peter 211       throw cmd_error(ss2.str());
966 11 Oct 07 peter 212     }
966 11 Oct 07 peter 213   }
966 11 Oct 07 peter 214
4200 19 Aug 22 peter 215
2458 03 Apr 11 peter 216   void CommandLine::parse(std::vector<std::string>::iterator& first,
2458 03 Apr 11 peter 217                           std::vector<std::string>::iterator& last)
2458 03 Apr 11 peter 218   {
2458 03 Apr 11 peter 219     if (is_long_option(*first))
2458 03 Apr 11 peter 220       parse_long(first, last);
2458 03 Apr 11 peter 221     else if (is_short_option(*first))
2458 03 Apr 11 peter 222       parse_short(first, last);
2458 03 Apr 11 peter 223     else
2458 03 Apr 11 peter 224       parse_free_arg(first, last);
966 11 Oct 07 peter 225
2458 03 Apr 11 peter 226   }
2458 03 Apr 11 peter 227
4200 19 Aug 22 peter 228
1741 22 Jan 09 peter 229   bool CommandLine::parsed(void) const
1741 22 Jan 09 peter 230   {
1741 22 Jan 09 peter 231     return parsed_;
1741 22 Jan 09 peter 232   }
1741 22 Jan 09 peter 233
1741 22 Jan 09 peter 234
966 11 Oct 07 peter 235   std::string CommandLine::program_name(void) const
966 11 Oct 07 peter 236   {
966 11 Oct 07 peter 237     return program_name_;
966 11 Oct 07 peter 238   }
966 11 Oct 07 peter 239
966 11 Oct 07 peter 240
2265 05 Jun 10 peter 241   void CommandLine::sort(void)
2265 05 Jun 10 peter 242   {
2265 05 Jun 10 peter 243     sort(OptionCompare());
2265 05 Jun 10 peter 244   }
2265 05 Jun 10 peter 245
2265 05 Jun 10 peter 246
966 11 Oct 07 peter 247   std::string CommandLine::try_help(void) const
966 11 Oct 07 peter 248   {
2964 22 Jan 13 peter 249     return std::string("Try '"+program_name()+" --help' for more information.");
966 11 Oct 07 peter 250   }
966 11 Oct 07 peter 251
966 11 Oct 07 peter 252
981 22 Oct 07 peter 253   std::ostream& operator<<(std::ostream& os, const CommandLine& cmd)
966 11 Oct 07 peter 254   {
982 22 Oct 07 peter 255     os << cmd.description_ << "\n";
966 11 Oct 07 peter 256     ColumnStream cs2(os, 2);
972 17 Oct 07 peter 257     std::string::size_type width = 0;
4200 19 Aug 22 peter 258     for (std::vector<Option*>::const_iterator i(cmd.options_.begin());
966 11 Oct 07 peter 259          i!=cmd.options_.end();++i) {
966 11 Oct 07 peter 260       std::stringstream ss((*i)->print());
966 11 Oct 07 peter 261       std::string str;
966 11 Oct 07 peter 262       getline(ss, str, '\t');
4200 19 Aug 22 peter 263       width = std::max(width, str.size()+3);
966 11 Oct 07 peter 264     }
966 11 Oct 07 peter 265     cs2.width(0)=width;
966 11 Oct 07 peter 266     cs2.width(1)=76-width;
966 11 Oct 07 peter 267     cs2.margin(0)=2;
966 11 Oct 07 peter 268
4200 19 Aug 22 peter 269     for (std::vector<Option*>::const_iterator i(cmd.options_.begin());
4200 19 Aug 22 peter 270          i!=cmd.options_.end();++i)
966 11 Oct 07 peter 271       cs2 << (*i)->print() << "\n";
966 11 Oct 07 peter 272
966 11 Oct 07 peter 273     return os;
966 11 Oct 07 peter 274   }
966 11 Oct 07 peter 275
966 11 Oct 07 peter 276
4200 19 Aug 22 peter 277   bool CommandLine::OptionCompare::operator()(const Option* lhs,
2265 05 Jun 10 peter 278                                               const Option* rhs) const
2265 05 Jun 10 peter 279   {
2265 05 Jun 10 peter 280     assert(lhs);
2265 05 Jun 10 peter 281     assert(rhs);
2265 05 Jun 10 peter 282     std::string lhs_str = lhs->long_name();
2265 05 Jun 10 peter 283     if (lhs_str.empty())
2265 05 Jun 10 peter 284       lhs_str = lhs->short_name();
2265 05 Jun 10 peter 285     std::string rhs_str = rhs->long_name();
2265 05 Jun 10 peter 286     if (rhs_str.empty())
2265 05 Jun 10 peter 287       rhs_str = rhs->short_name();
2265 05 Jun 10 peter 288     return lhs_str < rhs_str;
2265 05 Jun 10 peter 289   }
2265 05 Jun 10 peter 290
2265 05 Jun 10 peter 291
966 11 Oct 07 peter 292 }}} // of namespace utility, yat, and theplu