program_options.cpp
Go to the documentation of this file.
00001 //
00002 //  Copyright (c) Benjamin Kaufmann 2004
00003 //
00004 //  This is free software; you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation; either version 2 of the License, or
00007 //  (at your option) any later version. 
00008 // 
00009 //  This file is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this file; if not, write to the Free Software
00016 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 //
00018 //
00019 // NOTE: ProgramOptions is inspired by Boost.Program_options
00020 //       see: www.boost.org/libs/program_options
00021 //
00022 #ifdef _MSC_VER
00023 #pragma warning (disable : 4786)
00024 #pragma warning (disable : 4503)
00025 #pragma warning (disable : 4996)
00026 #endif
00027 #include <program_opts/program_options.h>
00028 #include <program_opts/errors.h>
00029 #include <cassert>
00030 #include <cstring>
00031 #include <climits>
00032 #include <ostream>  // for op<<
00033 #include <istream>  // for CfgFileParser
00034 #include <algorithm>// std::sort
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <cctype>
00038 using namespace std;
00039 
00040 namespace ProgramOptions {
00042 // DefaultFormat
00044 std::size_t DefaultFormat::format(std::vector<char>& buf, const Option& o, std::size_t maxW) {
00045         buf.clear();
00046         size_t bufSize = std::max(maxW, o.maxColumn()) + 3;
00047         const char* arg= o.argName();
00048         const char* np = "";
00049         const char* ap = "";
00050         if (o.value()->isNegatable()) {
00051                 if (!*arg) { np = "[no-]"; }
00052                 else       { ap = "|no"; bufSize += strlen(ap); }
00053         }
00054         buf.resize(bufSize);
00055         char*  buffer  = &buf[0];
00056         size_t n       = sprintf(buffer, "  --%s%s", np, o.name().c_str());
00057         if (o.value()->isImplicit() && *arg) {
00058                 n += sprintf(buffer+n, "[=%s%s]", arg, ap);
00059         }
00060         if (o.alias()) {
00061                 n += sprintf(buffer+n, ",-%c", o.alias());
00062         }
00063         if (!o.value()->isImplicit()) {
00064                 n += sprintf(buffer+n, "%c%s%s", (!o.alias()?'=':' '), arg, ap);
00065         }
00066         if (n < maxW) n += sprintf(buffer+n, "%-*.*s", int(maxW-n), int(maxW-n), " ");
00067         assert(n <= bufSize);
00068         return n;
00069 }
00070 std::size_t DefaultFormat::format(std::vector<char>& buf, const char* desc, const Value& val, std::size_t) {
00071         std::size_t minS = strlen(desc);
00072         const char* temp = 0;
00073         if (!desc)  desc = "";
00074         buf.clear();
00075         buf.reserve(minS+2);
00076         buf.push_back(':');
00077         buf.push_back(' ');
00078         for (const char* look;; ++desc) {
00079                 look = desc;
00080                 while (*look && *look != '%') {
00081                         ++look;
00082                 }
00083                 if (look != desc)      { buf.insert(buf.end(), desc, look); }
00084                 if      (!*look++ || !*look) break;
00085                 else if (*look == 'D') { temp = val.defaultsTo(); }
00086                 else if (*look == 'A') { temp = val.arg(); }
00087                 else if (*look == 'I') { temp = val.implicit(); }
00088                 else                   { buf.push_back(*look); }
00089                 if (temp)              { buf.insert(buf.end(), temp, temp + strlen(temp)); }
00090                 desc = look;
00091                 temp = 0;
00092         }
00093         buf.push_back('\n');
00094         return buf.size();
00095 }
00096 std::size_t DefaultFormat::format(std::vector<char>& buffer, const OptionGroup& grp) {
00097         buffer.clear();
00098         if (grp.caption().length()) {
00099                 buffer.reserve(grp.caption().length() + 4);
00100                 buffer.push_back('\n');
00101                 buffer.insert(buffer.end(), grp.caption().begin(), grp.caption().end());
00102                 buffer.push_back(':');
00103                 buffer.push_back('\n');
00104                 buffer.push_back('\n');
00105         }
00106         return buffer.size();
00107 }
00108 void OstreamWriter::write(const std::vector<char>& buf, std::size_t n) {
00109         if (n) { out.write(&buf[0], n); }
00110 }
00111 void StringWriter::write(const std::vector<char>& buf, std::size_t n) {
00112         if (n) { out.append(&buf[0], n); }
00113 }
00114 void FileWriter::write(const std::vector<char>& buf, std::size_t n) {
00115         if (n) { fwrite(&buf[0], 1, n, out); }
00116 }
00118 // class Value
00120 Value::Value(byte_t flagSet, State initial)
00121         : state_(static_cast<byte_t>(initial))
00122         , flags_(flagSet)
00123         , descFlag_(0)
00124         , optAlias_(0) {
00125         desc_.value = 0;
00126 }
00127 
00128 Value::~Value() {
00129         if (descFlag_ == desc_pack) {
00130                 ::operator delete(desc_.pack);
00131         }
00132 }
00133 
00134 const char* Value::arg() const {
00135         const char* x = desc(desc_name);
00136         if (x) return x;
00137         return isFlag() ? "" : "<arg>";
00138 }
00139 
00140 Value* Value::desc(DescType t, const char* n) {
00141         if (n == 0) return this;
00142         if (t == desc_implicit) { 
00143                 setProperty(property_implicit);
00144                 if (!*n) return this;
00145         }
00146         if (descFlag_ == 0 || descFlag_ == t) {
00147                 desc_.value = n;
00148                 descFlag_   = static_cast<byte_t>(t);
00149                 return this;
00150         }
00151         if (descFlag_ != desc_pack) {
00152                 const char* oldVal = desc_.value;
00153                 unsigned    oldKey = descFlag_ >> 1u;
00154                 desc_.pack   = (const char**)::operator new(sizeof(const char*[3]));
00155                 desc_.pack[0]= desc_.pack[1] = desc_.pack[2] = 0;
00156                 descFlag_    = desc_pack;
00157                 desc_.pack[oldKey] = oldVal;
00158         }
00159         desc_.pack[t>>1u] = n;
00160         return this;
00161 }
00162 const char* Value::desc(DescType t) const {
00163         if (descFlag_ == t || descFlag_ == desc_pack) {
00164                 return descFlag_ == t
00165                         ? desc_.value
00166                         : desc_.pack[t >> 1u];
00167         }
00168         return 0;
00169 }
00170 
00171 const char* Value::implicit() const {
00172         if (!hasProperty(property_implicit)) return 0;
00173         const char* x = desc(desc_implicit);
00174         return x ? x : "1";
00175 }
00176 
00177 bool Value::parse(const std::string& name, const std::string& value, State st) {
00178         if (!value.empty() || !isImplicit()) return state(doParse(name, value), st);
00179         const char* x = implicit();
00180         assert(x);
00181         return state(doParse(name, x), st);
00182 }
00184 // class Option
00186 Option::Option(const string& longName, char alias, const char* desc, Value* v)
00187         : name_(longName)
00188         , description_(desc ? desc : "")
00189         , value_(v) {
00190         assert(v);
00191         assert(!longName.empty());
00192         value_->alias(alias);
00193 }
00194 
00195 Option::~Option() {
00196         delete value_;
00197 }
00198 std::size_t Option::maxColumn() const {
00199         std::size_t col = 4 + name_.size(); //  --name
00200         if (alias()) {
00201                 col += 3; // ,-o
00202         }
00203         std::size_t argN = strlen(argName());
00204         if (argN) {
00205                 col += (argN + 1); // =arg
00206                 if (value()->isImplicit()) {
00207                         col += 2; // []
00208                 }
00209                 if (value()->isNegatable()) {
00210                         col += 3; // |no
00211                 }
00212         }
00213         else if (value()->isNegatable()) {
00214                 col += 5; // [no-]
00215         }
00216         return col;
00217 }
00218 
00219 bool Option::assignDefault() const {
00220         if (value()->defaultsTo() != 0 && value()->state() != Value::value_defaulted) {
00221                 return value()->parse(name(), value()->defaultsTo(), Value::value_defaulted);
00222         }
00223         return true;
00224 }
00226 // class OptionGroup
00228 OptionGroup::OptionGroup(const std::string& caption, DescriptionLevel hl) : caption_(caption), level_(hl) {}
00229 OptionGroup::~OptionGroup() { }
00230 
00231 OptionInitHelper OptionGroup::addOptions() {
00232         return OptionInitHelper(*this);
00233 }
00234 
00235 void OptionGroup::addOption(std::auto_ptr<Option> option) {
00236         SharedOptPtr opt(option.release());
00237         options_.push_back(opt);
00238 }
00239 
00240 std::size_t OptionGroup::maxColumn(DescriptionLevel level) const {
00241         std::size_t maxW = 0;
00242         for (option_iterator it = options_.begin(), end = options_.end(); it != end; ++it) {
00243                 if ((*it)->descLevel() <= level) {
00244                         maxW = std::max(maxW, (*it)->maxColumn());
00245                 }
00246         }
00247         return maxW;
00248 }
00249 
00250 void OptionGroup::format(OptionOutput& out, size_t maxW, DescriptionLevel dl) const {
00251         for (option_iterator it = options_.begin(), end = options_.end(); it != end; ++it) {
00252                 if ((*it)->descLevel() <= dl) {
00253                         out.printOption(**it, maxW);
00254                 }
00255         }
00256 }
00258 // class OptionInitHelper
00260 OptionInitHelper::OptionInitHelper(OptionGroup& owner)
00261         : owner_(&owner) { }
00262 
00263 OptionInitHelper& OptionInitHelper::operator()(const char* name, Value* val, const char* desc) {
00264         std::auto_ptr<Value> value(val);
00265         if (!name || !*name || *name == ',' || *name == '!') {
00266                 throw Error("Invalid empty option name");
00267         }
00268         const char* n = strchr(name, ',');
00269         string longName; char shortName = 0;
00270         if (!n) {
00271                 longName = name;
00272         }
00273         else {
00274                 longName.assign(name, n);
00275                 unsigned level = owner_->descLevel();
00276                 const char* x  = ++n;
00277                 if (*x && (!x[1] || x[1] == ',')) {
00278                         shortName = *x++;
00279                         x        += *x == ',';
00280                 }
00281                 if (*x == '@') {
00282                         ++x; level = 0;
00283                         while (*x >= '0' && *x <= '9') {
00284                                 level *= 10;
00285                                 level += *x - '0';
00286                                 ++x;
00287                         }
00288                 }
00289                 if (!*n || *x || level > desc_level_hidden) {
00290                         throw Error(std::string("Invalid Key '").append(name).append("'"));
00291                 }
00292                 value->level(DescriptionLevel(level));
00293         }
00294         if (*(longName.end()-1) == '!') {
00295                 bool neg = *(longName.end()-2) != '\\';
00296                 longName.erase(longName.end()- (1+!neg), longName.end());
00297                 if (neg) value->negatable();
00298                 else     longName += '!';
00299         }
00300         owner_->addOption(auto_ptr<Option>(new Option(longName, shortName, desc, value.release())));
00301         return *this;
00302 }
00304 // class OptionContext
00306 OptionContext::OptionContext(const std::string& cap, DescriptionLevel def)
00307         : caption_(cap)
00308         , descLevel_(def) {
00309 }
00310 OptionContext::~OptionContext()
00311 {}
00312 const std::string& OptionContext::caption() const {
00313         return caption_;
00314 }
00315 void OptionContext::setActiveDescLevel(DescriptionLevel x) {
00316         descLevel_ = std::min(x, desc_level_all);
00317 }
00318 size_t OptionContext::findGroupKey(const std::string& name) const {
00319         for (size_t i = 0; i != groups_.size(); ++i) {
00320                 if (groups_[i].caption() == name) { return i; }
00321         }
00322         return size_t(-1);
00323 }
00324 
00325 OptionContext& OptionContext::add(const OptionGroup& options) {
00326         size_t k = findGroupKey(options.caption());
00327         if (k >= groups_.size()) {
00328                 // add as new group
00329                 k = groups_.size();
00330                 groups_.push_back(OptionGroup(options.caption(), options.descLevel()));
00331         }
00332         for (option_iterator it = options.begin(), end = options.end(); it != end; ++it) {
00333                 insertOption(k, *it);
00334         }
00335         groups_[k].setDescriptionLevel(std::min(options.descLevel(), groups_[k].descLevel()));
00336         return *this;
00337 }
00338 
00339 OptionContext& OptionContext::addAlias(const std::string& aliasName, option_iterator option) {
00340         if (option != end() && !aliasName.empty()) {
00341                 key_type k(option - begin());
00342                 if (!index_.insert(Name2Key::value_type(aliasName, k)).second) { throw DuplicateOption(caption(), aliasName); }
00343         }
00344         return *this;
00345 }
00346 
00347 const OptionGroup& OptionContext::findGroup(const std::string& name) const {
00348         std::size_t x = findGroupKey(name);
00349         if (x < groups_.size()) { return groups_[x]; }
00350         throw ContextError(caption(), ContextError::unknown_group, name);
00351 }
00352 const OptionGroup* OptionContext::tryFindGroup(const std::string& name) const {
00353         std::size_t x = findGroupKey(name);
00354         return x < groups_.size() ? &groups_[x] : 0;
00355 }
00356 
00357 OptionContext& OptionContext::add(const OptionContext& other) {
00358         if (this == &other) return *this;
00359         for (size_t g = 0; g != other.groups_.size(); ++g) {
00360                 add(other.groups_[g]);
00361         }
00362         return *this;
00363 }
00364 
00365 void OptionContext::insertOption(size_t groupId, const SharedOptPtr& opt) {
00366         const string& l = opt->name();
00367         key_type k(options_.size());
00368         if (opt->alias()) {
00369                 char sName[2] = {'-', opt->alias()};
00370                 std::string shortName(sName, 2);
00371                 if (!index_.insert(Name2Key::value_type(shortName, k)).second) {
00372                         throw DuplicateOption(caption(), l);
00373                 }
00374         }
00375         if (!l.empty()) {
00376                 if (!index_.insert(Name2Key::value_type(l, k)).second) {
00377                         throw DuplicateOption(caption(), l);
00378                 }
00379         }
00380         options_.push_back(opt);
00381         groups_[groupId].options_.push_back(opt);
00382 }
00383 
00384 OptionContext::option_iterator OptionContext::find(const char* key, FindType t) const {
00385         return options_.begin() + findImpl(key, t, unsigned(-1)).first->second;
00386 }
00387 
00388 OptionContext::option_iterator OptionContext::tryFind(const char* key, FindType t) const {
00389         PrefixRange r = findImpl(key, t, 0u);
00390         return std::distance(r.first, r.second) == 1 ? options_.begin() + r.first->second : end();
00391 }
00392 
00393 OptionContext::PrefixRange OptionContext::findImpl(const char* key, FindType t, unsigned eMask, const std::string& eCtx) const {
00394         std::string k(key ? key : "");
00395         if (t == find_alias && !k.empty() && k[0] != '-') {
00396                 k   += k[0];
00397                 k[0] = '-';
00398         }
00399         index_iterator it = index_.lower_bound(k);
00400         index_iterator up = it;
00401         if (it != index_.end()) {
00402                 if ( (it->first == k) && ( (t & (find_alias|find_name)) != 0 ) ) {
00403                         ++up;
00404                 }
00405                 else if ( (t & find_prefix) != 0 ) {
00406                         k += char(CHAR_MAX);
00407                         up = index_.upper_bound(k);
00408                         k.erase(k.end()-1);
00409                 }
00410         }
00411         if (std::distance(it, up) != 1 && eMask) {
00412                 if ((eMask & 1u) && it == up) { throw UnknownOption(eCtx, k); }
00413                 if ((eMask & 2u) && it != up) { 
00414                         std::string str;
00415                         for (; it != up; ++it) { 
00416                                 str += "  ";
00417                                 str += it->first;
00418                                 str += "\n";
00419                         }
00420                         throw AmbiguousOption(eCtx, k, str);
00421                 }
00422         }
00423         return PrefixRange(it, up); 
00424 }
00425 
00426 OptionOutput& OptionContext::description(OptionOutput& out) const {
00427         DescriptionLevel dl = descLevel_;
00428         if (out.printContext(*this)) {          
00429                 size_t maxW = 23;
00430                 for (size_t i = 0; i != groups(); ++i) {
00431                         maxW = std::max(maxW, groups_[i].maxColumn(dl));
00432                 }
00433                 // print all visible groups
00434                 for (std::size_t i = 1; i < groups_.size(); ++i) {
00435                         if (groups_[i].descLevel() <= dl && out.printGroup(groups_[i])) {
00436                                 groups_[i].format(out, maxW, dl);
00437                         }
00438                 }
00439                 if (!groups_.empty() && groups_[0].descLevel() <= dl && out.printGroup(groups_[0])) {
00440                         groups_[0].format(out, maxW, dl);
00441                 }
00442         }
00443         return out;
00444 }
00445 
00446 std::string OptionContext::defaults(std::size_t n) const {
00447         DescriptionLevel dl = descLevel_;
00448         std::size_t line    = n;
00449         std::string defs; 
00450         defs.reserve(options_.size());
00451         std::string opt; opt.reserve(80);
00452         for (int g = 0; g < 2; ++g) {
00453                 // print all sub-groups followed by main group
00454                 for (std::size_t i = (g == 0), end = (g == 0) ? groups_.size() : 1; i < end; ++i) {
00455                         if (groups_[i].descLevel() <= dl) {
00456                                 for (option_iterator it = groups_[i].begin(), end = groups_[i].end(); it != end; ++it) {
00457                                         const Option& o = **it;
00458                                         if (o.value()->defaultsTo() && o.descLevel() <= dl) {
00459                                                 ((((opt += "--") += o.name()) += "=") += o.value()->defaultsTo());
00460                                                 if (line + opt.size() > 78) {
00461                                                         defs += '\n';
00462                                                         defs.append(n, ' ');
00463                                                         line  = n;
00464                                                 }
00465                                                 defs += opt;
00466                                                 defs += ' ';
00467                                                 line += opt.size() + 1;
00468                                                 opt.clear();
00469                                         }
00470                                 }
00471                         }
00472                 }
00473         }
00474         return defs;    
00475 }
00476 std::ostream& operator<<(std::ostream& os, const OptionContext& grp) {
00477         StreamOut out(os);
00478         grp.description(out);
00479         return os;
00480 }
00481 
00482 bool OptionContext::assignDefaults(const ParsedOptions& opts) const {
00483         for (option_iterator it = begin(), end = this->end(); it != end; ++it) {
00484                 const Option& o = **it;
00485                 if (opts.count(o.name()) == 0 && !o.assignDefault()) {
00486                         throw ValueError(caption(), ValueError::invalid_default, o.name(), o.value()->defaultsTo());
00487                 }
00488         }
00489         return true;
00490 }
00492 // class ParsedOptions
00494 ParsedOptions::ParsedOptions()  {}
00495 ParsedOptions::~ParsedOptions() { parsed_.clear(); }
00496 bool ParsedOptions::assign(const ParsedValues& p, const ParsedOptions* exclude) {
00497         if (!p.ctx) return false;
00498         struct Assign {
00499                 Assign(ParsedOptions* x, const ParsedOptions* exclude) : self(x), ignore(exclude) {}
00500                 void assign(const ParsedValues& p) {
00501                         begin = it = p.begin();
00502                         // assign parsed values
00503                         for (ParsedValues::iterator end = p.end(); it != end; ++it) {
00504                                 const Option& o = *it->first;
00505                                 if (ignore && ignore->count(o.name()) != 0 && !o.value()->isComposing()){ 
00506                                         continue;
00507                                 }
00508                                 if (int ret = self->assign(o, it->second)) {
00509                                         throw ValueError(p.ctx ? p.ctx->caption() : "", static_cast<ValueError::Type>(ret-1), o.name(), it->second);
00510                                 }
00511                         }
00512                 }
00513                 ~Assign() { 
00514                         for (ParsedValues::iterator x = begin, end = this->it; x != end; ++x) {
00515                                 const Option& o = *x->first;
00516                                 assert(o.value()->state() == Value::value_fixed || self->parsed_.count(o.name()) != 0 || ignore->count(o.name()) != 0);
00517                                 if (o.value()->state() == Value::value_fixed) {
00518                                         self->parsed_.insert(x->first->name());
00519                                         o.value()->state(Value::value_unassigned);
00520                                 }
00521                         }
00522                 }
00523                 ParsedOptions* self;
00524                 const ParsedOptions*   ignore;
00525                 ParsedValues::iterator begin;
00526                 ParsedValues::iterator it;
00527         } scoped(this, exclude);
00528         scoped.assign(p);
00529         return true;
00530 }
00531 int ParsedOptions::assign(const Option& o, const std::string& value) {
00532         unsigned badState = 0;
00533         if (!o.value()->isComposing()) {
00534                 if (parsed_.count(o.name())) { return 0; }
00535                 badState = (Value::value_fixed & o.value()->state());
00536         }
00537         if (badState || !o.value()->parse(o.name(), value, Value::value_fixed)) {
00538                 return badState 
00539                         ? 1 + ValueError::multiple_occurences
00540                         : 1 + ValueError::invalid_value;
00541         }
00542         return 0;
00543 }
00545 // class ParsedValues
00547 namespace {
00548 template <class P>
00549 struct LessFirst {
00550         bool operator()(const P& lhs, const P& rhs) const {
00551                 return lhs.first.get() < rhs.first.get();
00552         }
00553 };
00554 }
00555 void ParsedValues::add(const std::string& name, const std::string& value) {
00556         OptionContext::option_iterator it = ctx->tryFind(name.c_str());
00557         if (it != ctx->end()) {
00558                 add(*it, value);
00559         }
00560 }
00562 // class OptionParser
00564 OptionParser::OptionParser(ParseContext& o)
00565         : ctx_(&o)
00566 {}
00567 
00568 OptionParser::~OptionParser()
00569 {}
00570 
00571 ParseContext& OptionParser::parse() {
00572         doParse();
00573         return *ctx_;
00574 }
00575 ParseContext::~ParseContext() {}
00576 namespace {   
00578 // class CommandLineParser
00580 class CommandLineParser : public OptionParser {
00581 public:
00582         enum OptionType {short_opt, long_opt, end_opt, no_opt};
00583         CommandLineParser(ParseContext& ctx, unsigned f)
00584                 : OptionParser(ctx)
00585                 , flags(f)
00586         {}
00587         std::vector<const char*> remaining;
00588         unsigned flags;
00589 private:
00590         virtual const char* next() = 0;
00591         void doParse() {
00592                 bool breakEarly  = false;
00593                 int  posKey      = 0;
00594                 const char* curr;
00595                 while ( (curr=next()) != 0 && !breakEarly ) {
00596                         switch(getOptionType(curr)) {
00597                                 case short_opt: if (handleShortOpt(curr + 1)) curr = 0; break;
00598                                 case long_opt:  if (handleLongOpt(curr + 2))  curr = 0; break;
00599                                 case end_opt:   curr = 0; breakEarly = true; break;
00600                                 case no_opt: {
00601                                         SharedOptPtr opt = getOption(posKey++, curr);
00602                                         if (opt.get()) {
00603                                                 addOptionValue(opt, curr);
00604                                                 curr = 0;
00605                                         }
00606                                         break;}
00607                                 default:
00608                                         assert(0);
00609                         }
00610                         if (curr) {
00611                                 remaining.push_back(curr);
00612                         }
00613                 }
00614                 while (curr)  {
00615                         remaining.push_back(curr);
00616                         curr = next();
00617                 }
00618         }
00619         OptionType getOptionType(const char* o) const {
00620                 if (strncmp(o, "--", 2) == 0) {
00621                         return o[2] ? long_opt : end_opt;
00622                 }
00623                 return *o == '-' && *(o + 1) != '\0' ? short_opt : no_opt;
00624         }
00625         bool handleShortOpt(const char* optName) {
00626                 // either -o value or -o[value|opts]
00627                 char optn[2];
00628                 optn[1] = '\0';
00629                 SharedOptPtr o;
00630                 while (*optName) {
00631                         optn[0]         = *optName;
00632                         const char* val = optName + 1;
00633                         if ( (o = getOption(optn, OptionContext::find_alias)).get() ) {
00634                                 if (o->value()->isImplicit()) {
00635                                         // -ovalue or -oopts
00636                                         if (!o->value()->isFlag()) {
00637                                                 // consume (possibly empty) value
00638                                                 addOptionValue(o, val);
00639                                                 return true;
00640                                         }
00641                                         else {
00642                                                 // -o + more options
00643                                                 addOptionValue(o, "");
00644                                                 ++optName;
00645                                         }
00646                                 }
00647                                 else if (*val != 0 || (val = next()) != 0) {
00648                                         // -ovalue or -o value
00649                                         addOptionValue(o, val);
00650                                         return true;
00651                                 }
00652                                 else {
00653                                         throw SyntaxError(SyntaxError::missing_value, optn);
00654                                 }
00655                         }
00656                         else {
00657                                 return false;
00658                         }
00659                 }
00660                 return true;
00661         }
00662         bool handleLongOpt(const char* optName) {
00663                 string name(optName);
00664                 string value;
00665                 string::size_type p = name.find('='); 
00666                 if (p != string::npos) {
00667                         value.assign(name, p + 1, string::npos);
00668                         name.erase(p, string::npos);
00669                 }
00670                 SharedOptPtr o, on;
00671                 bool neg = false;
00672                 if (value.empty() && std::strncmp(optName, "no-", 3) == 0) {
00673                         try        { on = getOption(optName+3, OptionContext::find_name_or_prefix); }
00674                         catch(...) {}
00675                         if (on.get() && !on->value()->isNegatable()) { on.reset(); }
00676                 }
00677                 try  { o = getOption(name.c_str(), OptionContext::find_name_or_prefix); }
00678                 catch (const UnknownOption&) { 
00679                         if (!on.get()) { throw; } 
00680                 }
00681                 if (!o.get() && on.get()) {
00682                         std::swap(o, on);
00683                         value = "no";
00684                         neg   = true;
00685                 }
00686                 if (o.get()) {
00687                         if (!o->value()->isImplicit() && value.empty()) {
00688                                 if (const char* v = next()) { value = v; }
00689                                 else { throw SyntaxError(SyntaxError::missing_value, name); }
00690                         }
00691                         else if (o->value()->isFlag() && !value.empty() && !neg && (flags & unsigned(command_line_allow_flag_value)) == 0u) {
00692                                 // flags don't have values
00693                                 throw SyntaxError(SyntaxError::extra_value, name);
00694                         }
00695                         addOptionValue(o, value);
00696                         return true;
00697                 }
00698                 return false;
00699         }
00700 };
00701 
00702 class ArgvParser : public CommandLineParser {
00703 public:
00704         ArgvParser(ParseContext& ctx, int startPos, char** argv, unsigned flags)
00705                 : CommandLineParser(ctx, flags)
00706                 , currentArg_(0)
00707                 , argPos_(startPos)
00708                 , argv_(argv) {
00709         }
00710 
00711 private:
00712         const char* next() {
00713                 currentArg_ = argv_[argPos_++];
00714                 return currentArg_;
00715         }
00716         char*  currentArg_;
00717         int    argPos_;
00718         char** argv_;   
00719 };
00720 
00721 class CommandStringParser : public CommandLineParser {
00722 public:
00723         CommandStringParser(const char* cmd, ParseContext& ctx, unsigned flags)
00724                 : CommandLineParser(ctx, flags)
00725                 , cmd_(cmd ? cmd : "") {
00726                 tok_.reserve(80);
00727         }
00728 private:
00729         const char* next() {
00730                 // skip leading white
00731                 while (std::isspace(static_cast<unsigned char>(*cmd_))) { ++cmd_; }
00732                 if (!*cmd_) return 0;
00733                 tok_.clear();
00734                 // find end of current arg
00735                 for (char c, t = ' ', n; (c = *cmd_) != 0; ++cmd_) {
00736                         if      (c == t)                                      { if (t == ' ') break; t = ' '; }
00737                         else if ((c == '\'' || c == '"') && t == ' ')         { t = c; }
00738                         else if (c != '\\')                                   { tok_ += c; }
00739                         else if ((n=cmd_[1]) == '"' || n == '\'' || n == '\\'){ tok_ += n; ++cmd_; }
00740                         else                                                  { tok_ += c; }
00741                 }
00742                 return tok_.c_str();
00743         }
00744         CommandStringParser& operator=(const CommandStringParser&);
00745         const char* cmd_;
00746         std::string tok_;
00747 };
00749 // class CfgFileParser
00751 class CfgFileParser : public OptionParser
00752 {
00753 public:
00754         CfgFileParser(ParseContext& ctx, std::istream& in)
00755                 : OptionParser(ctx)
00756                 , in_(in)
00757         {}
00758 private: void operator=(const CfgFileParser&);
00759         inline void trimLeft(std::string& str, const std::string& charList = " \t") {
00760                 std::string::size_type pos = str.find_first_not_of(charList);
00761                 if (pos != 0)
00762                         str.erase(0, pos);
00763         }
00764         inline void trimRight(std::string& str, const std::string& charList = " \t") {
00765                 std::string::size_type pos = str.find_last_not_of(charList);
00766                 if (pos != std::string::npos)
00767                         str.erase(pos + 1, std::string::npos);
00768         }
00769         bool splitHalf( const std::string& str, const std::string& seperator,
00770                 std::string& leftSide,
00771                 std::string& rightSide) {
00772                 std::string::size_type sepPos = str.find(seperator);
00773                 leftSide.assign(str, 0, sepPos);
00774                 if (sepPos != std::string::npos) {
00775                         rightSide.assign(str, sepPos + seperator.length(), std::string::npos);
00776                         return true;
00777                 }
00778                 return false;
00779         }
00780         void doParse() {
00781                 int lineNr = 0;
00782                 std::string sectionName;      // current section name
00783                 std::string sectionValue;     // current section value
00784                 bool inSection = false;       // true if multi line section value
00785                 FindType ft    = OptionContext::find_name_or_prefix;
00786                 SharedOptPtr opt;
00787                 // reads the config file.
00788                 // A config file may only contain empty lines, single line comments or
00789                 // sections structured in a name = value fashion.
00790                 // value can span multiple lines, but parts in different lines than name
00791                 // must not contain a '='-Character.
00792                 for (std::string line; std::getline(in_, line);) {
00793                         ++lineNr;
00794                         trimLeft(line);
00795                         trimRight(line);
00796 
00797                         if (line.empty() || line.find("#") == 0) {
00798                                 // An empty line or single line comment stops a multi line section value.
00799                                 if (inSection) {
00800                                         if ( (opt = getOption(sectionName.c_str(), ft)).get() )
00801                                                 addOptionValue(opt, sectionValue);
00802                                         inSection = false;
00803                                 }
00804                                 continue;
00805                         }
00806                         std::string::size_type pos;
00807                         if ( (pos = line.find("=")) != std::string::npos) {
00808                                 // A new section terminates a multi line section value.
00809                                 // First process the current section value...
00810                                 if (inSection && (opt = getOption(sectionName.c_str(), ft)).get()) {
00811                                         addOptionValue(opt, sectionValue);
00812                                 }
00813                                 // ...then save the new section's value.
00814                                 splitHalf(line, "=", sectionName, sectionValue);
00815                                 trimRight(sectionName);
00816                                 trimLeft(sectionValue, " \t\n");
00817                                 inSection = true;
00818                         }
00819                         else if (inSection) {
00820                                 sectionValue += " ";
00821                                 sectionValue += line;
00822                         }
00823                         else {
00824                                 throw SyntaxError(SyntaxError::invalid_format, line);
00825                         }
00826                 }
00827                 if (inSection) { // file does not end with an empty line
00828                         if ( (opt = getOption(sectionName.c_str(), ft)).get() )
00829                                 addOptionValue(opt, sectionValue);
00830                 }
00831         }
00832         std::istream& in_;
00833 };
00834 class DefaultContext : public ParseContext {
00835 public:
00836         DefaultContext(const OptionContext& o, bool allowUnreg, PosOption po)
00837                 : posOpt(po)
00838                 , parsed(o)
00839                 , eMask(2u + unsigned(!allowUnreg)) {}
00840         SharedOptPtr  getOption(const char* name, FindType ft) {
00841                 OptionContext::OptionRange r = parsed.ctx->findImpl(name, ft, eMask);
00842                 if (r.first != r.second) { return *(parsed.ctx->begin() + r.first->second); }
00843                 return SharedOptPtr(0);
00844         }
00845         SharedOptPtr  getOption(int, const char* tok) {
00846                 std::string optName;
00847                 if (!posOpt || !posOpt(tok, optName)) { return getOption("Positional Option", OptionContext::find_name_or_prefix); }
00848                 return getOption(optName.c_str(), OptionContext::find_name_or_prefix);
00849         }
00850         void          addValue(const SharedOptPtr& key, const std::string& value) { parsed.add(key, value); }
00851         PosOption    posOpt;
00852         ParsedValues parsed;
00853         unsigned     eMask;
00854 };
00855 
00856 } // end unnamed namespace
00857 
00858 ParsedValues parseCommandLine(int& argc, char** argv, const OptionContext& o, bool allowUnreg, PosOption po, unsigned flags) {
00859         DefaultContext ctx(o, allowUnreg, po);
00860         return static_cast<DefaultContext&>(parseCommandLine(argc, argv, ctx, flags)).parsed;
00861 }
00862 ParseContext& parseCommandLine(int& argc, char** argv, ParseContext& ctx, unsigned flags) {
00863         ArgvParser parser(ctx, 1, argv, flags);
00864         parser.parse();
00865         argc = 1 + (int)parser.remaining.size();
00866         for (int i = 1; i != argc; ++i) {
00867                 argv[i] = const_cast<char*>(parser.remaining[i-1]);
00868         }
00869         argv[argc] = 0;
00870         return ctx;
00871 }
00872 ParseContext& parseCommandString(const char* cmd, ParseContext& ctx, unsigned flags) {
00873         return CommandStringParser(cmd, ctx, flags).parse();
00874 }
00875 ParsedValues parseCommandString(const std::string& cmd, const OptionContext& o, bool allowUnreg, PosOption po, unsigned flags) {
00876         DefaultContext ctx(o, allowUnreg, po);
00877         return static_cast<DefaultContext&>(CommandStringParser(cmd.c_str(), ctx, flags).parse()).parsed;
00878 }
00879 
00880 ParsedValues parseCfgFile(std::istream& in, const OptionContext& o, bool allowUnreg) {
00881         DefaultContext ctx(o, allowUnreg, 0);
00882         return static_cast<DefaultContext&>(CfgFileParser(ctx, in).parse()).parsed;
00883 }
00884 
00886 // Errors
00888 static std::string quote(const std::string& x) {
00889         return std::string("'").append(x).append("'");
00890 }
00891 static std::string format(SyntaxError::Type t, const std::string& key) {
00892         std::string ret("SyntaxError: ");
00893         ret += quote(key);
00894         switch (t) {
00895                 case SyntaxError::missing_value: ret += " requires a value!";       break;
00896                 case SyntaxError::extra_value:   ret += " does not take a value!";  break;
00897                 case SyntaxError::invalid_format:ret += " unrecognized line!";      break;
00898                 default:                         ret += " unknown syntax!";
00899         };
00900         return ret;
00901 }
00902 static std::string format(ContextError::Type t, const std::string& ctx, const std::string& key, const std::string& alt) {
00903         std::string ret;
00904         if (!ctx.empty()) { ret += "In context "; ret += quote(ctx); ret += ": "; }
00905         switch (t) {
00906                 case ContextError::duplicate_option: ret += "duplicate option: "; break;
00907                 case ContextError::unknown_option:   ret += "unknown option: ";   break;
00908                 case ContextError::ambiguous_option: ret += "ambiguous option: "; break;
00909                 case ContextError::unknown_group:    ret += "unknown group: ";    break;
00910                 default:                             ret += "unknown error in: ";
00911         };
00912         ret += quote(key);
00913         if (t == ContextError::ambiguous_option && !alt.empty()) {
00914                 ret += " could be:\n";
00915                 ret += alt;
00916         }
00917         return ret;
00918 }
00919 static std::string format(ValueError::Type t, const std::string& ctx, const std::string& key, const std::string& value) {
00920         std::string ret; const char* x = "";
00921         if (!ctx.empty()) { ret += "In context "; ret += quote(ctx); ret += ": "; }
00922         switch (t) {
00923                 case ValueError::multiple_occurences: ret += "multiple occurences: "; break;
00924                 case ValueError::invalid_default: x = "default ";
00925                 case ValueError::invalid_value:       
00926                         ret += quote(value); 
00927                         ret += " invalid ";
00928                         ret += x;
00929                         ret += "value for: ";
00930                         break;
00931                 default: ret += "unknown error in: ";
00932         };
00933         ret += quote(key);
00934         return ret;
00935 }
00936 SyntaxError::SyntaxError(Type t, const std::string& key) 
00937         : Error(format(t, key)) 
00938         , key_(key)
00939         , type_(t) {
00940 }
00941 ContextError::ContextError(const std::string& ctx, Type t, const std::string& key, const std::string& alt)
00942         : Error(format(t, ctx, key, alt))
00943         , ctx_(ctx)
00944         , key_(key)
00945         , type_(t) {
00946 }
00947 ValueError::ValueError(const std::string& ctx, Type t, const std::string& opt, const std::string& value)
00948         : Error(format(t, ctx, opt, value))
00949         , ctx_(ctx)
00950         , key_(opt)
00951         , value_(value)
00952         , type_(t) {
00953 }
00954 
00955 }


clasp
Author(s): Benjamin Kaufmann
autogenerated on Thu Aug 27 2015 12:41:39