spot/spot/misc/optionmap.cc
Alexandre Duret-Lutz e419150c30 option_map: Diagnose unused option on request
* spot/misc/optionmap.hh, spot/misc/optionmap.cc (report_unused_options,
set_, set_set_): New methods.
* bin/autfilt.cc, bin/dstar2tgba.cc, bin/ltl2tgba.cc,
bin/ltl2tgta.cc: Call report_unused_options().
* tests/core/ltlcross2.test, tests/core/readsave.test: Fix typos in
options.
* tests/core/minusx.test: New file.
* tests/Makefile.am: Add it.
* NEWS: Mention this.
2016-06-22 20:57:53 +02:00

225 lines
5.7 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2013, 2014, 2015, 2016 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "config.h"
#include <cstring>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <spot/misc/optionmap.hh>
namespace spot
{
const char*
option_map::parse_options(const char* options)
{
while (*options)
{
// Skip leading separators.
while (*options && strchr(" \t\n,;", *options))
++options;
// `!foo' is a shorthand for `foo=0'.
const char* negated = nullptr;
if (*options == '!')
{
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
negated = options++;
}
if (!*options)
{
if (negated)
return negated;
else
break;
}
const char* name_start = options;
// Find the end of the name.
while (*options && !strchr(", \t\n;=", *options))
++options;
std::string name(name_start, options);
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
if (*options != '=')
{
set_(name, !negated);
}
else if (negated)
{
return negated;
}
else
{
++options;
// Skip spaces.
while (*options && strchr(" \t\n", *options))
++options;
if (!*options)
return name_start;
if (*options == '\'' || *options == '"')
{
auto sep = *options;
auto start = options + 1;
do
++options;
while (*options && *options != sep);
if (*options != sep)
return start - 1;
set_str_(name, std::string(start, options));
if (*options)
++options;
}
else
{
char* val_end;
int val = strtol(options, &val_end, 10);
if (val_end == options)
return name_start;
if (*val_end == 'K')
{
val *= 1024;
++val_end;
}
else if (*val_end == 'M')
{
val *= 1024 * 1024;
++val_end;
}
else if (*val_end && !strchr(" \t\n,;", *val_end))
{
return options;
}
options = val_end;
set_(name, val);
}
}
}
return nullptr;
}
int
option_map::get(const char* option, int def) const
{
unused_.erase(option);
auto it = options_.find(option);
return (it == options_.end()) ? def : it->second;
}
std::string
option_map::get_str(const char* option, std::string def) const
{
unused_.erase(option);
auto it = options_str_.find(option);
return (it == options_str_.end()) ? def : it->second;
}
void option_map::set_(const std::string& option, int val)
{
options_[option] = val;
unused_.insert(option);
}
void option_map::set_str_(const std::string& option, const std::string& val)
{
options_str_[option] = val;
unused_.insert(option);
}
int
option_map::set(const char* option, int val, int def)
{
int old = get(option, def);
set_(option, val);
return old;
}
std::string
option_map::set_str(const char* option, std::string val, std::string def)
{
std::string old = get_str(option, def);
set_str_(option, val);
return old;
}
void
option_map::set(const option_map& o)
{
options_ = o.options_;
options_str_ = o.options_str_;
unused_ = o.unused_;
}
int
option_map::operator[](const char* option) const
{
unused_.erase(option);
return get(option);
}
int&
option_map::operator[](const char* option)
{
unused_.erase(option);
return options_[option];
}
std::ostream&
operator<<(std::ostream& os, const option_map& m)
{
for (auto p: m.options_)
os << '"' << p.first << "\" = " << p.second << '\n';
for (auto p: m.options_str_)
os << '"' << p.first << "\" = \"" << p.second << "\"\n";
return os;
}
void option_map::report_unused_options() const
{
auto s = unused_.size();
if (s == 0U)
return;
std::ostringstream os;
if (s == 1U)
{
os << "option '" << *unused_.begin()
<< "' was not used (possible typo?)";
}
else
{
os << "the following options where not used (possible typos?):";
for (auto opt: unused_)
os << "\n\t- '" << opt << '\'';
}
throw std::runtime_error(os.str());
}
}