summaryrefslogtreecommitdiff
path: root/src/option.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/option.h')
-rw-r--r--src/option.h294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/option.h b/src/option.h
new file mode 100644
index 00000000..b81c2ce7
--- /dev/null
+++ b/src/option.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2003-2009, 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;
+ value_t 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[name_len - 1] == '_') {
+ TRACE_CTOR(option_t, "const char *, const char");
+ DEBUG("option.names", "Option: " << name);
+ }
+ 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) {
+ if (wants_arg) {
+ out << desc() << " => ";
+ value.dump(out);
+ } else {
+ out << desc();
+ }
+ out << " <" << *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() {
+ assert(handled);
+ if (! value)
+ throw_(std::runtime_error, _("No argument provided for %1") << desc());
+ return value.as_string_lval();
+ }
+
+ string str() const {
+ assert(handled);
+ if (! value)
+ throw_(std::runtime_error, _("No argument provided for %1") << desc());
+ return value.as_string();
+ }
+
+ void on_only(const optional<string>& whence) {
+ handled = true;
+ source = whence;
+ }
+ void on(const optional<string>& whence, const string& str) {
+ on_with(whence, string_value(str));
+ }
+ virtual void on_with(const optional<string>& whence,
+ const value_t& val) {
+ handled = true;
+ value = val;
+ source = whence;
+ }
+
+ void off() {
+ handled = false;
+ value = value_t();
+ source = none;
+ }
+
+ virtual void handler_thunk(call_scope_t&) {}
+
+ virtual void handler(call_scope_t& args) {
+ if (wants_arg) {
+ if (args.size() < 2)
+ throw_(std::runtime_error, _("No argument provided for %1") << desc());
+ else if (args.size() > 2)
+ throw_(std::runtime_error, _("To many arguments provided for %1") << desc());
+ else if (! args[0].is_string())
+ throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
+ on_with(args[0].as_string(), args[1]);
+ }
+ else if (args.size() < 1) {
+ throw_(std::runtime_error, _("No argument provided for %1") << desc());
+ }
+ else if (! args[0].is_string()) {
+ throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
+ }
+ else {
+ on_only(args[0].as_string());
+ }
+
+ handler_thunk(args);
+ }
+
+ virtual value_t handler_wrapper(call_scope_t& args) {
+ handler(args);
+ return true;
+ }
+
+ virtual value_t operator()(call_scope_t& args) {
+ if (! args.empty()) {
+ args.push_front(string_value("?expr"));
+ return handler_wrapper(args);
+ }
+ else if (wants_arg) {
+ if (handled)
+ return value;
+ else
+ return NULL_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 DECL1(type, name, vartype, var, value) \
+ vartype var ; \
+ name ## _option_t() : option_t<type>(#name), var(value)
+
+#define DO() virtual void handler_thunk(call_scope_t&)
+#define DO_(var) virtual void handler_thunk(call_scope_t& 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_wrapper, 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)
+
+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