00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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>
00033 #include <istream>
00034 #include <algorithm>
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <cctype>
00038 using namespace std;
00039
00040 namespace ProgramOptions {
00042
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
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
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();
00200 if (alias()) {
00201 col += 3;
00202 }
00203 std::size_t argN = strlen(argName());
00204 if (argN) {
00205 col += (argN + 1);
00206 if (value()->isImplicit()) {
00207 col += 2;
00208 }
00209 if (value()->isNegatable()) {
00210 col += 3;
00211 }
00212 }
00213 else if (value()->isNegatable()) {
00214 col += 5;
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
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
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
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
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
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
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
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
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
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
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
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
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
00636 if (!o->value()->isFlag()) {
00637
00638 addOptionValue(o, val);
00639 return true;
00640 }
00641 else {
00642
00643 addOptionValue(o, "");
00644 ++optName;
00645 }
00646 }
00647 else if (*val != 0 || (val = next()) != 0) {
00648
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
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
00731 while (std::isspace(static_cast<unsigned char>(*cmd_))) { ++cmd_; }
00732 if (!*cmd_) return 0;
00733 tok_.clear();
00734
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
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;
00783 std::string sectionValue;
00784 bool inSection = false;
00785 FindType ft = OptionContext::find_name_or_prefix;
00786 SharedOptPtr opt;
00787
00788
00789
00790
00791
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
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
00809
00810 if (inSection && (opt = getOption(sectionName.c_str(), ft)).get()) {
00811 addOptionValue(opt, sectionValue);
00812 }
00813
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) {
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 }
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
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 }