<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "partition_version.hh"

class partition_snapshot_row_cursor;

// A non-owning reference to a row inside partition_snapshot which
// maintains it's position and thus can be kept across reference invalidation points.
class partition_snapshot_row_weakref final {
    mutation_partition::rows_type::iterator _it;
    partition_snapshot::change_mark _change_mark;
    position_in_partition _pos = position_in_partition::min();
public:
    partition_snapshot_row_weakref() = default;
    // Makes this object point to a row pointed to by given partition_snapshot_row_cursor.
    explicit partition_snapshot_row_weakref(const partition_snapshot_row_cursor&);
    explicit partition_snapshot_row_weakref(std::nullptr_t) {}
    partition_snapshot_row_weakref(partition_snapshot& snp, mutation_partition::rows_type::iterator it)
        : _it(it)
        , _change_mark(snp.get_change_mark())
        , _pos(it->position())
    { }
    partition_snapshot_row_weakref& operator=(const partition_snapshot_row_cursor&);
    partition_snapshot_row_weakref& operator=(std::nullptr_t) noexcept {
        _change_mark = {};
        return *this;
    }
    // Returns true iff the pointer is pointing at a row.
    explicit operator bool() const { return _change_mark != partition_snapshot::change_mark(); }
public:
    // Returns the position of the row.
    // Call only when pointing at a row.
    const position_in_partition& position() const { return _pos; }
    // Returns true iff the object is valid.
    bool valid(partition_snapshot& snp) { return snp.get_change_mark() == _change_mark; }
    // Brings the object back to validity and returns true iff the snapshot contains the row.
    // When not pointing at a row, returns false.
    bool refresh(partition_snapshot& snp) {
        auto snp_cm = snp.get_change_mark();
        if (snp_cm == _change_mark) {
            return true;
        }
        if (!_change_mark) {
            return false;
        }
        _change_mark = snp_cm;
        rows_entry::compare less(*snp.schema());
        for (auto&& v : snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            _it = rows.find(_pos, less);
            if (_it != rows.end()) {
                return true;
            }
        }
        return false;
    }
};

// Allows iterating over rows of mutation_partition represented by given partition_snapshot.
//
// The cursor initially has a position before all rows and is not pointing at any row.
// To position the cursor, use advance_to().
//
// All methods should be called with the region of the snapshot locked. The cursor is invalidated
// when that lock section is left, or if the snapshot is modified.
//
// When the cursor is invalidated, it still maintains its previous position. It can be brought
// back to validity by calling maybe_refresh(), or advance_to().
//
// Insertion of row entries after cursor's position invalidates the cursor.
// Exceptions thrown from mutators invalidate the cursor.
//
class partition_snapshot_row_cursor final {
    friend class partition_snapshot_row_weakref;
    struct position_in_version {
        mutation_partition::rows_type::iterator it;
        mutation_partition::rows_type::iterator end;
        int version_no;

        struct less_compare {
            rows_entry::tri_compare _cmp;
        public:
            explicit less_compare(const schema& s) : _cmp(s) { }
            bool operator()(const position_in_version& a, const position_in_version& b) {
                auto res = _cmp(*a.it, *b.it);
                return res > 0 || (res == 0 && a.version_no > b.version_no);
            }
        };
    };

    const schema& _schema;
    partition_snapshot& _snp;
    std::vector<position_in_version> _heap;
    std::vector<mutation_partition::rows_type::iterator> _iterators;
    std::vector<position_in_version> _current_row;
    position_in_partition _position;
    partition_snapshot::change_mark _change_mark;

    // Removes the next row from _heap and puts it into _current_row
    void recreate_current_row() {
        position_in_version::less_compare heap_less(_schema);
        position_in_partition::equal_compare eq(_schema);
        do {
            boost::range::pop_heap(_heap, heap_less);
            memory::on_alloc_point();
            _current_row.push_back(_heap.back());
            _heap.pop_back();
        } while (!_heap.empty() && eq(_current_row[0].it->position(), _heap[0].it->position()));
        _position = position_in_partition(_current_row[0].it->position());
    }
public:
    partition_snapshot_row_cursor(const schema& s, partition_snapshot& snp)
        : _schema(s)
        , _snp(snp)
        , _position(position_in_partition::static_row_tag_t{})
    { }

    mutation_partition::rows_type::iterator get_iterator_in_latest_version() const {
        return _iterators[0];
    }

    // Returns true iff the iterators obtained since the cursor was last made valid
    // are still valid. Note that this doesn't mean that the cursor itself is valid.
    bool iterators_valid() const {
        return _snp.get_change_mark() == _change_mark;
    }

    // Brings back the cursor to validity.
    // Can be only called when cursor is pointing at a row.
    //
    // Semantically equivalent to:
    //
    //   advance_to(position());
    //
    // but avoids work if not necessary.
    bool maybe_refresh() {
        if (!iterators_valid()) {
            return advance_to(_position);
        }
        // Refresh latest version's iterator in case there was an insertion
        // before it and after cursor's position. There cannot be any
        // insertions for non-latest versions, so we don't have to update them.
        if (_current_row[0].version_no != 0) {
            rows_entry::compare less(_schema);
            position_in_partition::equal_compare eq(_schema);
            position_in_version::less_compare heap_less(_schema);
            auto& rows = _snp.version()->partition().clustered_rows();
            auto it = _iterators[0] = rows.lower_bound(_position, less);
            auto heap_i = boost::find_if(_heap, [](auto&& v) { return v.version_no == 0; });
            if (it == rows.end()) {
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else if (eq(_position, it->position())) {
                _current_row.insert(_current_row.begin(), position_in_version{it, rows.end(), 0});
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else {
                if (heap_i != _heap.end()) {
                    heap_i->it = it;
                    boost::range::make_heap(_heap, heap_less);
                } else {
                    _heap.push_back({it, rows.end(), 0});
                    boost::range::push_heap(_heap, heap_less);
                }
            }
        }
        return true;
    }

    // Moves the cursor to the first entry with position >= pos.
    //
    // The caller must ensure that such entry exists.
    //
    // Returns true iff there can't be any clustering row entries
    // between lower_bound (inclusive) and the entry to which the cursor
    // was advanced.
    //
    // May be called when cursor is not valid.
    // The cursor is valid after the call.
    // Must be called under reclaim lock.
    // When throws, the cursor is invalidated and its position is not changed.
    bool advance_to(position_in_partition_view lower_bound) {
        memory::on_alloc_point();
        rows_entry::compare less(_schema);
        position_in_version::less_compare heap_less(_schema);
        _heap.clear();
        _current_row.clear();
        _iterators.clear();
        int version_no = 0;
        for (auto&& v : _snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            auto pos = rows.lower_bound(lower_bound, less);
            auto end = rows.end();
            _iterators.push_back(pos);
            if (pos != end) {
                _heap.push_back({pos, end, version_no});
            }
            ++version_no;
        }
        boost::range::make_heap(_heap, heap_less);
        _change_mark = _snp.get_change_mark();
        bool found = no_clustering_row_between(_schema, lower_bound, _heap[0].it->position());
        recreate_current_row();
        return found;
    }

    // Advances the cursor to the next row.
    // If there is no next row, returns false and the cursor is no longer pointing at a row.
    // Can be only called on a valid cursor pointing at a row.
    // When throws, the cursor is invalidated and its position is not changed.
    bool next() {
        memory::on_alloc_point();
        position_in_version::less_compare heap_less(_schema);
        assert(iterators_valid());
        for (auto&& curr : _current_row) {
            ++curr.it;
            _iterators[curr.version_no] = curr.it;
            if (curr.it != curr.end) {
                _heap.push_back(curr);
                boost::range::push_heap(_heap, heap_less);
            }
        }
        _current_row.clear();
        if (_heap.empty()) {
            return false;
        }
        recreate_current_row();
        return true;
    }

    // Can be called only when cursor is valid and pointing at a row.
    bool continuous() const { return bool(_current_row[0].it->continuous()); }

    // Can be called only when cursor is valid and pointing at a row.
    bool dummy() const { return bool(_current_row[0].it->dummy()); }

    // Can be called only when cursor is valid and pointing at a row, and !dummy().
    const clustering_key& key() const { return _current_row[0].it->key(); }

    // Can be called only when cursor is valid and pointing at a row.
    mutation_fragment row() const {
        auto it = _current_row.begin();
        auto mf = mutation_fragment(clustering_row(*it->it));
        auto& cr = mf.as_mutable_clustering_row();
        for (++it; it != _current_row.end(); ++it) {
            cr.apply(_schema, *it->it);
        }
        return mf;
    }

    // Can be called when cursor is pointing at a row, even when invalid.
    const position_in_partition& position() const {
        return _position;
    }

    bool is_in_latest_version() const;

    // Reads the rest of the partition into a mutation_partition object.
    // There must be at least one entry ahead of the cursor.
    // The cursor must be pointing at a row and valid.
    // The cursor will not be pointing at a row after this.
    mutation_partition read_partition() {
        mutation_partition p(_schema.shared_from_this());
        do {
            p.clustered_row(_schema, position(), is_dummy(dummy()), is_continuous(continuous()))
                .apply(_schema, row().as_clustering_row());
        } while (next());
        return p;
    }

    friend std::ostream& operator<<(std::ostream& out, const partition_snapshot_row_cursor& cur) {
        out << "{cursor: position=" << cur._position << ", ";
        if (!cur.iterators_valid()) {
            return out << " iterators invalid}";
        }
        out << "current=[";
        bool first = true;
        for (auto&& v : cur._current_row) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << v.version_no;
        }
        out << "], heap=[";
        first = true;
        for (auto&& v : cur._heap) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << "{v=" << v.version_no << ", pos=" << v.it->position() << "}";
        }
        out << "], iterators=[";
        first = true;
        auto v = cur._snp.versions().begin();
        for (auto&& i : cur._iterators) {
            if (!first) {
                out << ", ";
            }
            first = false;
            if (i == v->partition().clustered_rows().end()) {
                out << "end";
            } else {
                out << i->position();
            }
            ++v;
        }
        return out << "]}";
    };
};

inline
bool partition_snapshot_row_cursor::is_in_latest_version() const {
    return _current_row[0].version_no == 0;
}

inline
partition_snapshot_row_weakref::partition_snapshot_row_weakref(const partition_snapshot_row_cursor& c)
    : _it(c._current_row[0].it)
    , _change_mark(c._change_mark)
    , _pos(c._position)
{ }

inline
partition_snapshot_row_weakref& partition_snapshot_row_weakref::operator=(const partition_snapshot_row_cursor& c) {
    auto tmp = partition_snapshot_row_weakref(c);
    this->~partition_snapshot_row_weakref();
    new (this) partition_snapshot_row_weakref(std::move(tmp));
    return *this;
}
<commit_msg>mvcc: partition_snapshot_row_cursor: Extract prepare_heap()<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "partition_version.hh"

class partition_snapshot_row_cursor;

// A non-owning reference to a row inside partition_snapshot which
// maintains it's position and thus can be kept across reference invalidation points.
class partition_snapshot_row_weakref final {
    mutation_partition::rows_type::iterator _it;
    partition_snapshot::change_mark _change_mark;
    position_in_partition _pos = position_in_partition::min();
public:
    partition_snapshot_row_weakref() = default;
    // Makes this object point to a row pointed to by given partition_snapshot_row_cursor.
    explicit partition_snapshot_row_weakref(const partition_snapshot_row_cursor&);
    explicit partition_snapshot_row_weakref(std::nullptr_t) {}
    partition_snapshot_row_weakref(partition_snapshot& snp, mutation_partition::rows_type::iterator it)
        : _it(it)
        , _change_mark(snp.get_change_mark())
        , _pos(it->position())
    { }
    partition_snapshot_row_weakref& operator=(const partition_snapshot_row_cursor&);
    partition_snapshot_row_weakref& operator=(std::nullptr_t) noexcept {
        _change_mark = {};
        return *this;
    }
    // Returns true iff the pointer is pointing at a row.
    explicit operator bool() const { return _change_mark != partition_snapshot::change_mark(); }
public:
    // Returns the position of the row.
    // Call only when pointing at a row.
    const position_in_partition& position() const { return _pos; }
    // Returns true iff the object is valid.
    bool valid(partition_snapshot& snp) { return snp.get_change_mark() == _change_mark; }
    // Brings the object back to validity and returns true iff the snapshot contains the row.
    // When not pointing at a row, returns false.
    bool refresh(partition_snapshot& snp) {
        auto snp_cm = snp.get_change_mark();
        if (snp_cm == _change_mark) {
            return true;
        }
        if (!_change_mark) {
            return false;
        }
        _change_mark = snp_cm;
        rows_entry::compare less(*snp.schema());
        for (auto&& v : snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            _it = rows.find(_pos, less);
            if (_it != rows.end()) {
                return true;
            }
        }
        return false;
    }
};

// Allows iterating over rows of mutation_partition represented by given partition_snapshot.
//
// The cursor initially has a position before all rows and is not pointing at any row.
// To position the cursor, use advance_to().
//
// All methods should be called with the region of the snapshot locked. The cursor is invalidated
// when that lock section is left, or if the snapshot is modified.
//
// When the cursor is invalidated, it still maintains its previous position. It can be brought
// back to validity by calling maybe_refresh(), or advance_to().
//
// Insertion of row entries after cursor's position invalidates the cursor.
// Exceptions thrown from mutators invalidate the cursor.
//
class partition_snapshot_row_cursor final {
    friend class partition_snapshot_row_weakref;
    struct position_in_version {
        mutation_partition::rows_type::iterator it;
        mutation_partition::rows_type::iterator end;
        int version_no;

        struct less_compare {
            rows_entry::tri_compare _cmp;
        public:
            explicit less_compare(const schema& s) : _cmp(s) { }
            bool operator()(const position_in_version& a, const position_in_version& b) {
                auto res = _cmp(*a.it, *b.it);
                return res > 0 || (res == 0 && a.version_no > b.version_no);
            }
        };
    };

    const schema& _schema;
    partition_snapshot& _snp;
    std::vector<position_in_version> _heap;
    std::vector<mutation_partition::rows_type::iterator> _iterators;
    std::vector<position_in_version> _current_row;
    position_in_partition _position;
    partition_snapshot::change_mark _change_mark;

    // Removes the next row from _heap and puts it into _current_row
    void recreate_current_row() {
        position_in_version::less_compare heap_less(_schema);
        position_in_partition::equal_compare eq(_schema);
        do {
            boost::range::pop_heap(_heap, heap_less);
            memory::on_alloc_point();
            _current_row.push_back(_heap.back());
            _heap.pop_back();
        } while (!_heap.empty() && eq(_current_row[0].it->position(), _heap[0].it->position()));
        _position = position_in_partition(_current_row[0].it->position());
    }

    void prepare_heap(position_in_partition_view lower_bound) {
        memory::on_alloc_point();
        rows_entry::compare less(_schema);
        position_in_version::less_compare heap_less(_schema);
        _heap.clear();
        _current_row.clear();
        _iterators.clear();
        int version_no = 0;
        for (auto&& v : _snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            auto pos = rows.lower_bound(lower_bound, less);
            auto end = rows.end();
            _iterators.push_back(pos);
            if (pos != end) {
                _heap.push_back({pos, end, version_no});
            }
            ++version_no;
        }
        boost::range::make_heap(_heap, heap_less);
    }
public:
    partition_snapshot_row_cursor(const schema& s, partition_snapshot& snp)
        : _schema(s)
        , _snp(snp)
        , _position(position_in_partition::static_row_tag_t{})
    { }

    mutation_partition::rows_type::iterator get_iterator_in_latest_version() const {
        return _iterators[0];
    }

    // Returns true iff the iterators obtained since the cursor was last made valid
    // are still valid. Note that this doesn't mean that the cursor itself is valid.
    bool iterators_valid() const {
        return _snp.get_change_mark() == _change_mark;
    }

    // Brings back the cursor to validity.
    // Can be only called when cursor is pointing at a row.
    //
    // Semantically equivalent to:
    //
    //   advance_to(position());
    //
    // but avoids work if not necessary.
    bool maybe_refresh() {
        if (!iterators_valid()) {
            return advance_to(_position);
        }
        // Refresh latest version's iterator in case there was an insertion
        // before it and after cursor's position. There cannot be any
        // insertions for non-latest versions, so we don't have to update them.
        if (_current_row[0].version_no != 0) {
            rows_entry::compare less(_schema);
            position_in_partition::equal_compare eq(_schema);
            position_in_version::less_compare heap_less(_schema);
            auto& rows = _snp.version()->partition().clustered_rows();
            auto it = _iterators[0] = rows.lower_bound(_position, less);
            auto heap_i = boost::find_if(_heap, [](auto&& v) { return v.version_no == 0; });
            if (it == rows.end()) {
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else if (eq(_position, it->position())) {
                _current_row.insert(_current_row.begin(), position_in_version{it, rows.end(), 0});
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else {
                if (heap_i != _heap.end()) {
                    heap_i->it = it;
                    boost::range::make_heap(_heap, heap_less);
                } else {
                    _heap.push_back({it, rows.end(), 0});
                    boost::range::push_heap(_heap, heap_less);
                }
            }
        }
        return true;
    }

    // Moves the cursor to the first entry with position >= pos.
    //
    // The caller must ensure that such entry exists.
    //
    // Returns true iff there can't be any clustering row entries
    // between lower_bound (inclusive) and the entry to which the cursor
    // was advanced.
    //
    // May be called when cursor is not valid.
    // The cursor is valid after the call.
    // Must be called under reclaim lock.
    // When throws, the cursor is invalidated and its position is not changed.
    bool advance_to(position_in_partition_view lower_bound) {
        prepare_heap(lower_bound);
        _change_mark = _snp.get_change_mark();
        bool found = no_clustering_row_between(_schema, lower_bound, _heap[0].it->position());
        recreate_current_row();
        return found;
    }

    // Advances the cursor to the next row.
    // If there is no next row, returns false and the cursor is no longer pointing at a row.
    // Can be only called on a valid cursor pointing at a row.
    // When throws, the cursor is invalidated and its position is not changed.
    bool next() {
        memory::on_alloc_point();
        position_in_version::less_compare heap_less(_schema);
        assert(iterators_valid());
        for (auto&& curr : _current_row) {
            ++curr.it;
            _iterators[curr.version_no] = curr.it;
            if (curr.it != curr.end) {
                _heap.push_back(curr);
                boost::range::push_heap(_heap, heap_less);
            }
        }
        _current_row.clear();
        if (_heap.empty()) {
            return false;
        }
        recreate_current_row();
        return true;
    }

    // Can be called only when cursor is valid and pointing at a row.
    bool continuous() const { return bool(_current_row[0].it->continuous()); }

    // Can be called only when cursor is valid and pointing at a row.
    bool dummy() const { return bool(_current_row[0].it->dummy()); }

    // Can be called only when cursor is valid and pointing at a row, and !dummy().
    const clustering_key& key() const { return _current_row[0].it->key(); }

    // Can be called only when cursor is valid and pointing at a row.
    mutation_fragment row() const {
        auto it = _current_row.begin();
        auto mf = mutation_fragment(clustering_row(*it->it));
        auto& cr = mf.as_mutable_clustering_row();
        for (++it; it != _current_row.end(); ++it) {
            cr.apply(_schema, *it->it);
        }
        return mf;
    }

    // Can be called when cursor is pointing at a row, even when invalid.
    const position_in_partition& position() const {
        return _position;
    }

    bool is_in_latest_version() const;

    // Reads the rest of the partition into a mutation_partition object.
    // There must be at least one entry ahead of the cursor.
    // The cursor must be pointing at a row and valid.
    // The cursor will not be pointing at a row after this.
    mutation_partition read_partition() {
        mutation_partition p(_schema.shared_from_this());
        do {
            p.clustered_row(_schema, position(), is_dummy(dummy()), is_continuous(continuous()))
                .apply(_schema, row().as_clustering_row());
        } while (next());
        return p;
    }

    friend std::ostream& operator<<(std::ostream& out, const partition_snapshot_row_cursor& cur) {
        out << "{cursor: position=" << cur._position << ", ";
        if (!cur.iterators_valid()) {
            return out << " iterators invalid}";
        }
        out << "current=[";
        bool first = true;
        for (auto&& v : cur._current_row) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << v.version_no;
        }
        out << "], heap=[";
        first = true;
        for (auto&& v : cur._heap) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << "{v=" << v.version_no << ", pos=" << v.it->position() << "}";
        }
        out << "], iterators=[";
        first = true;
        auto v = cur._snp.versions().begin();
        for (auto&& i : cur._iterators) {
            if (!first) {
                out << ", ";
            }
            first = false;
            if (i == v->partition().clustered_rows().end()) {
                out << "end";
            } else {
                out << i->position();
            }
            ++v;
        }
        return out << "]}";
    };
};

inline
bool partition_snapshot_row_cursor::is_in_latest_version() const {
    return _current_row[0].version_no == 0;
}

inline
partition_snapshot_row_weakref::partition_snapshot_row_weakref(const partition_snapshot_row_cursor& c)
    : _it(c._current_row[0].it)
    , _change_mark(c._change_mark)
    , _pos(c._position)
{ }

inline
partition_snapshot_row_weakref& partition_snapshot_row_weakref::operator=(const partition_snapshot_row_cursor& c) {
    auto tmp = partition_snapshot_row_weakref(c);
    this->~partition_snapshot_row_weakref();
    new (this) partition_snapshot_row_weakref(std::move(tmp));
    return *this;
}
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 Kurento (http://kurento.org/)
 *
 * 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 <gst/gst.h>

#include "loadConfig.hpp"

#include <iostream>
#include <queue>
#include <list>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <utility>

#define GST_CAT_DEFAULT kurento_load_config
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoLoadConfig"

namespace kurento
{

class ParseException : public virtual std::exception
{
public:
  ParseException(std::string message) : message(std::move(message)){};

  ~ParseException() override = default;

  const char *what() const noexcept override { return message.c_str(); };

  const std::string &getMessage() const
  {
    return message;
  };

private:
  std::string message;
};

static std::list<std::string>
split (const std::string &s, char delim)
{
  std::list<std::string> elems;
  std::stringstream ss (s);
  std::string item;

  while (std::getline (ss, item, delim) ) {
    elems.push_back (item);
  }

  return elems;
}

void
mergePropertyTrees (boost::property_tree::ptree &ptMerged,
                    const boost::property_tree::ptree &ptSecond, int level )
{
  // Value or object or array
  if (level > 0 && ptSecond.empty() ) {
    // Copy value
    ptMerged = ptSecond;
  } else if (level > 0 && ptSecond.count (std::string() ) == ptSecond.size() ) {
    // Copy array
    ptMerged = ptSecond;
  } else {
    auto it = ptSecond.begin();

    for (; it != ptSecond.end(); ++it) {
      boost::property_tree::ptree child = ptMerged.get_child (it->first.data(),
                                          boost::property_tree::ptree() );
      mergePropertyTrees (child, it->second, level + 1);

      ptMerged.erase (it->first.data() );
      ptMerged.add_child (it->first.data(), child);
    }
  }
}

static std::string
loadFile (boost::property_tree::ptree &config,
          const boost::filesystem::path &file)
{
  boost::filesystem::path extension = file.extension();
  boost::filesystem::path extension2 = file.stem().extension();
  std::string fileName = file.filename().string();
  boost::property_tree::ptree readConfig;

  if (extension2.string() == ".conf") {
    if (extension.string() == ".json") {
      boost::property_tree::read_json (file.string(), readConfig);
    } else if (extension.string() == ".info") {
      boost::property_tree::read_info (file.string(), readConfig);
    } else if (extension.string() == ".ini") {
      boost::property_tree::read_ini (file.string(), readConfig);
    } else if (extension.string() == ".xml") {
      boost::property_tree::read_xml (file.string(), readConfig);
    } else {
      throw ParseException ("Unknonw file format");
    }
  } else {
    throw ParseException ("Unknonw file format");
  }

  mergePropertyTrees (config, readConfig);

  config.put ("configPath", file.parent_path().string() );

  fileName = fileName.substr (0, fileName.size() - extension.string().size() );
  fileName = fileName.substr (0, fileName.size() - extension2.string().size() );

  return fileName;
}

static std::string
diffPathToKey (const boost::filesystem::path &path,
               const boost::filesystem::path &ancestorPath)
{
  boost::filesystem::path diffpath;

  boost::filesystem::path tmppath = path;

  while (tmppath != ancestorPath) {
    diffpath = tmppath.stem() / diffpath;
    tmppath = tmppath.parent_path();
  }

  tmppath = diffpath;

  boost::property_tree::ptree loadedConfig;
  std::string key;

  for (auto it = tmppath.begin(); it != tmppath.end(); it ++) {
    if (key.empty() ) {
      key = it->string();
    } else {
      key += "." + it->string();
    }
  }

  return key;
}

static void
loadModulesConfigFromDir (boost::property_tree::ptree &config,
                          const boost::filesystem::path &dir, const boost::filesystem::path &parentDir)
{
  GST_INFO ("Looking for config files in %s", dir.string().c_str() );

  if (!boost::filesystem::is_directory (dir) ) {
    GST_WARNING ("Unable to load config files from: %s, it is not a directory",
                 dir.string().c_str() );
    return;
  }

  boost::filesystem::directory_iterator end_itr;

  for ( boost::filesystem::directory_iterator itr ( dir ); itr != end_itr;
        ++itr ) {
    if (boost::filesystem::is_regular (*itr) ) {
      try {
        boost::property_tree::ptree moduleConfig;
        std::string fileName = loadFile (moduleConfig, itr->path() );
        std::string key = diffPathToKey (itr->path().parent_path(), parentDir);
        boost::property_tree::ptree loadedConfig;

        key = key.empty() ? "modules" :  "modules." + key;
        key += "." + fileName;

        loadedConfig.put_child (key, moduleConfig);
        mergePropertyTrees (config, loadedConfig);

        GST_INFO ("Loaded module config from: %s", itr->path().string().c_str() );
      } catch (ParseException &e) {
        // Ignore this exceptions here
      }
    } else if (boost::filesystem::is_directory (*itr) ) {
      loadModulesConfigFromDir (config, itr->path(), parentDir);
    }
  }
}

static void
loadModulesConfig (boost::property_tree::ptree &config,
                   const boost::filesystem::path &configFilePath, std::string modulesConfigPath)
{
  std::list <std::string> locations;

  if (modulesConfigPath.empty() ) {
    boost::filesystem::path modulesConfigDir = configFilePath.parent_path() /
        "modules";

    modulesConfigPath = modulesConfigDir.string();
  }

  locations = split (modulesConfigPath, ':');

  for (std::string location : locations) {
    boost::filesystem::path dir (location);
    dir.normalize();

    loadModulesConfigFromDir (config, dir, dir);
  }
}

void
loadConfig (boost::property_tree::ptree &config, const std::string &file_name,
            const std::string &modulesConfigPath)
{
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    loadFile (config, configFilePath);
  } catch (ParseException &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  } catch (boost::property_tree::ptree_error &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  }

  loadModulesConfig (config, configFilePath, modulesConfigPath);

  GST_INFO ("Configuration loaded successfully");

  std::ostringstream oss;
  boost::property_tree::write_json (oss, config, true);
  std::string infoConfig = oss.str();

  GST_INFO ("Loaded config in effect:\n%s", infoConfig.c_str() );
}

} /* kurento */

static void init_debug() __attribute__((constructor));

static void init_debug() {
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}
<commit_msg>loadConfig: Catch proper exceptions on parse error<commit_after>/*
 * (C) Copyright 2015 Kurento (http://kurento.org/)
 *
 * 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 <gst/gst.h>

#include "loadConfig.hpp"

#include <iostream>
#include <queue>
#include <list>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <utility>

#define GST_CAT_DEFAULT kurento_load_config
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoLoadConfig"

namespace kurento
{

class ParseException : public virtual std::exception
{
public:
  ParseException(std::string message) : message(std::move(message)){};

  ~ParseException() override = default;

  const char *what() const noexcept override { return message.c_str(); };

  const std::string &getMessage() const
  {
    return message;
  };

private:
  std::string message;
};

static std::list<std::string>
split (const std::string &s, char delim)
{
  std::list<std::string> elems;
  std::stringstream ss (s);
  std::string item;

  while (std::getline (ss, item, delim) ) {
    elems.push_back (item);
  }

  return elems;
}

void
mergePropertyTrees (boost::property_tree::ptree &ptMerged,
                    const boost::property_tree::ptree &ptSecond, int level )
{
  // Value or object or array
  if (level > 0 && ptSecond.empty() ) {
    // Copy value
    ptMerged = ptSecond;
  } else if (level > 0 && ptSecond.count (std::string() ) == ptSecond.size() ) {
    // Copy array
    ptMerged = ptSecond;
  } else {
    auto it = ptSecond.begin();

    for (; it != ptSecond.end(); ++it) {
      boost::property_tree::ptree child = ptMerged.get_child (it->first.data(),
                                          boost::property_tree::ptree() );
      mergePropertyTrees (child, it->second, level + 1);

      ptMerged.erase (it->first.data() );
      ptMerged.add_child (it->first.data(), child);
    }
  }
}

static std::string
loadFile (boost::property_tree::ptree &config,
          const boost::filesystem::path &file)
{
  boost::filesystem::path extension = file.extension();
  boost::filesystem::path extension2 = file.stem().extension();
  std::string fileName = file.filename().string();
  boost::property_tree::ptree readConfig;

  if (extension2.string() == ".conf") {
    if (extension.string() == ".json") {
      try {
        boost::property_tree::read_json (file.string(), readConfig);
      } catch (boost::property_tree::json_parser_error &e) {
        throw ParseException (std::string("Parse error: ") + e.what());
      }
    } else if (extension.string() == ".info") {
      try {
        boost::property_tree::read_info (file.string(), readConfig);
      } catch (boost::property_tree::info_parser_error &e) {
        throw ParseException (std::string("Parse error: ") + e.what());
      }
    } else if (extension.string() == ".ini") {
      try {
        boost::property_tree::read_ini (file.string(), readConfig);
      } catch (boost::property_tree::ini_parser_error &e) {
        throw ParseException (std::string("Parse error: ") + e.what());
      }
    } else if (extension.string() == ".xml") {
      try {
        boost::property_tree::read_xml (file.string(), readConfig);
      } catch (boost::property_tree::xml_parser_error &e) {
        throw ParseException (std::string("Parse error: ") + e.what());
      }
    } else {
      throw ParseException ("Unknown file format");
    }
  } else {
    throw ParseException ("Unknown file format");
  }

  mergePropertyTrees (config, readConfig);

  config.put ("configPath", file.parent_path().string() );

  fileName = fileName.substr (0, fileName.size() - extension.string().size() );
  fileName = fileName.substr (0, fileName.size() - extension2.string().size() );

  return fileName;
}

static std::string
diffPathToKey (const boost::filesystem::path &path,
               const boost::filesystem::path &ancestorPath)
{
  boost::filesystem::path diffpath;

  boost::filesystem::path tmppath = path;

  while (tmppath != ancestorPath) {
    diffpath = tmppath.stem() / diffpath;
    tmppath = tmppath.parent_path();
  }

  tmppath = diffpath;

  boost::property_tree::ptree loadedConfig;
  std::string key;

  for (auto it = tmppath.begin(); it != tmppath.end(); it ++) {
    if (key.empty() ) {
      key = it->string();
    } else {
      key += "." + it->string();
    }
  }

  return key;
}

static void
loadModulesConfigFromDir (boost::property_tree::ptree &config,
                          const boost::filesystem::path &dir, const boost::filesystem::path &parentDir)
{
  GST_INFO ("Looking for config files in %s", dir.string().c_str() );

  if (!boost::filesystem::is_directory (dir) ) {
    GST_WARNING ("Unable to load config files from: %s, it is not a directory",
                 dir.string().c_str() );
    return;
  }

  boost::filesystem::directory_iterator end_itr;

  for ( boost::filesystem::directory_iterator itr ( dir ); itr != end_itr;
        ++itr ) {
    if (boost::filesystem::is_regular (*itr) ) {
      try {
        boost::property_tree::ptree moduleConfig;
        std::string fileName = loadFile (moduleConfig, itr->path() );
        std::string key = diffPathToKey (itr->path().parent_path(), parentDir);
        boost::property_tree::ptree loadedConfig;

        key = key.empty() ? "modules" :  "modules." + key;
        key += "." + fileName;

        loadedConfig.put_child (key, moduleConfig);
        mergePropertyTrees (config, loadedConfig);

        GST_INFO ("Loaded module config from: %s", itr->path().string().c_str() );
      } catch (ParseException &e) {
        GST_ERROR ("Error reading configuration: %s", e.what());
        std::cerr << "Error reading configuration: " << e.what() << std::endl;
      }
    } else if (boost::filesystem::is_directory (*itr) ) {
      loadModulesConfigFromDir (config, itr->path(), parentDir);
    }
  }
}

static void
loadModulesConfig (boost::property_tree::ptree &config,
                   const boost::filesystem::path &configFilePath, std::string modulesConfigPath)
{
  std::list <std::string> locations;

  if (modulesConfigPath.empty() ) {
    boost::filesystem::path modulesConfigDir = configFilePath.parent_path() /
        "modules";

    modulesConfigPath = modulesConfigDir.string();
  }

  locations = split (modulesConfigPath, ':');

  for (std::string location : locations) {
    boost::filesystem::path dir (location);
    dir.normalize();

    loadModulesConfigFromDir (config, dir, dir);
  }
}

void
loadConfig (boost::property_tree::ptree &config, const std::string &file_name,
            const std::string &modulesConfigPath)
{
  boost::filesystem::path configFilePath (file_name);
  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  try {
    loadFile (config, configFilePath);
  } catch (ParseException &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  } catch (boost::property_tree::ptree_error &e) {
    GST_ERROR ("Error reading configuration: %s", e.what() );
    std::cerr << "Error reading configuration: " << e.what() << std::endl;
    exit (1);
  }

  loadModulesConfig (config, configFilePath, modulesConfigPath);

  GST_INFO ("Configuration loaded successfully");

  std::ostringstream oss;
  boost::property_tree::write_json (oss, config, true);
  std::string infoConfig = oss.str();

  GST_INFO ("Loaded config in effect:\n%s", infoConfig.c_str() );
}

} /* kurento */

static void init_debug() __attribute__((constructor));

static void init_debug() {
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}
<|endoftext|>


<commit_before><commit_msg>this return value is always true<commit_after><|endoftext|>


<commit_before>#include <crow.h>
#include <json.h>
#include <mustache.h>

#include <boost/program_options.hpp>

#include <sstream>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <thread>

using namespace boost::program_options;

std::map<std::string, std::string> name_pass_map;
std::map<std::string, int> name_money_map;
std::map<std::string, std::string> name_token_map;
std::mutex money_mutex;

std::string file_to_string(const std::string& filename) {
    std::ifstream ifs(filename.c_str());
    if (!ifs.is_open())
        throw std::runtime_error("could not open JSON file : " + filename);
    return std::string(
        (std::istreambuf_iterator<char>(ifs)),
        std::istreambuf_iterator<char>());
}

std::string token_from_header(const crow::ci_map& header) {
    std::stringstream ss;
    auto it = header.find("User-Agent");
    ss << it->second;
    return ss.str();
}

void fill_user_pass(const crow::json::rvalue& val) {
    for (int i = 0; i < val["users"].size(); ++i)
        name_pass_map.insert(
            std::make_pair(
                val["users"][i]["name"].s(),
                val["users"][i]["pass"].s()));
}

void fill_user_money(const crow::json::rvalue& val) {
    for (int i = 0; i < val["users"].size(); ++i)
        name_money_map.insert(
            std::make_pair(
                val["users"][i]["name"].s(),
                (int)val["users"][i]["money"].d()));
}

int main(int ac, char** av)
{
    std::string path = "./";
    std::string data_file = "data.JSON";
    try {
        options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("input-path,i", value<std::string>(), "input path for files")
            ("data-file,d", value<std::string>(), "JSON data file")
            ;
        variables_map vm;
        store(command_line_parser(ac, av).options(desc).run(), vm);
        if (vm.count("help")) {
            std::cout << desc << std::endl;
            return 1;
        }
        if (vm.count("input-path")) {
            path = vm["input-path"].as<std::string>();
        }
        if (vm.count("data-file")) {
            data_file = vm["data-file"].as<std::string>();
        }
        data_file = path + data_file;
        crow::SimpleApp app;
        crow::mustache::set_base("./");
        std::string data_str = file_to_string("./data.JSON");
        crow::json::rvalue val = crow::json::load(data_str);
        fill_user_pass(val);
        fill_user_money(val);

        CROW_ROUTE(app, "/")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("index.html").render();
        });

        CROW_ROUTE(app, "/index.html")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("index.html").render();
        });

        CROW_ROUTE(app, "/do_stuff.js")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("do_stuff.js").render();
        });

        CROW_ROUTE(app, "/api/list/")
        ([]{
            crow::json::wvalue x;
            int i = 0;
            for (auto it : name_money_map) {
                x["message"][i]["name"] = it.first;
                x["message"][i]["money"] = it.second;
                std::string status = "offline";
                if (name_token_map.find(it.first) != name_token_map.end())
                    status = "online";
                x["message"][i]["status"] = status;
                i++;
            }
            return x;
        });

        CROW_ROUTE(app, "/api/whoami/")
        ([](const crow::request& req){
            std::string token = token_from_header(req.headers);
            for (auto ite : name_token_map) {
                if (ite.second == token) {
                    return crow::response(ite.first);
                } else {
                    std::cout << ite.second << " != " << token << std::endl;
                }
            }
            return crow::response(400, "not logged in!");
        });

        CROW_ROUTE(app, "/api/login/")
        ([](const crow::request& req){
            std::string user_name = "";
            std::string user_pass = "";
            if (req.url_params.get("user"))
                user_name = req.url_params.get("user");
            else
                return crow::response(400, "need a 'user' in the request!");
            if (req.url_params.get("pass"))
                user_pass = req.url_params.get("pass");
            else
                return crow::response(400, "need a 'pass' in the request!");
            std::string token = token_from_header(req.headers);
            auto name_pass_it = name_pass_map.find(user_name);
            if (name_pass_it == name_pass_map.end())
                return crow::response(400, "unknown 'user'!");
            if (name_pass_it->second != user_pass)
                return crow::response(500, "login failed!");
            // register new token
            name_token_map.insert(std::make_pair(user_name, token));
            auto it = name_money_map.find(user_name);
            if (it != name_money_map.end())
                return crow::response(std::to_string(it->second));
            else
                return crow::response("0");
        });

        CROW_ROUTE(app, "/api/send/")
        ([](const crow::request& req){
            std::string from_name = "";
            std::string to_name = "";
            int value = 0;
            if (req.url_params.get("from"))
                from_name = req.url_params.get("from");
            else
                return crow::response(400, "need a 'from' in the request!");
            if (req.url_params.get("to"))
                to_name = req.url_params.get("to");
            else
                return crow::response(400, "need a 'to' in the request!");
            if (req.url_params.get("value"))
                value = atoi(req.url_params.get("value"));
            else
                return crow::response(400, "need a 'value' in the request!");
            // check user has right (own the account)
            std::string token = token_from_header(req.headers);
            auto token_it = name_token_map.find(from_name);
            auto from_it = name_money_map.find(from_name);
            auto to_it = name_money_map.find(to_name);
            if (token != token_it->second)
                return crow::response(500, "invalid credential!");
            if (from_it == name_money_map.end())
                return crow::response(400, "unknown 'from' user");
            if (to_it == name_money_map.end())
                return crow::response(400, "unknown 'to' user");
            if (value < 0)
                return crow::response(400, "'value' cannot be negative");
            if (from_it == to_it)
                return crow::response(400, "'from' and 'to' are the same");
            if (value > from_it->second)
                return crow::response(400, "'from' doesn't have enough money");
            money_mutex.lock();
            from_it->second -= value;
            to_it->second += value;
            money_mutex.unlock();
            std::stringstream ss;
            ss << "sending : " << value << " from " << from_name << " to " << to_name;
            return crow::response(ss.str());
        });

        app.port(8080).multithreaded().run();
    } catch (std::exception& ex) {
        std::cerr << "exception (std) : " << ex.what() << std::endl;
        return -1;
    }
    return 0;
}
<commit_msg>added seed as a cookie<commit_after>#include <crow.h>
#include <json.h>
#include <mustache.h>

#include <boost/program_options.hpp>

#include <sstream>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <thread>
#include <random>

using namespace boost::program_options;

std::map<std::string, std::string> name_pass_map;
std::map<std::string, int> name_money_map;
std::map<std::string, std::string> name_token_map;
std::map<std::string, int> name_seed_map;
std::mutex money_mutex;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1, 65535);
auto seed_value = std::bind(distribution, generator);

std::string file_to_string(const std::string& filename) {
    std::ifstream ifs(filename.c_str());
    if (!ifs.is_open())
        throw std::runtime_error("could not open JSON file : " + filename);
    return std::string(
        (std::istreambuf_iterator<char>(ifs)),
        std::istreambuf_iterator<char>());
}

std::string token_from_header(const crow::ci_map& header) {
    std::stringstream ss;
    auto it = header.find("User-Agent");
    ss << it->second;
    return ss.str();
}

void fill_user_pass(const crow::json::rvalue& val) {
    for (int i = 0; i < val["users"].size(); ++i)
        name_pass_map.insert(
            std::make_pair(
                val["users"][i]["name"].s(),
                val["users"][i]["pass"].s()));
}

void fill_user_money(const crow::json::rvalue& val) {
    for (int i = 0; i < val["users"].size(); ++i)
        name_money_map.insert(
            std::make_pair(
                val["users"][i]["name"].s(),
                (int)val["users"][i]["money"].d()));
}

int main(int ac, char** av)
{
    std::string path = "./";
    std::string data_file = "data.JSON";
    try {
        options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("input-path,i", value<std::string>(), "input path for files")
            ("data-file,d", value<std::string>(), "JSON data file")
            ;
        variables_map vm;
        store(command_line_parser(ac, av).options(desc).run(), vm);
        if (vm.count("help")) {
            std::cout << desc << std::endl;
            return 1;
        }
        if (vm.count("input-path")) {
            path = vm["input-path"].as<std::string>();
        }
        if (vm.count("data-file")) {
            data_file = vm["data-file"].as<std::string>();
        }
        data_file = path + data_file;
        crow::SimpleApp app;
        crow::mustache::set_base("./");
        std::string data_str = file_to_string("./data.JSON");
        crow::json::rvalue val = crow::json::load(data_str);
        fill_user_pass(val);
        fill_user_money(val);

        CROW_ROUTE(app, "/")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("index.html").render();
        });

        CROW_ROUTE(app, "/index.html")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("index.html").render();
        });

        CROW_ROUTE(app, "/do_stuff.js")
        ([]{
            crow::mustache::context ctx;
            return crow::mustache::load("do_stuff.js").render();
        });

        CROW_ROUTE(app, "/api/list/")
        ([]{
            crow::json::wvalue x;
            int i = 0;
            for (auto it : name_money_map) {
                x["message"][i]["name"] = it.first;
                x["message"][i]["money"] = it.second;
                std::string status = "offline";
                if (name_token_map.find(it.first) != name_token_map.end())
                    status = "online";
                x["message"][i]["status"] = status;
                i++;
            }
            return x;
        });

        CROW_ROUTE(app, "/api/login/")
        ([](const crow::request& req){
            std::string user_name = "";
            std::string user_pass = "";
            if (req.url_params.get("user"))
                user_name = req.url_params.get("user");
            else
                return crow::response(400, "need a 'user' in the request!");
            if (req.url_params.get("pass"))
                user_pass = req.url_params.get("pass");
            else
                return crow::response(400, "need a 'pass' in the request!");
            std::string token = token_from_header(req.headers);
            auto name_pass_it = name_pass_map.find(user_name);
            if (name_pass_it == name_pass_map.end())
                return crow::response(400, "unknown 'user'!");
            if (name_pass_it->second != user_pass)
                return crow::response(500, "login failed!");
            // register new token
            name_seed_map.insert(std::make_pair(user_name, seed_value()));
            name_token_map.insert(std::make_pair(user_name, token));
            auto money_it = name_money_map.find(user_name);
            auto seed_it = name_seed_map.find(user_name);
            crow::json::wvalue jv;
            jv["money"] = money_it->second;
            jv["seed"] = seed_it->second;
            return crow::response(jv);
        });

        CROW_ROUTE(app, "/api/send/")
        ([](const crow::request& req){
            std::string from_name = "";
            std::string to_name = "";
            int value = 0;
            int seed = 0;
            if (req.url_params.get("from"))
                from_name = req.url_params.get("from");
            else
                return crow::response(400, "HACKER!!!!");
            if (req.url_params.get("to"))
                to_name = req.url_params.get("to");
            else
                return crow::response(400, "HACKER!!!!");
            if (req.url_params.get("value"))
                value = atoi(req.url_params.get("value"));
            else
                return crow::response(400, "HACKER!!!!");
            if (req.url_params.get("seed"))
                seed = atoi(req.url_params.get("seed"));
            else
                return crow::response(400, "HACKER!!!!");
            // check user has right (own the account)
            std::string token = token_from_header(req.headers);
            auto token_it = name_token_map.find(from_name);
            auto from_it = name_money_map.find(from_name);
            auto to_it = name_money_map.find(to_name);
            auto seed_it = name_seed_map.find(from_name);
            if (seed != seed_it->second)
                return crow::response(500, "HACKER!!!!");
            if (token != token_it->second)
                return crow::response(500, "invalid credential!");
            if (from_it == name_money_map.end())
                return crow::response(400, "HACKER!!!!");
            if (to_it == name_money_map.end())
                return crow::response(400, "unknown 'to' user");
            if (value < 0)
                return crow::response(400, "HACKER!!!!");
            if (from_it == to_it)
                return crow::response(400, "'from' and 'to' are the same");
            if (value > from_it->second)
                return crow::response(400, "'from' doesn't have enough money");
            money_mutex.lock();
            from_it->second -= value;
            to_it->second += value;
            money_mutex.unlock();
            std::stringstream ss;
            ss << "sending : " << value << " from " << from_name << " to " << to_name;
            return crow::response(ss.str());
        });

        app.port(8080).multithreaded().run();
    } catch (std::exception& ex) {
        std::cerr << "exception (std) : " << ex.what() << std::endl;
        return -1;
    }
    return 0;
}
<|endoftext|>


<commit_before>#ifndef SERVER_NETCOM_HPP
#define SERVER_NETCOM_HPP

#include <netcom_base.hpp>
#include <SFML/Network.hpp>

namespace server {
    class netcom : public netcom_base {
    public :
        netcom();
        ~netcom();

        /// Define the maximum number of simultaneously connected clients.
        /** If the limit is decreased and there are more connected clients than what is now allowed,
            no client will be disconnected. This will only prevent new clients to connect, until the
            new client limit is reached.
        **/
        void set_max_client(std::size_t max_client);

        /// Will return true as long as the server is listening to incoming connections.
        bool is_connected() const;

        /// Start the server, listening to the given port.
        void run(std::uint16_t port);

        /// Shut down the server cleanly (called by the destructor).
        /** Calling this function on a server that is not running has no effect.
        **/
        void terminate();

    private :
        struct client_t {
            std::unique_ptr<sf::TcpSocket> socket;
            actor_id_t                     id;
        };

        using client_list_t = sorted_vector<client_t, mem_var_comp(&client_t::id)>;

        void loop_();
        bool make_id_(actor_id_t& id);
        void free_id_(actor_id_t id);
        void remove_client_(actor_id_t cid);
        void remove_client_(client_list_t::iterator ic);

        std::uint16_t      listen_port_;
        std::size_t        max_client_;
        sf::TcpListener    listener_;
        sf::SocketSelector selector_;

        client_list_t             clients_;
        sorted_vector<actor_id_t, std::greater<request_id_t>> available_ids_;

        std::atomic<bool> is_connected_;
        std::atomic<bool> terminate_thread_;
        sf::Thread listener_thread_;
    };
}

namespace message {
namespace server {
    namespace internal {
        ID_STRUCT(cannot_listen_port) {
            using types = type_list<std::uint16_t>;
        };

        ID_STRUCT(start_listening_port) {
            using types = type_list<std::uint16_t>;
        };

        ID_STRUCT(unknown_client) {
            using types = type_list<std::uint16_t>;
        };

        ID_STRUCT(client_connected) {
            using types = type_list<std::uint16_t, std::string>;
        };

        ID_STRUCT(client_disconnected) {
            enum class reason : std::uint8_t {
                connection_lost
            };

            using types = type_list<std::uint16_t, reason>;
        };
    }

    ID_STRUCT(connection_established) {
        using types = type_list<>;
    };

    ID_STRUCT(connection_failed) {
        enum class reason : std::uint8_t {
            cannot_authenticate,
            disconnected,
            timed_out
        };

        using types = type_list<reason>;
    };

    ID_STRUCT(connection_denied) {
        enum class reason : std::uint8_t {
            too_many_clients,
            unexpected_packet
        };

        using types = type_list<reason>;
    };

    ID_STRUCT(connection_granted) {
        using types = type_list<>;
    };
}
}

#endif
<commit_msg>Fixed using std::uint16_t instead of actor_id_t.<commit_after>#ifndef SERVER_NETCOM_HPP
#define SERVER_NETCOM_HPP

#include <netcom_base.hpp>
#include <SFML/Network.hpp>

namespace server {
    class netcom : public netcom_base {
    public :
        netcom();
        ~netcom();

        /// Define the maximum number of simultaneously connected clients.
        /** If the limit is decreased and there are more connected clients than what is now allowed,
            no client will be disconnected. This will only prevent new clients to connect, until the
            new client limit is reached.
        **/
        void set_max_client(std::size_t max_client);

        /// Will return true as long as the server is listening to incoming connections.
        bool is_connected() const;

        /// Start the server, listening to the given port.
        void run(std::uint16_t port);

        /// Shut down the server cleanly (called by the destructor).
        /** Calling this function on a server that is not running has no effect.
        **/
        void terminate();

    private :
        struct client_t {
            std::unique_ptr<sf::TcpSocket> socket;
            actor_id_t                     id;
        };

        using client_list_t = sorted_vector<client_t, mem_var_comp(&client_t::id)>;

        void loop_();
        bool make_id_(actor_id_t& id);
        void free_id_(actor_id_t id);
        void remove_client_(actor_id_t cid);
        void remove_client_(client_list_t::iterator ic);

        std::uint16_t      listen_port_;
        std::size_t        max_client_;
        sf::TcpListener    listener_;
        sf::SocketSelector selector_;

        client_list_t             clients_;
        sorted_vector<actor_id_t, std::greater<request_id_t>> available_ids_;

        std::atomic<bool> is_connected_;
        std::atomic<bool> terminate_thread_;
        sf::Thread listener_thread_;
    };
}

namespace message {
namespace server {
    namespace internal {
        ID_STRUCT(cannot_listen_port) {
            using types = type_list<std::uint16_t>;
        };

        ID_STRUCT(start_listening_port) {
            using types = type_list<std::uint16_t>;
        };

        ID_STRUCT(unknown_client) {
            using types = type_list<actor_id_t>;
        };

        ID_STRUCT(client_connected) {
            using types = type_list<actor_id_t, std::string>;
        };

        ID_STRUCT(client_disconnected) {
            enum class reason : std::uint8_t {
                connection_lost
            };

            using types = type_list<actor_id_t, reason>;
        };
    }

    ID_STRUCT(connection_established) {
        using types = type_list<>;
    };

    ID_STRUCT(connection_failed) {
        enum class reason : std::uint8_t {
            cannot_authenticate,
            disconnected,
            timed_out
        };

        using types = type_list<reason>;
    };

    ID_STRUCT(connection_denied) {
        enum class reason : std::uint8_t {
            too_many_clients,
            unexpected_packet
        };

        using types = type_list<reason>;
    };

    ID_STRUCT(connection_granted) {
        using types = type_list<>;
    };
}
}

#endif
<|endoftext|>


<commit_before>#include <dbc/Builder.h>
#include <dbc/ArgsControl.h>

#define ArgError(Message, ...) \
	fprintf(stderr, Message, ##__VA_ARGS__); \
	exit(-1);

int main(int argc, const char* argv[])
{
	using namespace dbc;
#if _DEBUG || DEBUG
	gen::ASM6502CG Generator(false);
	Builder::Build("test/main.dbs", &Generator, true, true);
	Builder::SaveSourceFile("out.s65");
	Builder::Destoy();
	PAUSE;
#else
	if (argc > 1)
	{
		bool Verbose = false;
		bool HelpPrinted = false;
		const char* MainFile = nullptr;
		const char* OutFile = nullptr;
		const char* Gen = nullptr;
		CodeGenerator* Generator = nullptr;
		int Index;
		for (Index = 1; Index < argc; ++Index)
		{
			ArgsControl::Command Cmd = ArgsControl::GeCommand(argv[Index]);
			switch (Cmd)
			{
				case ArgsControl::Command::HELP:
					printf("=======================================\n");
					printf("DamnBASIC Compiler v0.0.1\n");
					printf("=======================================\n");
					printf("Author: Felipe Alfonso.\n");
					printf("Url: https://github.com/vptr/DAMNBASIC/\n");
					printf("=======================================\n");
					printf("Usage:\n");
					printf("\tdbc -[command] [argument]\n");
					printf("\nExample:\n");
					printf("\tdbc -i main.dbs -t 6502 -o output.asm\n");
					printf("\nCommands:\n");
					printf("\t-i Input file.\n");
					printf("\t-t Target [6502].\n");
					printf("\t-o Output file name (optional).\n");
					printf("\t-v Verbose print (optional).\n");
					HelpPrinted = true;
					break;
					break;
				case ArgsControl::Command::ADDFILE:
					if (Index + 1 < argc)
					{
						MainFile = argv[++Index];
						continue;
					}
					break;
				case ArgsControl::Command::VERBOSE:
					Verbose = true;
					continue;
					break;
				case ArgsControl::Command::TARGET:
					if (Index + 1 < argc)
					{
						Gen = argv[++Index];
						continue;
					}
				case ArgsControl::Command::OUTPUT:
					if (Index + 1 < argc && OutFile == nullptr)
					{
						OutFile = argv[++Index];
						continue;
					}
					continue;
					break;
				case ArgsControl::Command::NONE:
					ArgError("Invalid Argument %s", argv[Index]);
					break;
				default:
					ArgError("Invalid Argument %s", argv[Index]);
					break;
			}
		}
		if (!HelpPrinted)
		{
			GenType GType = ArgsControl::GetGenType(Gen);
			Generator = ArgsControl::GetGenerator(GType, Verbose);
			if (Generator == nullptr)
			{
				ArgError("Invalid target");
			}
			if (MainFile == nullptr)
			{
				ArgError("Missing input file.");
			}
			Builder::Build(MainFile, Generator, true, Verbose);
			if (OutFile == nullptr)
			{
				Builder::SaveSourceFile((std::string("out.") + ArgsControl::GetExt(GType)).c_str());
			}
			else
			{
				Builder::SaveSourceFile(OutFile);
			}
			Builder::Destoy();
		}
	}
	else
	{
		ArgError("Missing Input file.");
	}
#endif
	return 0;
}<commit_msg>debug verbose<commit_after>#include <dbc/Builder.h>
#include <dbc/ArgsControl.h>

#define ArgError(Message, ...) \
	fprintf(stderr, Message, ##__VA_ARGS__); \
	exit(-1);

int main(int argc, const char* argv[])
{
	using namespace dbc;
#if _DEBUG || DEBUG
	gen::ASM6502CG Generator(true);
	Builder::Build("test/main.dbs", &Generator, true, true);
	Builder::SaveSourceFile("out.s65");
	Builder::Destoy();
	PAUSE;
#else
	if (argc > 1)
	{
		bool Verbose = false;
		bool HelpPrinted = false;
		const char* MainFile = nullptr;
		const char* OutFile = nullptr;
		const char* Gen = nullptr;
		CodeGenerator* Generator = nullptr;
		int Index;
		for (Index = 1; Index < argc; ++Index)
		{
			ArgsControl::Command Cmd = ArgsControl::GeCommand(argv[Index]);
			switch (Cmd)
			{
				case ArgsControl::Command::HELP:
					printf("=======================================\n");
					printf("DamnBASIC Compiler v0.0.1\n");
					printf("=======================================\n");
					printf("Author: Felipe Alfonso.\n");
					printf("Url: https://github.com/vptr/DAMNBASIC/\n");
					printf("=======================================\n");
					printf("Usage:\n");
					printf("\tdbc -[command] [argument]\n");
					printf("\nExample:\n");
					printf("\tdbc -i main.dbs -t 6502 -o output.asm\n");
					printf("\nCommands:\n");
					printf("\t-i Input file.\n");
					printf("\t-t Target [6502].\n");
					printf("\t-o Output file name (optional).\n");
					printf("\t-v Verbose print (optional).\n");
					HelpPrinted = true;
					break;
					break;
				case ArgsControl::Command::ADDFILE:
					if (Index + 1 < argc)
					{
						MainFile = argv[++Index];
						continue;
					}
					break;
				case ArgsControl::Command::VERBOSE:
					Verbose = true;
					continue;
					break;
				case ArgsControl::Command::TARGET:
					if (Index + 1 < argc)
					{
						Gen = argv[++Index];
						continue;
					}
				case ArgsControl::Command::OUTPUT:
					if (Index + 1 < argc && OutFile == nullptr)
					{
						OutFile = argv[++Index];
						continue;
					}
					continue;
					break;
				case ArgsControl::Command::NONE:
					ArgError("Invalid Argument %s", argv[Index]);
					break;
				default:
					ArgError("Invalid Argument %s", argv[Index]);
					break;
			}
		}
		if (!HelpPrinted)
		{
			GenType GType = ArgsControl::GetGenType(Gen);
			Generator = ArgsControl::GetGenerator(GType, Verbose);
			if (Generator == nullptr)
			{
				ArgError("Invalid target");
			}
			if (MainFile == nullptr)
			{
				ArgError("Missing input file.");
			}
			Builder::Build(MainFile, Generator, true, Verbose);
			if (OutFile == nullptr)
			{
				Builder::SaveSourceFile((std::string("out.") + ArgsControl::GetExt(GType)).c_str());
			}
			else
			{
				Builder::SaveSourceFile(OutFile);
			}
			Builder::Destoy();
		}
	}
	else
	{
		ArgError("Missing Input file.");
	}
#endif
	return 0;
}<|endoftext|>


<commit_before>/*
   The MIT License (MIT)

   Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>

   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.
*/

#ifndef __POLICY_HPP__
#define __POLICY_HPP__

#include <string>
#include <vector>
#include <map>

#include "path.hpp"
#include "fs.hpp"
#include "category.hpp"

namespace mergerfs
{
  class Policy
  {
  public:
    struct Enum
    {
      enum Type
        {
          invalid = -1,
          BEGIN   = 0,
          all     = BEGIN,
          einval,
          enosys,
          enotsup,
          epmfs,
          erofs,
          exdev,
          ff,
          ffwp,
          fwfs,
          lfs,
          mfs,
          newest,
          rand,
          END
        };
    };

    struct Func
    {
      typedef std::string string;
      typedef std::size_t size_t;
      typedef std::vector<string> strvec;
      typedef const string cstring;
      typedef const size_t csize_t;
      typedef const strvec cstrvec;
      typedef const Category::Enum::Type CType;

      typedef int (*Ptr)(CType,cstrvec&,cstring&,csize_t,strvec&);

      class Action
      {
      public:
        Action(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::action,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      class Create
      {
      public:
        Create(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::create,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      class Search
      {
      public:
        Search(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::search,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      static int all(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int einval(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int enosys(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int enotsup(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int epmfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int erofs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int exdev(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int ff(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int ffwp(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int fwfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int invalid(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int lfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int mfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int newest(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int rand(CType,cstrvec&,cstring&,csize_t,strvec&);
    };

  private:
    Enum::Type  _enum;
    std::string _str;
    Func::Ptr   _func;

  public:
    Policy()
      : _enum(invalid),
        _str(invalid),
        _func(invalid)
    {
    }

    Policy(const Enum::Type   enum_,
           const std::string &str_,
           const Func::Ptr    func_)
      : _enum(enum_),
        _str(str_),
        _func(func_)
    {
    }

  public:
    operator const Enum::Type() const { return _enum; }
    operator const std::string&() const { return _str; }
    operator const Func::Ptr() const { return _func; }
    operator const Policy*() const { return this; }

    bool operator==(const Enum::Type enum_) const
    { return _enum == enum_; }
    bool operator==(const std::string &str_) const
    { return _str == str_; }
    bool operator==(const Func::Ptr func_) const
    { return _func == func_; }

    bool operator!=(const Policy &r) const
    { return _enum != r._enum; }

    bool operator<(const Policy &r) const
    { return _enum < r._enum; }

  public:
    static const Policy &find(const std::string&);
    static const Policy &find(const Enum::Type);

  public:
    static const std::vector<Policy>  _policies_;
    static const Policy * const       policies;

    static const Policy &invalid;
    static const Policy &all;
    static const Policy &enosys;
    static const Policy &enotsup;
    static const Policy &epmfs;
    static const Policy &erofs;
    static const Policy &exdev;
    static const Policy &ff;
    static const Policy &ffwp;
    static const Policy &fwfs;
    static const Policy &lfs;
    static const Policy &mfs;
    static const Policy &newest;
    static const Policy &rand;
  };
}

#endif
<commit_msg>forgot to add einval to policy<commit_after>/*
   The MIT License (MIT)

   Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>

   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.
*/

#ifndef __POLICY_HPP__
#define __POLICY_HPP__

#include <string>
#include <vector>
#include <map>

#include "path.hpp"
#include "fs.hpp"
#include "category.hpp"

namespace mergerfs
{
  class Policy
  {
  public:
    struct Enum
    {
      enum Type
        {
          invalid = -1,
          BEGIN   = 0,
          all     = BEGIN,
          einval,
          enosys,
          enotsup,
          epmfs,
          erofs,
          exdev,
          ff,
          ffwp,
          fwfs,
          lfs,
          mfs,
          newest,
          rand,
          END
        };
    };

    struct Func
    {
      typedef std::string string;
      typedef std::size_t size_t;
      typedef std::vector<string> strvec;
      typedef const string cstring;
      typedef const size_t csize_t;
      typedef const strvec cstrvec;
      typedef const Category::Enum::Type CType;

      typedef int (*Ptr)(CType,cstrvec&,cstring&,csize_t,strvec&);

      class Action
      {
      public:
        Action(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::action,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      class Create
      {
      public:
        Create(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::create,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      class Search
      {
      public:
        Search(const Policy *p)
          : func(p->_func)
        {}

        int
        operator()(cstrvec& b,cstring& c,csize_t d,strvec& e)
        {
          return func(Category::Enum::search,b,c,d,e);
        }

      private:
        const Ptr func;
      };

      static int all(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int einval(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int enosys(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int enotsup(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int epmfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int erofs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int exdev(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int ff(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int ffwp(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int fwfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int invalid(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int lfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int mfs(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int newest(CType,cstrvec&,cstring&,csize_t,strvec&);
      static int rand(CType,cstrvec&,cstring&,csize_t,strvec&);
    };

  private:
    Enum::Type  _enum;
    std::string _str;
    Func::Ptr   _func;

  public:
    Policy()
      : _enum(invalid),
        _str(invalid),
        _func(invalid)
    {
    }

    Policy(const Enum::Type   enum_,
           const std::string &str_,
           const Func::Ptr    func_)
      : _enum(enum_),
        _str(str_),
        _func(func_)
    {
    }

  public:
    operator const Enum::Type() const { return _enum; }
    operator const std::string&() const { return _str; }
    operator const Func::Ptr() const { return _func; }
    operator const Policy*() const { return this; }

    bool operator==(const Enum::Type enum_) const
    { return _enum == enum_; }
    bool operator==(const std::string &str_) const
    { return _str == str_; }
    bool operator==(const Func::Ptr func_) const
    { return _func == func_; }

    bool operator!=(const Policy &r) const
    { return _enum != r._enum; }

    bool operator<(const Policy &r) const
    { return _enum < r._enum; }

  public:
    static const Policy &find(const std::string&);
    static const Policy &find(const Enum::Type);

  public:
    static const std::vector<Policy>  _policies_;
    static const Policy * const       policies;

    static const Policy &invalid;
    static const Policy &all;
    static const Policy &einval;
    static const Policy &enosys;
    static const Policy &enotsup;
    static const Policy &epmfs;
    static const Policy &erofs;
    static const Policy &exdev;
    static const Policy &ff;
    static const Policy &ffwp;
    static const Policy &fwfs;
    static const Policy &lfs;
    static const Policy &mfs;
    static const Policy &newest;
    static const Policy &rand;
  };
}

#endif
<|endoftext|>


<commit_before>#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <fnmatch.h>
#include <stdarg.h>

#include "pen.h"
#include "file_system.h"
#include "memory.h"
#include "pen_string.h"

extern pen::user_info pen_user_info;

#ifdef __linux__
#define get_mtime(s) s.st_mtime
#else
#define get_mtime(s) s.st_mtimespec.tv_sec
#endif

namespace pen
{
    pen_error filesystem_read_file_to_buffer( const char* filename, void** p_buffer, u32 &buffer_size )
    {
        *p_buffer = NULL;

        FILE* p_file = fopen( filename, "rb" );

        if( p_file )
        {
            fseek( p_file, 0L, SEEK_END );
            long size = ftell( p_file );

            fseek( p_file, 0L, SEEK_SET );

            buffer_size = ( u32 ) size;

            *p_buffer = pen::memory_alloc( buffer_size+1 );

            fread( *p_buffer, 1, buffer_size, p_file );

            ((u8*)*p_buffer)[buffer_size] = '\0';

            fclose( p_file );

            return PEN_ERR_OK;
        }

        return PEN_ERR_FILE_NOT_FOUND;
    }

    pen_error filesystem_enum_volumes( fs_tree_node &results )
    {
#ifndef __linux__
        struct statfs* mounts;
        int num_mounts = getmntinfo(&mounts, MNT_WAIT);

        results.children = (fs_tree_node*)pen::memory_alloc( sizeof(fs_tree_node) * num_mounts );
        results.num_children = num_mounts;pen_user_info

        static const c8* volumes_name = "Volumes";

        u32 len = pen::string_length(volumes_name);
        results.name = (c8*)pen::memory_alloc( len + 1 );
        pen::memory_cpy(results.name, volumes_name, len);
        results.name[ len ] = '\0';

        for( int i = 0; i < num_mounts; ++i )
        {
            len = pen::string_length( mounts[i].f_mntonname );
            results.children[i].name = (c8*)pen::memory_alloc( len + 1 );

            pen::memory_cpy(results.children[i].name, mounts[i].f_mntonname, len);
            results.children[i].name[len] = '\0';

            results.children[i].children = nullptr;
            results.children[i].num_children = 0;
        }
#endif
        return PEN_ERR_OK;
    }

    bool match_file( struct dirent *ent, s32 num_wildcards, va_list wildcards )
    {
        if( num_wildcards <= 0 )
        {
            return true;
        }

        va_list wildcards_consume;
        va_copy(wildcards_consume, wildcards);

        if( ent->d_type != DT_DIR )
        {
            for( s32 i = 0; i < num_wildcards; ++i )
            {
                const char* ft = va_arg(wildcards_consume, const char*);

                if( fnmatch( ft, ent->d_name, 0) == 0 )
                {
                    return true;
                }
            }
        }
        else
        {
            return true;
        }

        return false;
    }

    pen_error filesystem_enum_directory( const c8* directory, fs_tree_node &results, s32 num_wildcards, ... )
    {
        va_list wc;
        va_start ( wc, num_wildcards );

        pen_error res = filesystem_enum_directory( directory, results, num_wildcards, wc );

        va_end( wc );

        return res;
    }

    pen_error filesystem_enum_directory( const c8* directory, fs_tree_node &results, s32 num_wildcards, va_list wildcards )
    {
        DIR *dir;
        struct dirent *ent;

        u32 num_items = 0;
        if ((dir = opendir (directory)) != NULL)
        {
            while ((ent = readdir (dir)) != NULL)
            {
                if( match_file( ent, num_wildcards, wildcards ) )
                {
                    num_items++;
                }
            }

            closedir (dir);
        }

        if( num_items == 0 )
        {

            return PEN_ERR_FILE_NOT_FOUND;
        }

        if( results.children == nullptr )
        {
            //alloc new mem
            results.children = (fs_tree_node*)pen::memory_alloc( sizeof(fs_tree_node) * num_items );
            pen::memory_zero(results.children, sizeof(fs_tree_node) * num_items );
        }
        else
        {
            //grow buffer
            if( results.num_children < num_items )
            {
                results.children = (fs_tree_node*)pen::memory_realloc( results.children, sizeof(fs_tree_node) * num_items );
            }
        }

        results.num_children = num_items;

        u32 i = 0;
        if ((dir = opendir (directory)) != NULL)
        {
            while ((ent = readdir (dir)) != NULL)
            {
                if( match_file( ent, num_wildcards, wildcards ) )
                {
                    if( results.children[i].name == nullptr )
                    {
                        //allocate 1024 file buffer
                        results.children[i].name = (c8*)pen::memory_alloc( 1024 );
                        pen::memory_zero(results.children[i].name, 1024);
                    }

                    u32 len = pen::string_length( ent->d_name );
                    len = min<u32>( len, 1022 );

                    pen::memory_cpy(results.children[i].name, ent->d_name, len);
                    results.children[i].name[len] = '\0';

                    results.children[i].num_children = 0;

                    ++i;
                }
            }

            closedir (dir);
        }

        return PEN_ERR_OK;
    }

    pen_error filesystem_enum_free_mem( fs_tree_node &tree )
    {
        for( s32 i = 0; i < tree.num_children; ++i )
        {
            filesystem_enum_free_mem( tree.children[ i ] );
        }

        pen::memory_free( tree.children );
        pen::memory_free( tree.name );

        return PEN_ERR_OK;
    }

    pen_error filesystem_getmtime( const c8* filename, u32& mtime_out )
    {
        struct stat stat_res;

        stat( filename, &stat_res);

        mtime_out = get_mtime(stat_res);

        return PEN_ERR_OK;
    }

    const c8** filesystem_get_user_directory( s32& directory_depth )
    {
        static const u32 max_dir_depth = 3;

        static c8 default_dir[max_dir_depth][1024];

        static c8* dir_list[max_dir_depth];

        pen::string_format(default_dir[0], 1024, "/" );
        pen::string_format(default_dir[1], 1024, "Users" );
        pen::string_format(default_dir[2], 1024, "%s", pen_user_info.user_name );

        for( s32 i = 0; i < max_dir_depth; ++i )
        {
            dir_list[ i ] = &default_dir[ i ][ 0 ];
        }

        directory_depth = max_dir_depth;

        return (const c8**)&dir_list[0];
    }

	s32 filesystem_exclude_slash_depth()
	{
		//directory depth 0 can be a slash
		return 0;
	}
}
<commit_msg>compile fixes osx<commit_after>#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <fnmatch.h>
#include <stdarg.h>

#include "pen.h"
#include "file_system.h"
#include "memory.h"
#include "pen_string.h"

extern pen::user_info pen_user_info;

#ifdef __linux__
#define get_mtime(s) s.st_mtime
#else
#define get_mtime(s) s.st_mtimespec.tv_sec
#endif

namespace pen
{
    pen_error filesystem_read_file_to_buffer( const char* filename, void** p_buffer, u32 &buffer_size )
    {
        *p_buffer = NULL;

        FILE* p_file = fopen( filename, "rb" );

        if( p_file )
        {
            fseek( p_file, 0L, SEEK_END );
            long size = ftell( p_file );

            fseek( p_file, 0L, SEEK_SET );

            buffer_size = ( u32 ) size;

            *p_buffer = pen::memory_alloc( buffer_size+1 );

            fread( *p_buffer, 1, buffer_size, p_file );

            ((u8*)*p_buffer)[buffer_size] = '\0';

            fclose( p_file );

            return PEN_ERR_OK;
        }

        return PEN_ERR_FILE_NOT_FOUND;
    }

    pen_error filesystem_enum_volumes( fs_tree_node &results )
    {
#ifndef __linux__
        struct statfs* mounts;
        int num_mounts = getmntinfo(&mounts, MNT_WAIT);

        results.children = (fs_tree_node*)pen::memory_alloc( sizeof(fs_tree_node) * num_mounts );
        results.num_children = num_mounts;

        static const c8* volumes_name = "Volumes";

        u32 len = pen::string_length(volumes_name);
        results.name = (c8*)pen::memory_alloc( len + 1 );
        pen::memory_cpy(results.name, volumes_name, len);
        results.name[ len ] = '\0';

        for( int i = 0; i < num_mounts; ++i )
        {
            len = pen::string_length( mounts[i].f_mntonname );
            results.children[i].name = (c8*)pen::memory_alloc( len + 1 );

            pen::memory_cpy(results.children[i].name, mounts[i].f_mntonname, len);
            results.children[i].name[len] = '\0';

            results.children[i].children = nullptr;
            results.children[i].num_children = 0;
        }
#endif
        return PEN_ERR_OK;
    }

    bool match_file( struct dirent *ent, s32 num_wildcards, va_list wildcards )
    {
        if( num_wildcards <= 0 )
        {
            return true;
        }

        va_list wildcards_consume;
        va_copy(wildcards_consume, wildcards);

        if( ent->d_type != DT_DIR )
        {
            for( s32 i = 0; i < num_wildcards; ++i )
            {
                const char* ft = va_arg(wildcards_consume, const char*);

                if( fnmatch( ft, ent->d_name, 0) == 0 )
                {
                    return true;
                }
            }
        }
        else
        {
            return true;
        }

        return false;
    }

    pen_error filesystem_enum_directory( const c8* directory, fs_tree_node &results, s32 num_wildcards, ... )
    {
        va_list wc;
        va_start ( wc, num_wildcards );

        pen_error res = filesystem_enum_directory( directory, results, num_wildcards, wc );

        va_end( wc );

        return res;
    }

    pen_error filesystem_enum_directory( const c8* directory, fs_tree_node &results, s32 num_wildcards, va_list wildcards )
    {
        DIR *dir;
        struct dirent *ent;

        u32 num_items = 0;
        if ((dir = opendir (directory)) != NULL)
        {
            while ((ent = readdir (dir)) != NULL)
            {
                if( match_file( ent, num_wildcards, wildcards ) )
                {
                    num_items++;
                }
            }

            closedir (dir);
        }

        if( num_items == 0 )
        {

            return PEN_ERR_FILE_NOT_FOUND;
        }

        if( results.children == nullptr )
        {
            //alloc new mem
            results.children = (fs_tree_node*)pen::memory_alloc( sizeof(fs_tree_node) * num_items );
            pen::memory_zero(results.children, sizeof(fs_tree_node) * num_items );
        }
        else
        {
            //grow buffer
            if( results.num_children < num_items )
            {
                results.children = (fs_tree_node*)pen::memory_realloc( results.children, sizeof(fs_tree_node) * num_items );
            }
        }

        results.num_children = num_items;

        u32 i = 0;
        if ((dir = opendir (directory)) != NULL)
        {
            while ((ent = readdir (dir)) != NULL)
            {
                if( match_file( ent, num_wildcards, wildcards ) )
                {
                    if( results.children[i].name == nullptr )
                    {
                        //allocate 1024 file buffer
                        results.children[i].name = (c8*)pen::memory_alloc( 1024 );
                        pen::memory_zero(results.children[i].name, 1024);
                    }

                    u32 len = pen::string_length( ent->d_name );
                    len = min<u32>( len, 1022 );

                    pen::memory_cpy(results.children[i].name, ent->d_name, len);
                    results.children[i].name[len] = '\0';

                    results.children[i].num_children = 0;

                    ++i;
                }
            }

            closedir (dir);
        }

        return PEN_ERR_OK;
    }

    pen_error filesystem_enum_free_mem( fs_tree_node &tree )
    {
        for( s32 i = 0; i < tree.num_children; ++i )
        {
            filesystem_enum_free_mem( tree.children[ i ] );
        }

        pen::memory_free( tree.children );
        pen::memory_free( tree.name );

        return PEN_ERR_OK;
    }

    pen_error filesystem_getmtime( const c8* filename, u32& mtime_out )
    {
        struct stat stat_res;

        stat( filename, &stat_res);

        mtime_out = get_mtime(stat_res);

        return PEN_ERR_OK;
    }

    const c8** filesystem_get_user_directory( s32& directory_depth )
    {
        static const u32 max_dir_depth = 3;

        static c8 default_dir[max_dir_depth][1024];

        static c8* dir_list[max_dir_depth];

        pen::string_format(default_dir[0], 1024, "/" );
        pen::string_format(default_dir[1], 1024, "Users" );
        pen::string_format(default_dir[2], 1024, "%s", pen_user_info.user_name );

        for( s32 i = 0; i < max_dir_depth; ++i )
        {
            dir_list[ i ] = &default_dir[ i ][ 0 ];
        }

        directory_depth = max_dir_depth;

        return (const c8**)&dir_list[0];
    }

	s32 filesystem_exclude_slash_depth()
	{
		//directory depth 0 can be a slash
		return 0;
	}
}
<|endoftext|>