<commit_before>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "exception.h"

#include <cstdarg>            // for va_end, va_list, va_start
#include <cstdio>             // for vsnprintf
#include <cstdlib>            // for free
#include <cstring>            // for strtok_r
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <execinfo.h>         // for backtrace, backtrace_symbols

#define BUFFER_SIZE 1024

std::string
traceback(const std::string& filename, int line, void *const * callstack, int frames)
{
	std::string tb = "\n== Traceback at (" + filename + ":" + std::to_string(line) + ")";

	if (frames == 0) {
		return tb;
	}

	tb.push_back(':');

	// resolve addresses into strings containing "filename(function+address)"
	char** strs = backtrace_symbols(callstack, frames);

	// iterate over the returned symbol lines. skip the first, it is the
	// address of this function.
	for (int i = 1; i < frames; ++i) {
		int status = 0;
		const char *sep = "\t ()+";
		char *mangled, *lasts;
		std::string result;
		for (mangled = strtok_r(strs[i], sep, &lasts); mangled; mangled = strtok_r(nullptr, sep, &lasts)) {
			char* unmangled = abi::__cxa_demangle(mangled, nullptr, 0, &status);
			if (!result.empty()) {
				result += " ";
			}
			if (status == 0) {
				result += unmangled;
			} else {
				result += mangled;
			}
			free(unmangled);
		}
		tb += "\n    " + result;
	}

	free(strs);
	return tb;
}

std::string
traceback(const std::string& filename, int line)
{
	void* callstack[128];

	// retrieve current stack addresses
	int frames = backtrace(callstack, sizeof(callstack) / sizeof(void*));

	auto tb = traceback(filename, line, callstack, frames);
	if (frames == 0) {
		tb.append(":\n    <empty, possibly corrupt>");
	}

	return tb;
}

BaseException::BaseException()
	: line{0},
	  frames{0},
	  callstack{{}}
{ }

BaseException::BaseException(const BaseException& exc)
	: type{exc.type},
	  message{exc.message},
	  context{exc.context},
	  traceback{exc.traceback},
	  filename{exc.filename},
	  line{exc.line},
	  frames{exc.frames}
{
	memcpy(callstack, exc.callstack, frames * sizeof(void*));
}

BaseException::BaseException(BaseException&& exc)
	: type{std::move(exc.type)},
	  message{std::move(exc.message)},
	  context{std::move(exc.context)},
	  traceback{std::move(exc.traceback)},
	  filename{std::move(exc.filename)},
	  line{std::move(exc.line)},
	  callstack{std::move(exc.callstack)},
	  frames{std::move(exc.frames)}
{ }

BaseException::BaseException(const BaseException* exc)
	: BaseException(exc ? *exc : BaseException())
{ }

BaseException::BaseException(const char *filename, int line, const char* type, const char *format, ...)
	: type(type),
	  filename(filename),
	  line(line),
	  frames(0)
{
	va_list argptr;
	va_start(argptr, format);

	// Figure out the length of the formatted message.
	va_list argptr_copy;
	va_copy(argptr_copy, argptr);
	auto len = vsnprintf(nullptr, 0, format, argptr_copy);
	va_end(argptr_copy);

	// Make a string to hold the formatted message.
	message.resize(len + 1);
	message.resize(vsnprintf(&message[0], len + 1, format, argptr));

	va_end(argptr);

#ifdef XAPIAND_TRACEBACKS
	frames = backtrace(callstack, sizeof(callstack) / sizeof(void*));
#endif
}

const char*
BaseException::get_message() const
{
	if (message.empty()) {
		message.assign(type);
	}
	return message.c_str();
}

const char*
BaseException::get_context() const
{
	if (context.empty()) {
		context.assign(filename + ":" + std::to_string(line) + ": " + get_message());
	}
	return context.c_str();
}

const char*
BaseException::get_traceback() const
{
	if (traceback.empty()) {
		traceback = ::traceback(filename, line, callstack, frames);
	}
	return traceback.c_str();
}
<commit_msg>Exception: Reordered initialization<commit_after>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "exception.h"

#include <cstdarg>            // for va_end, va_list, va_start
#include <cstdio>             // for vsnprintf
#include <cstdlib>            // for free
#include <cstring>            // for strtok_r
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <execinfo.h>         // for backtrace, backtrace_symbols

#define BUFFER_SIZE 1024

std::string
traceback(const std::string& filename, int line, void *const * callstack, int frames)
{
	std::string tb = "\n== Traceback at (" + filename + ":" + std::to_string(line) + ")";

	if (frames == 0) {
		return tb;
	}

	tb.push_back(':');

	// resolve addresses into strings containing "filename(function+address)"
	char** strs = backtrace_symbols(callstack, frames);

	// iterate over the returned symbol lines. skip the first, it is the
	// address of this function.
	for (int i = 1; i < frames; ++i) {
		int status = 0;
		const char *sep = "\t ()+";
		char *mangled, *lasts;
		std::string result;
		for (mangled = strtok_r(strs[i], sep, &lasts); mangled; mangled = strtok_r(nullptr, sep, &lasts)) {
			char* unmangled = abi::__cxa_demangle(mangled, nullptr, 0, &status);
			if (!result.empty()) {
				result += " ";
			}
			if (status == 0) {
				result += unmangled;
			} else {
				result += mangled;
			}
			free(unmangled);
		}
		tb += "\n    " + result;
	}

	free(strs);
	return tb;
}

std::string
traceback(const std::string& filename, int line)
{
	void* callstack[128];

	// retrieve current stack addresses
	int frames = backtrace(callstack, sizeof(callstack) / sizeof(void*));

	auto tb = traceback(filename, line, callstack, frames);
	if (frames == 0) {
		tb.append(":\n    <empty, possibly corrupt>");
	}

	return tb;
}

BaseException::BaseException()
	: line{0},
	  callstack{{}},
	  frames{0}
{ }

BaseException::BaseException(const BaseException& exc)
	: type{exc.type},
	  filename{exc.filename},
	  line{exc.line},
	  frames{exc.frames},
	  message{exc.message},
	  context{exc.context},
	  traceback{exc.traceback}
{
	memcpy(callstack, exc.callstack, frames * sizeof(void*));
}

BaseException::BaseException(BaseException&& exc)
	: type{std::move(exc.type)},
	  filename{std::move(exc.filename)},
	  line{std::move(exc.line)},
	  callstack{std::move(exc.callstack)},
	  frames{std::move(exc.frames)},
	  message{std::move(exc.message)},
	  context{std::move(exc.context)},
	  traceback{std::move(exc.traceback)}
{ }

BaseException::BaseException(const BaseException* exc)
	: BaseException(exc ? *exc : BaseException())
{ }

BaseException::BaseException(const char *filename, int line, const char* type, const char *format, ...)
	: type(type),
	  filename(filename),
	  line(line),
	  frames(0)
{
	va_list argptr;
	va_start(argptr, format);

	// Figure out the length of the formatted message.
	va_list argptr_copy;
	va_copy(argptr_copy, argptr);
	auto len = vsnprintf(nullptr, 0, format, argptr_copy);
	va_end(argptr_copy);

	// Make a string to hold the formatted message.
	message.resize(len + 1);
	message.resize(vsnprintf(&message[0], len + 1, format, argptr));

	va_end(argptr);

#ifdef XAPIAND_TRACEBACKS
	frames = backtrace(callstack, sizeof(callstack) / sizeof(void*));
#endif
}

const char*
BaseException::get_message() const
{
	if (message.empty()) {
		message.assign(type);
	}
	return message.c_str();
}

const char*
BaseException::get_context() const
{
	if (context.empty()) {
		context.assign(filename + ":" + std::to_string(line) + ": " + get_message());
	}
	return context.c_str();
}

const char*
BaseException::get_traceback() const
{
	if (traceback.empty()) {
		traceback = ::traceback(filename, line, callstack, frames);
	}
	return traceback.c_str();
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>

#include "expenses.hpp"
#include "args.hpp"
#include "accounts.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "writer.hpp"
#include "budget_exception.hpp"

using namespace budget;

namespace {

static data_handler<expense> expenses { "expenses", "expenses.data" };

void show_templates(){
    std::vector<std::string> columns = {"ID", "Account", "Name", "Amount"};
    std::vector<std::vector<std::string>> contents;

    size_t count = 0;

    for(auto& expense : expenses.data()){
        if(expense.date == TEMPLATE_DATE){
            contents.push_back({to_string(expense.id), get_account(expense.account).name, expense.name, to_string(expense.amount)});
            ++count;
        }
    }

    if(count == 0){
        std::cout << "No templates" << std::endl;
    } else {
        console_writer w(std::cout);
        w.display_table(columns, contents);
    }
}

} //end of anonymous namespace

std::map<std::string, std::string> budget::expense::get_params() const {
    std::map<std::string, std::string> params;

    params["input_id"]      = budget::to_string(id);
    params["input_guid"]    = guid;
    params["input_date"]    = budget::to_string(date);
    params["input_name"]    = name;
    params["input_account"] = budget::to_string(account);
    params["input_amount"]  = budget::to_string(amount);

    return params;
}

void budget::expenses_module::load(){
    load_expenses();
    load_accounts();
}

void budget::expenses_module::unload(){
    save_expenses();
}

void budget::expenses_module::handle(const std::vector<std::string>& args){
    console_writer w(std::cout);

    if(args.size() == 1){
        show_expenses(w);
    } else {
        auto& subcommand = args[1];

        if(subcommand == "show"){
            if(args.size() == 2){
                show_expenses(w);
            } else if(args.size() == 3){
                show_expenses(budget::month(to_number<unsigned short>(args[2])), w);
            } else if(args.size() == 4){
                show_expenses(
                    budget::month(to_number<unsigned short>(args[2])),
                    budget::year(to_number<unsigned short>(args[3])), w);
            } else {
                throw budget_exception("Too many arguments to expense show");
            }
        } else if(subcommand == "all"){
            show_all_expenses(w);
        } else if(subcommand == "template"){
            show_templates();
        } else if(subcommand == "add"){
            if(args.size() > 2){
                std::string template_name;
                std::string space;

                for(size_t i = 2; i < args.size(); ++i){
                    template_name += space;
                    template_name += args[i];
                    space = " ";
                }

                bool template_found = false;

                for(auto& template_expense : all_expenses()){
                    if(template_expense.date == TEMPLATE_DATE && template_expense.name == template_name){
                        expense expense;
                        expense.guid = generate_guid();
                        expense.date = budget::local_day();
                        expense.name = template_expense.name;
                        expense.amount = template_expense.amount;
                        expense.account = template_expense.account;

                        auto id = expenses.add(std::move(expense));
                        std::cout << "Expense " << id << " has been created" << std::endl;

                        template_found = true;
                        break;
                    }
                }

                if(!template_found){
                    std::cout << "Template \"" << template_name << "\" not found, creating a new template" << std::endl;

                    expense expense;
                    expense.guid = generate_guid();
                    expense.date = TEMPLATE_DATE;
                    expense.name = template_name;

                    std::string account_name;
                    edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker());
                    expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

                    edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

                    auto id = expenses.add(std::move(expense));
                    std::cout << "Template " << id << " has been created" << std::endl;
                }
            } else {
                expense expense;
                expense.guid = generate_guid();
                expense.date = budget::local_day();

                std::string account_name;

                if (config_contains("default_account")) {
                    auto default_account = config_value("default_account");

                    if (account_exists(default_account)) {
                        account_name = default_account;
                    } else {
                        std::cerr << "error: The default account set in the configuration does not exist, ignoring it" << std::endl;
                    }
                }

                edit_date(expense.date, "Date");

                edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker(expense.date));
                expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

                edit_string(expense.name, "Name", not_empty_checker());
                edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

                auto id = expenses.add(std::move(expense));
                std::cout << "Expense " << id << " has been created" << std::endl;
            }
        } else if(subcommand == "delete"){
            enough_args(args, 3);

            size_t id = to_number<size_t>(args[2]);

            if (expenses.remove(id)) {
                std::cout << "Expense " << id << " has been deleted" << std::endl;
            } else {
                throw budget_exception("There are no expense with id ");
            }
        } else if(subcommand == "edit"){
            enough_args(args, 3);

            size_t id = to_number<size_t>(args[2]);

            auto expense = expenses[id];

            edit_date(expense.date, "Date");

            auto account_name = get_account(expense.account).name;
            edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker(expense.date));
            expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

            edit_string(expense.name, "Name", not_empty_checker());
            edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

            if (expenses.indirect_edit(expense)) {
                std::cout << "Expense " << id << " has been modified" << std::endl;
            }
        } else if (subcommand == "search") {
            std::string search;
            edit_string(search, "Search", not_empty_checker());

            search_expenses(search, w);
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::load_expenses(){
    expenses.load();
}

void budget::save_expenses(){
    expenses.save();
}

void budget::add_expense(budget::expense&& expense){
    expenses.add(std::forward<budget::expense>(expense));
}

bool budget::edit_expense(const expense& expense){
    return expenses.indirect_edit(expense);
}

std::ostream& budget::operator<<(std::ostream& stream, const expense& expense){
    return stream << expense.id  << ':' << expense.guid << ':' << expense.account << ':' << expense.name << ':' << expense.amount << ':' << to_string(expense.date);
}

void budget::operator>>(const std::vector<std::string>& parts, expense& expense){
    bool random = config_contains("random");

    expense.id = to_number<size_t>(parts.at(0));
    expense.guid = parts.at(1);
    expense.account = to_number<size_t>(parts.at(2));
    expense.name = parts.at(3);
    expense.date = from_string(parts.at(5));

    if(random){
        expense.amount = budget::random_money(10, 1500);
    } else {
        expense.amount = parse_money(parts.at(4));
    }
}

std::vector<expense> budget::all_expenses(){
    return expenses.data();
}

bool budget::indirect_edit_expense(const expense & expense, bool propagate) {
    return expenses.indirect_edit(expense, propagate);
}

void budget::set_expenses_changed(){
    expenses.set_changed();
}

void budget::show_all_expenses(budget::writer& w){
    w << title_begin << "All Expenses " << add_button("expenses") << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    for(auto& expense : expenses.data()){
        contents.push_back({to_string(expense.id), to_string(expense.date), get_account(expense.account).name,
            expense.name, to_string(expense.amount), "::edit::expenses::" + to_string(expense.id)});
    }

    w.display_table(columns, contents);
}

void budget::search_expenses(const std::string& search, budget::writer& w){
    w << title_begin << "Results" << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    money total;
    size_t count = 0;

    auto l_search = search;
    std::transform(l_search.begin(), l_search.end(), l_search.begin(), ::tolower);

    for(auto& expense : expenses.data()){
        auto l_name = expense.name;
        std::transform(l_name.begin(), l_name.end(), l_name.begin(), ::tolower);

        if(l_name.find(l_search) != std::string::npos){
            contents.push_back({to_string(expense.id), to_string(expense.date), get_account(expense.account).name, expense.name, to_string(expense.amount), "::edit::expenses::" + to_string(expense.id)});

            total += expense.amount;
            ++count;
        }
    }

    if(count == 0){
        w << "No expenses found" << end_of_line;
    } else {
        contents.push_back({"", "", "", "Total", to_string(total), ""});

        w.display_table(columns, contents, 1, {}, 0, 1);
    }
}

void budget::show_expenses(budget::month month, budget::year year, budget::writer& w){
    w << title_begin << "Expenses of " << month << " " << year << " "
      << add_button("expenses")
      << budget::year_month_selector{"expenses", year, month} << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    money total;
    size_t count = 0;

    for(auto& expense : expenses.data()){
        if(expense.date.year() == year && expense.date.month() == month){
            contents.push_back({to_string(expense.id), to_string(expense.date), get_account(expense.account).name, expense.name, to_string(expense.amount), "::edit::expenses::" + to_string(expense.id)});

            total += expense.amount;
            ++count;
        }
    }

    if(count == 0){
        w << "No expenses for " << month << "-" << year << end_of_line;
    } else {
        contents.push_back({"", "", "", "Total", to_string(total), ""});

        w.display_table(columns, contents, 1, {}, 0, 1);
    }
}

void budget::show_expenses(budget::month month, budget::writer& w){
    auto today = budget::local_day();

    show_expenses(month, today.year(), w);
}

void budget::show_expenses(budget::writer& w){
    auto today = budget::local_day();

    show_expenses(today.month(), today.year(), w);
}

bool budget::expense_exists(size_t id){
    return expenses.exists(id);
}

void budget::expense_delete(size_t id) {
    if (!expenses.exists(id)) {
        throw budget_exception("There are no expense with id ");
    }

    expenses.remove(id);
}

expense budget::expense_get(size_t id) {
    return expenses[id];
}

filter_view<expense> budget::all_expenses_year(data_cache & cache, budget::year year) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year;
    });
}

filter_view<expense> budget::all_expenses_month(data_cache & cache, budget::year year, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year && e.date.month() == month;
    });
}

filter_view<expense> budget::all_expenses_month(data_cache & cache, size_t account_id, budget::year year, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.account == account_id && e.date.year() == year && e.date.month() == month;
    });
}

filter_view<expense> budget::all_expenses_between(data_cache & cache, budget::year year, budget::month sm, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year && e.date.month() >= sm && e.date.month() <= month;
    });
}

filter_view<expense> budget::all_expenses_between(data_cache & cache, size_t account_id, budget::year year, budget::month sm, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.account == account_id && e.date.year() == year && e.date.month() >= sm && e.date.month() <= month;
    });
}
<commit_msg>Some reformating<commit_after>//=======================================================================
// Copyright (c) 2013-2020 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>

#include "expenses.hpp"
#include "args.hpp"
#include "accounts.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "writer.hpp"
#include "budget_exception.hpp"

using namespace budget;

namespace {

static data_handler<expense> expenses { "expenses", "expenses.data" };

void show_templates() {
    std::vector<std::string>              columns = {"ID", "Account", "Name", "Amount"};
    std::vector<std::vector<std::string>> contents;

    size_t count = 0;

    for (auto& expense : expenses.data()) {
        if (expense.date == TEMPLATE_DATE) {
            contents.push_back({to_string(expense.id), get_account(expense.account).name, expense.name, to_string(expense.amount)});
            ++count;
        }
    }

    if (count == 0) {
        std::cout << "No templates" << std::endl;
    } else {
        console_writer w(std::cout);
        w.display_table(columns, contents);
    }
}

} //end of anonymous namespace

std::map<std::string, std::string> budget::expense::get_params() const {
    std::map<std::string, std::string> params;

    params["input_id"]      = budget::to_string(id);
    params["input_guid"]    = guid;
    params["input_date"]    = budget::to_string(date);
    params["input_name"]    = name;
    params["input_account"] = budget::to_string(account);
    params["input_amount"]  = budget::to_string(amount);

    return params;
}

void budget::expenses_module::load(){
    load_expenses();
    load_accounts();
}

void budget::expenses_module::unload(){
    save_expenses();
}

void budget::expenses_module::handle(const std::vector<std::string>& args){
    console_writer w(std::cout);

    if(args.size() == 1){
        show_expenses(w);
    } else {
        auto& subcommand = args[1];

        if(subcommand == "show"){
            if(args.size() == 2){
                show_expenses(w);
            } else if(args.size() == 3){
                show_expenses(budget::month(to_number<unsigned short>(args[2])), w);
            } else if(args.size() == 4){
                show_expenses(
                    budget::month(to_number<unsigned short>(args[2])),
                    budget::year(to_number<unsigned short>(args[3])), w);
            } else {
                throw budget_exception("Too many arguments to expense show");
            }
        } else if(subcommand == "all"){
            show_all_expenses(w);
        } else if(subcommand == "template"){
            show_templates();
        } else if(subcommand == "add"){
            if(args.size() > 2){
                std::string template_name;
                std::string space;

                for(size_t i = 2; i < args.size(); ++i){
                    template_name += space;
                    template_name += args[i];
                    space = " ";
                }

                bool template_found = false;

                for (auto& template_expense : all_expenses()) {
                    if (template_expense.date == TEMPLATE_DATE && template_expense.name == template_name) {
                        expense expense;
                        expense.guid    = generate_guid();
                        expense.date    = budget::local_day();
                        expense.name    = template_expense.name;
                        expense.amount  = template_expense.amount;
                        expense.account = template_expense.account;

                        auto id = expenses.add(std::move(expense));
                        std::cout << "Expense " << id << " has been created" << std::endl;

                        template_found = true;
                        break;
                    }
                }

                if (!template_found) {
                    std::cout << "Template \"" << template_name << "\" not found, creating a new template" << std::endl;

                    expense expense;
                    expense.guid = generate_guid();
                    expense.date = TEMPLATE_DATE;
                    expense.name = template_name;

                    std::string account_name;
                    edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker());
                    expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

                    edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

                    auto id = expenses.add(std::move(expense));
                    std::cout << "Template " << id << " has been created" << std::endl;
                }
            } else {
                expense expense;
                expense.guid = generate_guid();
                expense.date = budget::local_day();

                std::string account_name;

                if (config_contains("default_account")) {
                    auto default_account = config_value("default_account");

                    if (account_exists(default_account)) {
                        account_name = default_account;
                    } else {
                        std::cerr << "error: The default account set in the configuration does not exist, ignoring it" << std::endl;
                    }
                }

                edit_date(expense.date, "Date");

                edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker(expense.date));
                expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

                edit_string(expense.name, "Name", not_empty_checker());
                edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

                auto id = expenses.add(std::move(expense));
                std::cout << "Expense " << id << " has been created" << std::endl;
            }
        } else if(subcommand == "delete"){
            enough_args(args, 3);

            size_t id = to_number<size_t>(args[2]);

            if (expenses.remove(id)) {
                std::cout << "Expense " << id << " has been deleted" << std::endl;
            } else {
                throw budget_exception("There are no expense with id ");
            }
        } else if(subcommand == "edit"){
            enough_args(args, 3);

            size_t id = to_number<size_t>(args[2]);

            auto expense = expenses[id];

            edit_date(expense.date, "Date");

            auto account_name = get_account(expense.account).name;
            edit_string_complete(account_name, "Account", all_account_names(), not_empty_checker(), account_checker(expense.date));
            expense.account = get_account(account_name, expense.date.year(), expense.date.month()).id;

            edit_string(expense.name, "Name", not_empty_checker());
            edit_money(expense.amount, "Amount", not_negative_checker(), not_zero_checker());

            if (expenses.indirect_edit(expense)) {
                std::cout << "Expense " << id << " has been modified" << std::endl;
            }
        } else if (subcommand == "search") {
            std::string search;
            edit_string(search, "Search", not_empty_checker());

            search_expenses(search, w);
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::load_expenses(){
    expenses.load();
}

void budget::save_expenses(){
    expenses.save();
}

void budget::add_expense(budget::expense&& expense){
    expenses.add(std::forward<budget::expense>(expense));
}

bool budget::edit_expense(const expense& expense){
    return expenses.indirect_edit(expense);
}

std::ostream& budget::operator<<(std::ostream& stream, const expense& expense){
    return stream << expense.id  << ':' << expense.guid << ':' << expense.account << ':' << expense.name << ':' << expense.amount << ':' << to_string(expense.date);
}

void budget::operator>>(const std::vector<std::string>& parts, expense& expense){
    bool random = config_contains("random");

    expense.id      = to_number<size_t>(parts.at(0));
    expense.guid    = parts.at(1);
    expense.account = to_number<size_t>(parts.at(2));
    expense.name    = parts.at(3);
    expense.date    = from_string(parts.at(5));

    if (random) {
        expense.amount = budget::random_money(10, 1500);
    } else {
        expense.amount = parse_money(parts.at(4));
    }
}

std::vector<expense> budget::all_expenses(){
    return expenses.data();
}

bool budget::indirect_edit_expense(const expense & expense, bool propagate) {
    return expenses.indirect_edit(expense, propagate);
}

void budget::set_expenses_changed(){
    expenses.set_changed();
}

void budget::show_all_expenses(budget::writer& w){
    w << title_begin << "All Expenses " << add_button("expenses") << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    for (auto& expense : expenses.data()) {
        contents.push_back({to_string(expense.id),
                            to_string(expense.date),
                            get_account(expense.account).name,
                            expense.name,
                            to_string(expense.amount),
                            "::edit::expenses::" + to_string(expense.id)});
    }

    w.display_table(columns, contents);
}

void budget::search_expenses(const std::string& search, budget::writer& w){
    w << title_begin << "Results" << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    money total;
    size_t count = 0;

    auto l_search = search;
    std::transform(l_search.begin(), l_search.end(), l_search.begin(), ::tolower);

    for (auto& expense : expenses.data()) {
        auto l_name = expense.name;
        std::transform(l_name.begin(), l_name.end(), l_name.begin(), ::tolower);

        if (l_name.find(l_search) != std::string::npos) {
            contents.push_back({to_string(expense.id),
                                to_string(expense.date),
                                get_account(expense.account).name,
                                expense.name,
                                to_string(expense.amount),
                                "::edit::expenses::" + to_string(expense.id)});

            total += expense.amount;
            ++count;
        }
    }

    if(count == 0){
        w << "No expenses found" << end_of_line;
    } else {
        contents.push_back({"", "", "", "Total", to_string(total), ""});

        w.display_table(columns, contents, 1, {}, 0, 1);
    }
}

void budget::show_expenses(budget::month month, budget::year year, budget::writer& w){
    w << title_begin << "Expenses of " << month << " " << year << " "
      << add_button("expenses")
      << budget::year_month_selector{"expenses", year, month} << title_end;

    std::vector<std::string> columns = {"ID", "Date", "Account", "Name", "Amount", "Edit"};
    std::vector<std::vector<std::string>> contents;

    money total;
    size_t count = 0;

    for (auto& expense : expenses.data()) {
        if (expense.date.year() == year && expense.date.month() == month) {
            contents.push_back({to_string(expense.id),
                                to_string(expense.date),
                                get_account(expense.account).name,
                                expense.name,
                                to_string(expense.amount),
                                "::edit::expenses::" + to_string(expense.id)});

            total += expense.amount;
            ++count;
        }
    }

    if(count == 0){
        w << "No expenses for " << month << "-" << year << end_of_line;
    } else {
        contents.push_back({"", "", "", "Total", to_string(total), ""});

        w.display_table(columns, contents, 1, {}, 0, 1);
    }
}

void budget::show_expenses(budget::month month, budget::writer& w){
    auto today = budget::local_day();

    show_expenses(month, today.year(), w);
}

void budget::show_expenses(budget::writer& w){
    auto today = budget::local_day();

    show_expenses(today.month(), today.year(), w);
}

bool budget::expense_exists(size_t id){
    return expenses.exists(id);
}

void budget::expense_delete(size_t id) {
    if (!expenses.exists(id)) {
        throw budget_exception("There are no expense with id ");
    }

    expenses.remove(id);
}

expense budget::expense_get(size_t id) {
    return expenses[id];
}

filter_view<expense> budget::all_expenses_year(data_cache & cache, budget::year year) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year;
    });
}

filter_view<expense> budget::all_expenses_month(data_cache & cache, budget::year year, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year && e.date.month() == month;
    });
}

filter_view<expense> budget::all_expenses_month(data_cache & cache, size_t account_id, budget::year year, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.account == account_id && e.date.year() == year && e.date.month() == month;
    });
}

filter_view<expense> budget::all_expenses_between(data_cache & cache, budget::year year, budget::month sm, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.date.year() == year && e.date.month() >= sm && e.date.month() <= month;
    });
}

filter_view<expense> budget::all_expenses_between(data_cache & cache, size_t account_id, budget::year year, budget::month sm, budget::month month) {
    return make_filter_view(cache.expenses(), [=](const expense& e) {
        return e.account == account_id && e.date.year() == year && e.date.month() >= sm && e.date.month() <= month;
    });
}
<|endoftext|>


<commit_before>/**
 * Fass.cc
 *
 *      Author: Sara Vallero
 *      Author: Valentina Zaccolo
 */
#include "Fass.h"
#include "FassLog.h"
#include "Configurator.h"
#include "InitShares.h"
#include "FassDb.h"
//#include "Log.h"
//#include "RPCManager.h"
//#include "PriorityManager.h"

#include <fstream>
#include <signal.h>
#include <sstream>
#include <stdexcept>
#include <unistd.h>
//#include <stdlib.h>
//#include <libxml/parser.h>

//#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <pthread.h>
#include <iostream>

using namespace std;

void Fass::start(bool bootstrap_only)
{

    bool             rc;
    //int             fd;
    sigset_t        mask;
    int             signal;
    char            hn[80];

    /// returns the null-terminated hostname in the character
    /// array hn, which has a length of 79 bytes.
    /// is hostname used somewhere?
    if ( gethostname(hn,79) != 0 )
    {
        throw runtime_error("Error getting hostname");
    }

    hostname = hn;

    /** Configuration system */
    fass_configuration = new FassConfigurator(etc_location, var_location);

    rc = fass_configuration->load_configuration();

    if ( !rc )
    {
        throw runtime_error("Could not load Fass configuration file.");
    }

    /** Initial shares system */
    //initial_shares = new FassInitShares(etc_location, var_location);

    //rc = initial_shares->load_shares();

    //if ( !rc )
    //{
    //    throw runtime_error("Could not load initial shares file.");
    //}

    /** Log system */

    ostringstream os;

    try{
        Log::MessageType   clevel;

        clevel     = get_debug_level();

        /// Initializing FASS daemon log system
        string log_fname;
        log_fname = log_location + "fass.log";
        FassLog::init_log_system(clevel,
                           log_fname.c_str(),
                           ios_base::trunc,
                           "fassd");

        os << "Starting " << version() << endl;
        os << "----------------------------------------\n";
        os << "     Fass Configuration File      \n";
        os << "----------------------------------------\n";
        os << fass_configuration->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS",Log::INFO,os);
        fass_configuration->print_loaded_options();
    } catch(runtime_error&){

        throw;
    }

    /** Initialize the XML library */
    // Try to avoid it
    //    xmlInitParser();

    /** Database */

    // instance of specific DB (can be changed)
    string dbtype;
    string dbendpoint;
    string dbname;
    int dbport;
    fass_configuration->get_single_option("database", "type", dbtype);
    fass_configuration->get_single_option("database", "endpoint", dbendpoint);
    fass_configuration->get_single_option("database", "port", dbport);
    fass_configuration->get_single_option("database", "name", dbname);
    if ( dbtype == "influxdb" ){ // TODO: should be done with a switch and enum, not critical
       database = new InfluxDb(dbendpoint, dbport, dbname);
    } else {
       FassLog::log("FASS", Log::ERROR, "Unknown database type!");
       throw;
    }

    // FOR VALE: database should be passed as argument to the Priority Manager

    /** Block all signals before creating any thread */

    sigfillset(&mask);

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    /** Managers */
    // some config variables are used both by the RPCManager and the XMLRPCClient (called by the PM)
    // we use the same valuse for both server/client, these should be consistent with the OpenNebula ones
    int  message_size;
    int  timeout;
    // OpenNebula xml-rpc endpoint
    string one_endpoint;
    string one_port;
    fass_configuration->get_single_option("fass", "one_endpoint", one_endpoint);
    fass_configuration->get_single_option("fass", "one_port", one_port);
    one_endpoint.append(":");
    one_endpoint.append(one_port);
    one_endpoint.append("/RPC2");
    //FassLog::log("FASS", Log::DEBUG, one_endpoint);
    // OpenNebula authentication
    string one_secret;
    fass_configuration->get_single_option("fass", "one_secret", one_secret);
    // xml-rpc config
    fass_configuration->get_single_option("rpcm", "message_size", message_size);
    fass_configuration->get_single_option("rpcm", "timeout", timeout);

   /// ---- Priority Manager ----
   try
   {
    int  manager_timer;
    int machines_limit;

    fass_configuration->get_single_option("pm", "max_vm", machines_limit);
    fass_configuration->get_single_option("pm", "manager_timer", manager_timer);

    // Read initial shares from separate file
    //initial_shares = new FassConfigurator(etc_location, var_location);

    //rc = initial_shares->load_configuration();

    //if ( !rc )
    //{
    //    throw runtime_error("Could not load Initial Shares file.");
    //}

    //std::vector<user> users;
    //std::list<users> list_of_users;

// TODO add the shares vector

    //initial_shares->get_single_option("users", "user", user);

    //list_of_users::iterator list_it;

    //for (list_it = users.begin(); list_it != users.end(); ++list_it)
    //{
    //    list_of_users.insert(list_it,users);
    //}

    //pm = new PriorityManager(one_endpoint, one_secret, message_size, timeout, machines_limit, manager_timer, list_of_users);
    pm = new PriorityManager(one_endpoint, one_secret, message_size, timeout, machines_limit, manager_timer);

    }

    catch (bad_alloc&)
    {
        FassLog::log("FASS", Log::ERROR, "Error creating Priority Manager");
        throw;
    }

    /// ---- Start the Priority Manager ----

    rc = pm->start();

    if ( !rc )
    {
       throw runtime_error("Could not start the Priority Manager");
    }

    /// ---- Request Manager ----
    try
    {

        //int  rm_port = 0;
        string rm_port = "";
	int  max_conn;
        int  max_conn_backlog;
        int  keepalive_timeout;
        int  keepalive_max_conn;
        bool rpc_log;
        string log_call_format;
        string rpc_filename = "";
        string rm_listen_address; //= "0.0.0.0";

        fass_configuration->get_single_option("rpcm", "listen_port", rm_port);
        fass_configuration->get_single_option("rpcm", "listen_address", rm_listen_address);
        fass_configuration->get_single_option("rpcm", "max_conn", max_conn);
        fass_configuration->get_single_option("rpcm", "max_conn_backlog", max_conn_backlog);
        fass_configuration->get_single_option("rpcm", "keepalive_timeout", keepalive_timeout);
        fass_configuration->get_single_option("rpcm", "keepalive_max_conn", keepalive_max_conn);
        fass_configuration->get_single_option("rpcm", "rpc_log", rpc_log);
        fass_configuration->get_single_option("rpcm", "log_call_format", log_call_format);

        if (rpc_log)
        {
            rpc_filename = log_location + "fass_xmlrpc.log";
        }

        rpcm = new RPCManager(one_endpoint,rm_port, max_conn, max_conn_backlog,
            keepalive_timeout, keepalive_max_conn, timeout, rpc_filename,
            log_call_format, rm_listen_address, message_size);

    }

    catch (bad_alloc&)
    {
        FassLog::log("FASS", Log::ERROR, "Error creating RPC Manager");
        throw;
    }

    /// ---- Start the Request Manager ----

    rc = rpcm->start();

    if ( !rc )
    {
       throw runtime_error("Could not start the RPC Manager");
    }

    /** Wait for a SIGTERM or SIGINT signal */

    sigemptyset(&mask);

    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);

    sigwait(&mask, &signal);

    /** Stop the managers and free resources */

    pm->finalize();

    //sleep to wait drivers???

    pthread_join(rpcm->get_thread_id(),0);
    pthread_join(pm->get_thread_id(),0);

    //XML Library
//    xmlCleanupParser();

    FassLog::log("FASS", Log::INFO, "All modules finalized, exiting.\n");

    return;

//error_mad:
//    Log::log("FASS", Log::ERROR, "Could not load driver");
//    throw runtime_error("Could not load a Fass driver");
}

Log::MessageType Fass::get_debug_level() const
{
    Log::MessageType clevel = Log::ERROR;

    int              log_level_int ;

    fass_configuration->get_single_option("fass", "log_level", log_level_int);

    if ( log_level_int != 0 )
    {

        if ( Log::ERROR <= log_level_int && log_level_int <= Log::DDDEBUG )
        {
            clevel = static_cast<Log::MessageType>(log_level_int);
        }
    }

    return clevel;
}
<commit_msg>Update Fass.cc<commit_after>/**
 * Copyright © 2017 INFN Torino - INDIGO-DataCloud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Fass.h"
#include "FassLog.h"
#include "Configurator.h"
#include "InitShares.h"
#include "FassDb.h"
//#include "Log.h"
//#include "RPCManager.h"
//#include "PriorityManager.h"

#include <fstream>
#include <signal.h>
#include <sstream>
#include <stdexcept>
#include <unistd.h>
//#include <stdlib.h>
//#include <libxml/parser.h>

//#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <pthread.h>
#include <iostream>

using namespace std;

void Fass::start(bool bootstrap_only)
{

    bool             rc;
    //int             fd;
    sigset_t        mask;
    int             signal;
    char            hn[80];

    /// returns the null-terminated hostname in the character
    /// array hn, which has a length of 79 bytes.
    /// is hostname used somewhere?
    if ( gethostname(hn,79) != 0 )
    {
        throw runtime_error("Error getting hostname");
    }

    hostname = hn;

    /** Configuration system */
    fass_configuration = new FassConfigurator(etc_location, var_location);

    rc = fass_configuration->load_configuration();

    if ( !rc )
    {
        throw runtime_error("Could not load Fass configuration file.");
    }

    /** Initial shares system */
    //initial_shares = new FassInitShares(etc_location, var_location);

    //rc = initial_shares->load_shares();

    //if ( !rc )
    //{
    //    throw runtime_error("Could not load initial shares file.");
    //}

    /** Log system */

    ostringstream os;

    try{
        Log::MessageType   clevel;

        clevel     = get_debug_level();

        /// Initializing FASS daemon log system
        string log_fname;
        log_fname = log_location + "fass.log";
        FassLog::init_log_system(clevel,
                           log_fname.c_str(),
                           ios_base::trunc,
                           "fassd");

        os << "Starting " << version() << endl;
        os << "----------------------------------------\n";
        os << "     Fass Configuration File      \n";
        os << "----------------------------------------\n";
        os << fass_configuration->get_conf_fname()  << " \n";
        os << "----------------------------------------";

        FassLog::log("FASS",Log::INFO,os);
        fass_configuration->print_loaded_options();
    } catch(runtime_error&){

        throw;
    }

    /** Initialize the XML library */
    // Try to avoid it
    //    xmlInitParser();

    /** Database */

    // instance of specific DB (can be changed)
    string dbtype;
    string dbendpoint;
    string dbname;
    int dbport;
    fass_configuration->get_single_option("database", "type", dbtype);
    fass_configuration->get_single_option("database", "endpoint", dbendpoint);
    fass_configuration->get_single_option("database", "port", dbport);
    fass_configuration->get_single_option("database", "name", dbname);
    if ( dbtype == "influxdb" ){ // TODO: should be done with a switch and enum, not critical
       database = new InfluxDb(dbendpoint, dbport, dbname);
    } else {
       FassLog::log("FASS", Log::ERROR, "Unknown database type!");
       throw;
    }

    // FOR VALE: database should be passed as argument to the Priority Manager

    /** Block all signals before creating any thread */

    sigfillset(&mask);

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    /** Managers */
    // some config variables are used both by the RPCManager and the XMLRPCClient (called by the PM)
    // we use the same valuse for both server/client, these should be consistent with the OpenNebula ones
    int  message_size;
    int  timeout;
    // OpenNebula xml-rpc endpoint
    string one_endpoint;
    string one_port;
    fass_configuration->get_single_option("fass", "one_endpoint", one_endpoint);
    fass_configuration->get_single_option("fass", "one_port", one_port);
    one_endpoint.append(":");
    one_endpoint.append(one_port);
    one_endpoint.append("/RPC2");
    //FassLog::log("FASS", Log::DEBUG, one_endpoint);
    // OpenNebula authentication
    string one_secret;
    fass_configuration->get_single_option("fass", "one_secret", one_secret);
    // xml-rpc config
    fass_configuration->get_single_option("rpcm", "message_size", message_size);
    fass_configuration->get_single_option("rpcm", "timeout", timeout);

   /// ---- Priority Manager ----
   try
   {
    int  manager_timer;
    int machines_limit;

    fass_configuration->get_single_option("pm", "max_vm", machines_limit);
    fass_configuration->get_single_option("pm", "manager_timer", manager_timer);

    // Read initial shares from separate file
    //initial_shares = new FassConfigurator(etc_location, var_location);

    //rc = initial_shares->load_configuration();

    //if ( !rc )
    //{
    //    throw runtime_error("Could not load Initial Shares file.");
    //}

    //std::vector<user> users;
    //std::list<users> list_of_users;

// TODO add the shares vector

    //initial_shares->get_single_option("users", "user", user);

    //list_of_users::iterator list_it;

    //for (list_it = users.begin(); list_it != users.end(); ++list_it)
    //{
    //    list_of_users.insert(list_it,users);
    //}

    //pm = new PriorityManager(one_endpoint, one_secret, message_size, timeout, machines_limit, manager_timer, list_of_users);
    pm = new PriorityManager(one_endpoint, one_secret, message_size, timeout, machines_limit, manager_timer);

    }

    catch (bad_alloc&)
    {
        FassLog::log("FASS", Log::ERROR, "Error creating Priority Manager");
        throw;
    }

    /// ---- Start the Priority Manager ----

    rc = pm->start();

    if ( !rc )
    {
       throw runtime_error("Could not start the Priority Manager");
    }

    /// ---- Request Manager ----
    try
    {

        //int  rm_port = 0;
        string rm_port = "";
	int  max_conn;
        int  max_conn_backlog;
        int  keepalive_timeout;
        int  keepalive_max_conn;
        bool rpc_log;
        string log_call_format;
        string rpc_filename = "";
        string rm_listen_address; //= "0.0.0.0";

        fass_configuration->get_single_option("rpcm", "listen_port", rm_port);
        fass_configuration->get_single_option("rpcm", "listen_address", rm_listen_address);
        fass_configuration->get_single_option("rpcm", "max_conn", max_conn);
        fass_configuration->get_single_option("rpcm", "max_conn_backlog", max_conn_backlog);
        fass_configuration->get_single_option("rpcm", "keepalive_timeout", keepalive_timeout);
        fass_configuration->get_single_option("rpcm", "keepalive_max_conn", keepalive_max_conn);
        fass_configuration->get_single_option("rpcm", "rpc_log", rpc_log);
        fass_configuration->get_single_option("rpcm", "log_call_format", log_call_format);

        if (rpc_log)
        {
            rpc_filename = log_location + "fass_xmlrpc.log";
        }

        rpcm = new RPCManager(one_endpoint,rm_port, max_conn, max_conn_backlog,
            keepalive_timeout, keepalive_max_conn, timeout, rpc_filename,
            log_call_format, rm_listen_address, message_size);

    }

    catch (bad_alloc&)
    {
        FassLog::log("FASS", Log::ERROR, "Error creating RPC Manager");
        throw;
    }

    /// ---- Start the Request Manager ----

    rc = rpcm->start();

    if ( !rc )
    {
       throw runtime_error("Could not start the RPC Manager");
    }

    /** Wait for a SIGTERM or SIGINT signal */

    sigemptyset(&mask);

    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);

    sigwait(&mask, &signal);

    /** Stop the managers and free resources */

    pm->finalize();

    //sleep to wait drivers???

    pthread_join(rpcm->get_thread_id(),0);
    pthread_join(pm->get_thread_id(),0);

    //XML Library
//    xmlCleanupParser();

    FassLog::log("FASS", Log::INFO, "All modules finalized, exiting.\n");

    return;

//error_mad:
//    Log::log("FASS", Log::ERROR, "Could not load driver");
//    throw runtime_error("Could not load a Fass driver");
}

Log::MessageType Fass::get_debug_level() const
{
    Log::MessageType clevel = Log::ERROR;

    int              log_level_int ;

    fass_configuration->get_single_option("fass", "log_level", log_level_int);

    if ( log_level_int != 0 )
    {

        if ( Log::ERROR <= log_level_int && log_level_int <= Log::DDDEBUG )
        {
            clevel = static_cast<Log::MessageType>(log_level_int);
        }
    }

    return clevel;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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.
 */

#include "Stock.hxx"
#include "Factory.hxx"
#include "FilteredSocket.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/LoggerDomain.hxx"
#include "address_list.hxx"
#include "pool/pool.hxx"
#include "net/PConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/ToString.hxx"
#include "io/Logger.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

struct FilteredSocketStockRequest {
    const bool ip_transparent;

    const SocketAddress bind_address, address;

    const unsigned timeout;

    SocketFilterFactory *const filter_factory;

    FilteredSocketStockRequest(bool _ip_transparent,
                               SocketAddress _bind_address,
                               SocketAddress _address,
                               unsigned _timeout,
                               SocketFilterFactory *_filter_factory)
        :ip_transparent(_ip_transparent),
         bind_address(_bind_address), address(_address),
         timeout(_timeout),
         filter_factory(_filter_factory) {}
};

struct FilteredSocketStockConnection final
    : StockItem, ConnectSocketHandler, BufferedSocketHandler, Cancellable {

    BasicLogger<StockLoggerDomain> logger;

    const FdType type;

    const AllocatedSocketAddress address;

    SocketFilterFactory *const filter_factory;

    /**
     * To cancel the ClientSocket.
     */
    CancellablePointer cancel_ptr;

    FilteredSocket socket;

    FilteredSocketStockConnection(CreateStockItem c, FdType _type,
                                  SocketAddress _address,
                                  SocketFilterFactory *_filter_factory,
                                  CancellablePointer &_cancel_ptr) noexcept
        :StockItem(c),
         logger(c.stock),
         type(_type),
         address(_address),
         filter_factory(_filter_factory),
         socket(c.stock.GetEventLoop()) {
        _cancel_ptr = *this;

        cancel_ptr = nullptr;
    }

    ~FilteredSocketStockConnection() override {
        if (cancel_ptr)
            cancel_ptr.Cancel();
        else if (socket.IsValid() && socket.IsConnected()) {
            socket.Close();
            socket.Destroy();
        }
    }

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        assert(cancel_ptr);

        cancel_ptr.CancelAndClear();
        InvokeCreateAborted();
    }

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) override;
    void OnSocketConnectError(std::exception_ptr ep) override;

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override;
    bool OnBufferedClosed() noexcept override;

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override;

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        return true;
    }

    bool Release() noexcept override;
};

/*
 * BufferedSocketHandler
 *
 */

BufferedResult
FilteredSocketStockConnection::OnBufferedData()
{
    logger(2, "unexpected data in idle TCP connection");
    InvokeIdleDisconnect();
    return BufferedResult::CLOSED;
}

bool
FilteredSocketStockConnection::OnBufferedClosed() noexcept
{
    InvokeIdleDisconnect();
    return false;
}

void
FilteredSocketStockConnection::OnBufferedError(std::exception_ptr e) noexcept
{
    logger(2, "error on idle connection: ", e);
    InvokeIdleDisconnect();
}

/*
 * client_socket callback
 *
 */

void
FilteredSocketStockConnection::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd)
{
    cancel_ptr = nullptr;

    try {
        socket.Init(fd.Release(), type,
                    nullptr, nullptr,
                    filter_factory != nullptr
                    ? filter_factory->CreateFilter()
                    : nullptr,
                    *this);
    } catch (...) {
        InvokeCreateError(std::current_exception());
        return;
    }

    InvokeCreateSuccess();
}

void
FilteredSocketStockConnection::OnSocketConnectError(std::exception_ptr ep)
{
    cancel_ptr = nullptr;

    ep = NestException(ep,
                       FormatRuntimeError("Failed to connect to '%s'",
                                          GetStockName()));
    InvokeCreateError(ep);
}

/*
 * stock class
 *
 */

void
FilteredSocketStock::Create(CreateStockItem c, void *info,
                            struct pool &caller_pool,
                            CancellablePointer &cancel_ptr)
{
    const auto &request = *(const FilteredSocketStockRequest *)info;

    const int address_family = request.address.GetFamily();
    const FdType type = address_family == AF_LOCAL
        ? FD_SOCKET
        : FD_TCP;

    auto *connection = new FilteredSocketStockConnection(c, type,
                                                         request.address,
                                                         request.filter_factory,
                                                         cancel_ptr);

    client_socket_new(c.stock.GetEventLoop(), caller_pool,
                      address_family, SOCK_STREAM, 0,
                      request.ip_transparent,
                      request.bind_address,
                      request.address,
                      request.timeout,
                      *connection,
                      connection->cancel_ptr);
}

bool
FilteredSocketStockConnection::Release() noexcept
{
    if (!socket.IsEmpty()) {
        logger(2, "unexpected data in idle connection");
        return false;
    }

    socket.Reinit(nullptr, nullptr, *this);
    socket.UnscheduleWrite();

    static const struct timeval tv = {
        .tv_sec = 60,
        .tv_usec = 0,
    };
    socket.ScheduleReadTimeout(false, &tv);

    return true;
}

/*
 * interface
 *
 */

void
FilteredSocketStock::Get(struct pool &pool, const char *name,
                         bool ip_transparent,
                         SocketAddress bind_address,
                         SocketAddress address,
                         unsigned timeout,
                         SocketFilterFactory *filter_factory,
                         StockGetHandler &handler,
                         CancellablePointer &cancel_ptr) noexcept
{
    assert(!address.IsNull());

    auto request =
        NewFromPool<FilteredSocketStockRequest>(pool, ip_transparent,
                                                bind_address, address,
                                                timeout, filter_factory);

    if (name == nullptr) {
        char buffer[1024];
        if (!ToString(buffer, sizeof(buffer), address))
            buffer[0] = 0;

        if (!bind_address.IsNull()) {
            char bind_buffer[1024];
            if (!ToString(bind_buffer, sizeof(bind_buffer), bind_address))
                bind_buffer[0] = 0;
            name = p_strcat(&pool, bind_buffer, ">", buffer, nullptr);
        } else
            name = p_strdup(&pool, buffer);
    }

    if (filter_factory != nullptr)
        name = p_strcat(&pool, name, "|", filter_factory->GetFilterId(),
                        nullptr);

    stock.Get(pool, name, request, handler, cancel_ptr);
}

FilteredSocket &
fs_stock_item_get(StockItem &item)
{
    auto &connection = (FilteredSocketStockConnection &)item;

    return connection.socket;
}

SocketAddress
fs_stock_item_get_address(const StockItem &item)
{
    const auto &connection = (const FilteredSocketStockConnection &)item;

    return connection.address;
}
<commit_msg>fs/Stock: check IsConnected() in Release()<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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.
 */

#include "Stock.hxx"
#include "Factory.hxx"
#include "FilteredSocket.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/LoggerDomain.hxx"
#include "address_list.hxx"
#include "pool/pool.hxx"
#include "net/PConnectSocket.hxx"
#include "net/SocketAddress.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/ToString.hxx"
#include "io/Logger.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"
#include "util/Exception.hxx"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

struct FilteredSocketStockRequest {
    const bool ip_transparent;

    const SocketAddress bind_address, address;

    const unsigned timeout;

    SocketFilterFactory *const filter_factory;

    FilteredSocketStockRequest(bool _ip_transparent,
                               SocketAddress _bind_address,
                               SocketAddress _address,
                               unsigned _timeout,
                               SocketFilterFactory *_filter_factory)
        :ip_transparent(_ip_transparent),
         bind_address(_bind_address), address(_address),
         timeout(_timeout),
         filter_factory(_filter_factory) {}
};

struct FilteredSocketStockConnection final
    : StockItem, ConnectSocketHandler, BufferedSocketHandler, Cancellable {

    BasicLogger<StockLoggerDomain> logger;

    const FdType type;

    const AllocatedSocketAddress address;

    SocketFilterFactory *const filter_factory;

    /**
     * To cancel the ClientSocket.
     */
    CancellablePointer cancel_ptr;

    FilteredSocket socket;

    FilteredSocketStockConnection(CreateStockItem c, FdType _type,
                                  SocketAddress _address,
                                  SocketFilterFactory *_filter_factory,
                                  CancellablePointer &_cancel_ptr) noexcept
        :StockItem(c),
         logger(c.stock),
         type(_type),
         address(_address),
         filter_factory(_filter_factory),
         socket(c.stock.GetEventLoop()) {
        _cancel_ptr = *this;

        cancel_ptr = nullptr;
    }

    ~FilteredSocketStockConnection() override {
        if (cancel_ptr)
            cancel_ptr.Cancel();
        else if (socket.IsValid() && socket.IsConnected()) {
            socket.Close();
            socket.Destroy();
        }
    }

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override {
        assert(cancel_ptr);

        cancel_ptr.CancelAndClear();
        InvokeCreateAborted();
    }

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(UniqueSocketDescriptor &&fd) override;
    void OnSocketConnectError(std::exception_ptr ep) override;

    /* virtual methods from class BufferedSocketHandler */
    BufferedResult OnBufferedData() override;
    bool OnBufferedClosed() noexcept override;

    gcc_noreturn
    bool OnBufferedWrite() override {
        /* should never be reached because we never schedule
           writing */
        gcc_unreachable();
    }

    void OnBufferedError(std::exception_ptr e) noexcept override;

    /* virtual methods from class StockItem */
    bool Borrow() noexcept override {
        return true;
    }

    bool Release() noexcept override;
};

/*
 * BufferedSocketHandler
 *
 */

BufferedResult
FilteredSocketStockConnection::OnBufferedData()
{
    logger(2, "unexpected data in idle TCP connection");
    InvokeIdleDisconnect();
    return BufferedResult::CLOSED;
}

bool
FilteredSocketStockConnection::OnBufferedClosed() noexcept
{
    InvokeIdleDisconnect();
    return false;
}

void
FilteredSocketStockConnection::OnBufferedError(std::exception_ptr e) noexcept
{
    logger(2, "error on idle connection: ", e);
    InvokeIdleDisconnect();
}

/*
 * client_socket callback
 *
 */

void
FilteredSocketStockConnection::OnSocketConnectSuccess(UniqueSocketDescriptor &&fd)
{
    cancel_ptr = nullptr;

    try {
        socket.Init(fd.Release(), type,
                    nullptr, nullptr,
                    filter_factory != nullptr
                    ? filter_factory->CreateFilter()
                    : nullptr,
                    *this);
    } catch (...) {
        InvokeCreateError(std::current_exception());
        return;
    }

    InvokeCreateSuccess();
}

void
FilteredSocketStockConnection::OnSocketConnectError(std::exception_ptr ep)
{
    cancel_ptr = nullptr;

    ep = NestException(ep,
                       FormatRuntimeError("Failed to connect to '%s'",
                                          GetStockName()));
    InvokeCreateError(ep);
}

/*
 * stock class
 *
 */

void
FilteredSocketStock::Create(CreateStockItem c, void *info,
                            struct pool &caller_pool,
                            CancellablePointer &cancel_ptr)
{
    const auto &request = *(const FilteredSocketStockRequest *)info;

    const int address_family = request.address.GetFamily();
    const FdType type = address_family == AF_LOCAL
        ? FD_SOCKET
        : FD_TCP;

    auto *connection = new FilteredSocketStockConnection(c, type,
                                                         request.address,
                                                         request.filter_factory,
                                                         cancel_ptr);

    client_socket_new(c.stock.GetEventLoop(), caller_pool,
                      address_family, SOCK_STREAM, 0,
                      request.ip_transparent,
                      request.bind_address,
                      request.address,
                      request.timeout,
                      *connection,
                      connection->cancel_ptr);
}

bool
FilteredSocketStockConnection::Release() noexcept
{
    if (!socket.IsConnected())
        return false;

    if (!socket.IsEmpty()) {
        logger(2, "unexpected data in idle connection");
        return false;
    }

    socket.Reinit(nullptr, nullptr, *this);
    socket.UnscheduleWrite();

    static const struct timeval tv = {
        .tv_sec = 60,
        .tv_usec = 0,
    };
    socket.ScheduleReadTimeout(false, &tv);

    return true;
}

/*
 * interface
 *
 */

void
FilteredSocketStock::Get(struct pool &pool, const char *name,
                         bool ip_transparent,
                         SocketAddress bind_address,
                         SocketAddress address,
                         unsigned timeout,
                         SocketFilterFactory *filter_factory,
                         StockGetHandler &handler,
                         CancellablePointer &cancel_ptr) noexcept
{
    assert(!address.IsNull());

    auto request =
        NewFromPool<FilteredSocketStockRequest>(pool, ip_transparent,
                                                bind_address, address,
                                                timeout, filter_factory);

    if (name == nullptr) {
        char buffer[1024];
        if (!ToString(buffer, sizeof(buffer), address))
            buffer[0] = 0;

        if (!bind_address.IsNull()) {
            char bind_buffer[1024];
            if (!ToString(bind_buffer, sizeof(bind_buffer), bind_address))
                bind_buffer[0] = 0;
            name = p_strcat(&pool, bind_buffer, ">", buffer, nullptr);
        } else
            name = p_strdup(&pool, buffer);
    }

    if (filter_factory != nullptr)
        name = p_strcat(&pool, name, "|", filter_factory->GetFilterId(),
                        nullptr);

    stock.Get(pool, name, request, handler, cancel_ptr);
}

FilteredSocket &
fs_stock_item_get(StockItem &item)
{
    auto &connection = (FilteredSocketStockConnection &)item;

    return connection.socket;
}

SocketAddress
fs_stock_item_get_address(const StockItem &item)
{
    const auto &connection = (const FilteredSocketStockConnection &)item;

    return connection.address;
}
<|endoftext|>


<commit_before>/*  $Id$
 *
 *  Copyright 2010 Anders Wallin (anders.e.e.wallin "at" gmail.com)
 *
 *  This file is part of OpenCAMlib.
 *
 *  OpenCAMlib 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.
 *
 *  OpenCAMlib 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 OpenCAMlib.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PATH_H
#define PATH_H

#include <list>

#include "point.hpp"
#include "line.hpp"
#include "arc.hpp"

namespace ocl
{

/// Span type
enum SpanType{
    LineSpanType,
    ArcSpanType
};

/// \brief A finite curve which returns Point objects along its length.
///
/// location along span is based on a parameter t for which 0 <= t <= 1.0
class Span{
    public:
        /// return type of span
        virtual SpanType type()const = 0;
        /// return the length of the span in the xy-plane
        virtual double length2d()const = 0;
        /// return a point at parameter value 0 <= t <= 1.0
        virtual Point getPoint(double t) const = 0; // 0.0 to 1.0
};

/// Line Span
class LineSpan : public Span {
    public:
        /// create a line span from Line l
        LineSpan(const Line& l) : line(l){}
        /// the line
        Line line;

        // Span's virtual functions
        /// return span type
        SpanType type()const{return LineSpanType;}
        /// return span length
        double length2d() const {
            return line.length2d();
        }
        /// return point on span
        Point getPoint(double t) const {
            return line.getPoint(t);
        }
};

/// circular Arc Span
class ArcSpan : public Span {
    public:
        /// create span
        ArcSpan(const Arc& a) : arc(a){}
        /// arc
        Arc arc;

        // Span's virtual functions
        /// return type
        SpanType type()const{return ArcSpanType;}
        /// return length in xy-plane
        double length2d()const{return arc.length2d();}
        /// return a point on the span
        Point getPoint(double t)const{return arc.getPoint(t);}
};

///
/// \brief A collection of Span objects
///
class Path {
    public:
        /// create empty path
        Path();
        /// copy constructor
        Path(const Path &p);
        /// destructor
        virtual ~Path();

        /// list of spans in this path
        std::list<Span*> span_list;

        // FIXME: this looks wrong
        // should be only one append() that takes a Span
        /// append a Line to this path
        void append(const Line &l);
        /// append an Arc to this path
        void append(const Arc &a);
};

} // end namespace
#endif
// end file path.h
<commit_msg>Fix gcc 4.7.1 compile error<commit_after>/*  $Id$
 *
 *  Copyright 2010 Anders Wallin (anders.e.e.wallin "at" gmail.com)
 *
 *  This file is part of OpenCAMlib.
 *
 *  OpenCAMlib 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.
 *
 *  OpenCAMlib 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 OpenCAMlib.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PATH_H
#define PATH_H

#include <list>

#include "point.hpp"
#include "line.hpp"
#include "arc.hpp"

namespace ocl
{

/// Span type
enum SpanType{
    LineSpanType,
    ArcSpanType
};

/// \brief A finite curve which returns Point objects along its length.
///
/// location along span is based on a parameter t for which 0 <= t <= 1.0
class Span{
    public:
        /// return type of span
        virtual SpanType type()const = 0;
        /// return the length of the span in the xy-plane
        virtual double length2d()const = 0;
        /// return a point at parameter value 0 <= t <= 1.0
        virtual Point getPoint(double t) const = 0; // 0.0 to 1.0
        /// avoid gcc 4.7.1 delete-non-virtual-dtor error
        virtual ~Span();
};

/// Line Span
class LineSpan : public Span {
    public:
        /// create a line span from Line l
        LineSpan(const Line& l) : line(l){}
        /// the line
        Line line;

        // Span's virtual functions
        /// return span type
        SpanType type()const{return LineSpanType;}
        /// return span length
        double length2d() const {
            return line.length2d();
        }
        /// return point on span
        Point getPoint(double t) const {
            return line.getPoint(t);
        }
};

/// circular Arc Span
class ArcSpan : public Span {
    public:
        /// create span
        ArcSpan(const Arc& a) : arc(a){}
        /// arc
        Arc arc;

        // Span's virtual functions
        /// return type
        SpanType type()const{return ArcSpanType;}
        /// return length in xy-plane
        double length2d()const{return arc.length2d();}
        /// return a point on the span
        Point getPoint(double t)const{return arc.getPoint(t);}
};

///
/// \brief A collection of Span objects
///
class Path {
    public:
        /// create empty path
        Path();
        /// copy constructor
        Path(const Path &p);
        /// destructor
        virtual ~Path();

        /// list of spans in this path
        std::list<Span*> span_list;

        // FIXME: this looks wrong
        // should be only one append() that takes a Span
        /// append a Line to this path
        void append(const Line &l);
        /// append an Arc to this path
        void append(const Arc &a);
};

} // end namespace
#endif
// end file path.h
<|endoftext|>