/* * Copyright (c) 2003-2023, 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 * * @brief Basic type and macros for handling command-line options */ #pragma once #include "scope.h" namespace ledger { using namespace boost::placeholders; class call_scope_t; template class option_t { protected: const char * name; string::size_type name_len; const char ch; bool handled; optional 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& whence) { handler_thunk(whence); handled = true; source = whence; } void on(const char * whence, const string& str) { on(string(whence), str); } void on(const optional& 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&) {} virtual void handler_thunk(const optional&, 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("Too 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(0), args.get(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(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 #define CTOR(type, name) \ name ## option_t() : option_t(#name) #define CTOR_(type, name, base) \ name ## option_t() : option_t(#name), base #define DECL1(type, name, vartype, var, value) \ vartype var ; \ name ## option_t() : option_t(#name), var value #define DO() virtual void handler_thunk(const optional& whence) #define DO_(var) virtual void handler_thunk(const optional& 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::handler, x, _1)) #define MAKE_OPT_FUNCTOR(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t::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