ledger/src/option.h
Alexis Hildebrandt 1dd9dcaab4 Bump copyright notice to 2015
The following script makes it a no-brainer:
% NEXT_YEAR=2015; ag -l 'Copyright.*Wiegley' \
  | xargs sed -i '' -e "s/\(Copyright.*\)-20[0-9]\{2\}/\1-${NEXT_YEAR}/"
2014-12-27 11:24:55 +01:00

301 lines
9.3 KiB
C++

/*
* Copyright (c) 2003-2015, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @addtogroup expr
*/
/**
* @file option.h
* @author John Wiegley
*
* @ingroup expr
*/
#ifndef _OPTION_H
#define _OPTION_H
#include "scope.h"
namespace ledger {
class call_scope_t;
template <typename T>
class option_t
{
protected:
const char * name;
string::size_type name_len;
const char ch;
bool handled;
optional<string> source;
option_t& operator=(const option_t&);
public:
T * parent;
string value;
bool wants_arg;
option_t(const char * _name, const char _ch = '\0')
: name(_name), name_len(std::strlen(name)), ch(_ch),
handled(false), parent(NULL), value(),
wants_arg(name_len > 0 ? name[name_len - 1] == '_' : false) {
DEBUG("option.names", "Option: " << name);
TRACE_CTOR(option_t, "const char *, const char");
}
option_t(const option_t& other)
: name(other.name),
name_len(other.name_len),
ch(other.ch),
handled(other.handled),
parent(NULL),
value(other.value),
wants_arg(other.wants_arg)
{
TRACE_CTOR(option_t, "copy");
}
virtual ~option_t() {
TRACE_DTOR(option_t);
}
void report(std::ostream& out) const {
if (handled && source) {
out.width(24);
out << std::right << desc();
if (wants_arg) {
out << " = ";
out.width(42);
out << std::left << value;
} else {
out.width(45);
out << ' ';
}
out << std::left << *source << std::endl;
}
}
string desc() const {
std::ostringstream out;
out << "--";
for (const char * p = name; *p; p++) {
if (*p == '_') {
if (*(p + 1))
out << '-';
} else {
out << *p;
}
}
if (ch)
out << " (-" << ch << ")";
return out.str();
}
operator bool() const {
return handled;
}
string str() const {
assert(handled);
if (value.empty())
throw_(std::runtime_error, _f("No argument provided for %1%") % desc());
return value;
}
void on(const char * whence) {
on(string(whence));
}
void on(const optional<string>& whence) {
handler_thunk(whence);
handled = true;
source = whence;
}
void on(const char * whence, const string& str) {
on(string(whence), str);
}
void on(const optional<string>& whence, const string& str) {
string before = value;
handler_thunk(whence, str);
if (value == before)
value = str;
handled = true;
source = whence;
}
void off() {
handled = false;
value = "";
source = none;
}
virtual void handler_thunk(const optional<string>&) {}
virtual void handler_thunk(const optional<string>&, const string&) {}
value_t handler(call_scope_t& args) {
if (wants_arg) {
if (args.size() < 2)
throw_(std::runtime_error, _f("No argument provided for %1%") % desc());
else if (args.size() > 2)
throw_(std::runtime_error, _f("To many arguments provided for %1%") % desc());
else if (! args[0].is_string())
throw_(std::runtime_error, _f("Context argument for %1% not a string") % desc());
on(args.get<string>(0), args.get<string>(1));
}
else if (args.size() < 1) {
throw_(std::runtime_error, _f("No argument provided for %1%") % desc());
}
else if (! args[0].is_string()) {
throw_(std::runtime_error, _f("Context argument for %1% not a string") % desc());
}
else {
on(args.get<string>(0));
}
return true;
}
virtual value_t operator()(call_scope_t& args) {
if (! args.empty()) {
args.push_front(string_value("?expr"));
return handler(args);
}
else if (wants_arg) {
return string_value(value);
}
else {
return handled;
}
}
};
#define BEGIN(type, name) \
struct name ## option_t : public option_t<type>
#define CTOR(type, name) \
name ## option_t() : option_t<type>(#name)
#define CTOR_(type, name, base) \
name ## option_t() : option_t<type>(#name), base
#define DECL1(type, name, vartype, var, value) \
vartype var ; \
name ## option_t() : option_t<type>(#name), var value
#define DO() virtual void handler_thunk(const optional<string>& whence)
#define DO_(var) virtual void handler_thunk(const optional<string>& whence, \
const string& var)
#define END(name) name ## handler
#define COPY_OPT(name, other) name ## handler(other.name ## handler)
#define MAKE_OPT_HANDLER(type, x) \
expr_t::op_t::wrap_functor(bind(&option_t<type>::handler, x, _1))
#define MAKE_OPT_FUNCTOR(type, x) \
expr_t::op_t::wrap_functor(bind(&option_t<type>::operator(), x, _1))
inline bool is_eq(const char * p, const char * n) {
// Test whether p matches n, substituting - in p for _ in n.
for (; *p && *n; p++, n++) {
if (! (*p == '-' && *n == '_') && *p != *n)
return false;
}
// Ignore any trailing underscore
return *p == *n || (! *p && *n == '_' && ! *(n + 1));
}
#define OPT(name) \
if (is_eq(p, #name)) \
return ((name ## handler).parent = this, &(name ## handler))
#define OPT_ALT(name, alt) \
if (is_eq(p, #name) || is_eq(p, #alt)) \
return ((name ## handler).parent = this, &(name ## handler))
#define OPT_(name) \
if (! *(p + 1) || \
((name ## handler).wants_arg && \
*(p + 1) == '_' && ! *(p + 2)) || \
is_eq(p, #name)) \
return ((name ## handler).parent = this, &(name ## handler))
#define OPT_CH(name) \
if (! *(p + 1) || \
((name ## handler).wants_arg && \
*(p + 1) == '_' && ! *(p + 2))) \
return ((name ## handler).parent = this, &(name ## handler))
#define HANDLER(name) name ## handler
#define HANDLED(name) HANDLER(name)
#define OPTION(type, name) \
BEGIN(type, name) \
{ \
CTOR(type, name) {} \
} \
END(name)
#define OPTION_(type, name, body) \
BEGIN(type, name) \
{ \
CTOR(type, name) {} \
body \
} \
END(name)
#define OPTION__(type, name, body) \
BEGIN(type, name) \
{ \
body \
} \
END(name)
#define OTHER(name) \
parent->HANDLER(name).parent = parent; \
parent->HANDLER(name)
bool process_option(const string& whence, const string& name, scope_t& scope,
const char * arg, const string& varname);
void process_environment(const char ** envp, const string& tag,
scope_t& scope);
strings_list process_arguments(strings_list args, scope_t& scope);
DECLARE_EXCEPTION(option_error, std::runtime_error);
} // namespace ledger
#endif // _OPTION_H