<commit_before>#include "model.h"
#include "recognition.h"
#include <memory>
#include <algorithm>
#include <vector>

using std::min;
using std::max;
using std::make_shared;
using std::dynamic_pointer_cast;

int FIGURE_SELECT_GAP = -1;
int CLOSED_FIGURE_GAP = -1;
const int TRACK_FIT_GAP = 10;
const int DELETION_MOVE_MINIMAL_LENGTH = 5;
const int DELETION_MOVE_MAX_ANGLE = 30;
const int DELETION_MOVE_MIN_COUNT = 3;
const double SQUARING_MIN_RATIO = 0.8;
const double MIN_FIT_POINTS_AMOUNT = 0.75;

void setRecognitionPreset(RecognitionPreset preset) {
    switch (preset) {
    case RecognitionPreset::Mouse:
        FIGURE_SELECT_GAP = 10;
        CLOSED_FIGURE_GAP = 10;
        break;
    case RecognitionPreset::Touch:
        FIGURE_SELECT_GAP = 30;
        CLOSED_FIGURE_GAP = 50;
        break;
    };
}

int figureSelectGap() {
    return FIGURE_SELECT_GAP;
}

BoundingBox getBoundingBox(const Track &track) {
    BoundingBox res;
    res.leftUp = res.rightDown = track[0];
    for (Point p : track.points) {
        res.leftUp.x = min(res.leftUp.x, p.x);
        res.leftUp.y = min(res.leftUp.y, p.y);
        res.rightDown.x = max(res.rightDown.x, p.x);
        res.rightDown.y = max(res.rightDown.y, p.y);
    }
    return res;
}

double getCircumference(const Track &track) {
    double res = 0;
    for (size_t i = 0; i + 1 < track.size(); i++) {
        res += (track[i + 1] - track[i]).length();
    }
    return res;
}
double getClosedCircumference(const Track &track) {
    double res = getCircumference(track);
    res += (track[0] - track[track.size() - 1]).length();
    return res;
}

bool cutToClosed(Track &track) {
    auto &points = track.points;
    auto it = points.begin();
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    while (it != points.end() && (points[0] - *it).length() > CLOSED_FIGURE_GAP) {
        it++;
    }
    if (it == points.end()) {
        return false;
    }
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    points.erase(it, points.end());
    return true;
}

double fitsToTrack(const Track &track, const PFigure &figure) {
    BoundingBox box = getBoundingBox(track);
    double maxDistance = TRACK_FIT_GAP;
    maxDistance = std::min(maxDistance, std::max(box.width(), box.height()) * 0.2);

    int goodCount = 0;
    for (Point p : track.points) {
        goodCount += figure->getApproximateDistanceToBorder(p) <= maxDistance;
    }

    return goodCount * 1.0 / track.size();
}

using namespace figures;

bool isDeletionTrack(const Track &track) {
    int cnt = 0;
    std::cout << "new\n";
    for (int i = 0; i < (int)track.size(); i++) {
        int prevPoint = i - 1;
        while (prevPoint >= 0 && (track[i] - track[prevPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            prevPoint--;
        }
        int nextPoint = i + 1;
        while (nextPoint < (int)track.size() && (track[i] - track[nextPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            nextPoint++;
        }
        if (prevPoint >= 0 && nextPoint < (int)track.size()) {
            Point vec1 = track[nextPoint] - track[i];
            Point vec2 = track[prevPoint] - track[i];
            double ang = acos(Point::dotProduct(vec1, vec2) / (vec1.length() * vec2.length()));
            if (ang <= PI * DELETION_MOVE_MAX_ANGLE / 180) {
                cnt++;
                i = nextPoint;
            }
        }
    }
    std::cout << "deletion cnt = " << cnt << "\n";
    return cnt >= DELETION_MOVE_MIN_COUNT;
}

PFigure recognizeConnections(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
        if (!figA) { continue; }
        if (!figure->isInsideOrOnBorder(start)) { continue; }
        for (PFigure figure2 : model) {
            if (figure == figure2) {
                continue;
            }
            auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
            if (figB && figB->isInsideOrOnBorder(end)) {
                auto result = make_shared<SegmentConnection>(figA, figB);
                model.addFigure(result);
                return result;
            }
        }
    }
    return nullptr;
}

PFigure recognizeGrabs(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        if (figure->getApproximateDistanceToBorder(start) <= FIGURE_SELECT_GAP) { // grabbed
            // recognize deletion
            if (isDeletionTrack(track)) {
                model.removeFigure(it);
                return figure;
            }

            // try connection
            auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
            if (figA) {
                for (PFigure figure2 : model) {
                    if (figure == figure2) {
                        continue;
                    }
                    auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
                    if (figB && figB->getApproximateDistanceToBorder(end) <= FIGURE_SELECT_GAP) {
                        auto result = make_shared<SegmentConnection>(figA, figB);
                        model.addFigure(result);
                        return result;
                    }
                }
            }

            // now we try translation
            // but only if figure was selected previously (#65)
            if (figure == model.selectedFigure) {
                figure->translate(end - start);
                model.recalculate();
                return figure;
            }
        }
    }
    return nullptr;
}

void squareBoundedFigure(PBoundedFigure figure) {
    BoundingBox box = figure->getBoundingBox();
    double siz1 = box.width();
    double siz2 = box.height();
    if (siz1 > siz2) {
        std::swap(siz1, siz2);
    }
    std::cout << "bounded size=" << siz1 << ";" << siz2 << "; k=" << (siz1 / siz2) << "\n";
    if (siz1 / siz2 < SQUARING_MIN_RATIO) { return; }

    double siz = (siz1 + siz2) / 2;
    box.rightDown = box.leftUp + Point(siz, siz);
    figure->setBoundingBox(box);
}

struct SelectionFit {
    bool isSegment;
    double distance;
    PFigure figure;

    SelectionFit() : isSegment(false), distance(HUGE_VAL), figure(nullptr) {}
    bool operator<(const SelectionFit &other) const {
        if (isSegment != other.isSegment) { return isSegment > other.isSegment; }
        return distance < other.distance;
    }
};
PFigure recognizeClicks(const Point &click, Model &model) {
    SelectionFit bestFit;
    for (PFigure figure : model) {
        SelectionFit currentFit;
        currentFit.isSegment = !!dynamic_pointer_cast<Segment>(figure);
        currentFit.distance = figure->getApproximateDistanceToBorder(click);
        currentFit.figure = figure;
        if (currentFit.distance >= FIGURE_SELECT_GAP) { continue; }
        bestFit = min(bestFit, currentFit);
    }
    if (bestFit.figure) {
        model.selectedFigure = bestFit.figure;
    } else {
        bool found = false;
        for (PFigure figure : model) {
            if (figure->isInsideOrOnBorder(click)) {
                model.selectedFigure = figure;
                found = true;
            }
        }
        if (!found) {
            model.selectedFigure = nullptr;
        }
    }

    std::shared_ptr<Segment> segm = dynamic_pointer_cast<Segment>(model.selectedFigure);
    if (segm) {
        if ((click - segm->getA()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedA(!segm->getArrowedA());
        }
        if ((click - segm->getB()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedB(!segm->getArrowedB());
        }
        return segm;
    }
    return nullptr;
}

/*
 * Consider a continuos polyline. This function returns its center of mass.
 * Please note that this differs from center of mass of all points of track,
 * we calculate weighted sum of segments' centers (weight == length of segment)
 */
Point getWeightedCenter(const Track &track) {
    Point result;
    double summaryLength = 0;
    for (size_t i = 0; i < track.size(); i++) {
        Point a = track[i];
        Point b = track[(i + 1) % track.size()];
        Point middle = (a + b) * 0.5;
        double length = (b - a).length();
        result += middle * length;
        summaryLength += length;
    }
    result = result * (1.0 / summaryLength);
    return result;
}

/*
 * Calculates best rectangle which will fit the track
 * For example, it considers that there may be some
 * outliners (they happen near corners when drawing on touch devices)
 * First, it calculate approximate center of the rectangle and then
 * it calculates average 'width' and 'height' of the rectangle.
 * Points which differ from the center no more than width/4 are
 * considered 'horizontal segments' and vice-versa for vertical.
 */
BoundingBox getBestFitRectangle(const Track &track) {
    BoundingBox total = getBoundingBox(track);
    double maxDx = total.width() / 4;
    double maxDy = total.height() / 4;
    Point center = getWeightedCenter(track);

    double sumDx = 0, sumDy = 0;
    int countDx = 0, countDy = 0;
    for (Point p : track.points) {
        double dx = fabs(p.x - center.x);
        double dy = fabs(p.y - center.y);
        if (dx < maxDx && dy >= maxDy) {
            sumDy += dy;
            countDy++;
        }
        if (dy < maxDy && dx >= maxDx) {
            sumDx += dx;
            countDx++;
        }
    }
    Point diff(
        countDx ? sumDx / countDx : maxDx,
        countDy ? sumDy / countDy : maxDy
    );
    return BoundingBox({ center - diff, center + diff });
}

std::vector<Point> smoothCurve(std::vector<Point> current) {
    assert(!current.empty());

    Point start = current[0];
    Point end = current.back();
    if ((start - end).length() <= CLOSED_FIGURE_GAP) {
        end = current.back() = start;
    }
    Segment segment(start, end);

    std::pair<double, size_t> maxDistance(0, 0);
    for (size_t i = 0; i < current.size(); i++) {
        double d = segment.getApproximateDistanceToBorder(current[i]);
        maxDistance = max(maxDistance, std::make_pair(d, i));
    }

    std::vector<Point> result;
    if (maxDistance.first > TRACK_FIT_GAP) {
        auto part1 = smoothCurve(std::vector<Point>(current.begin(), current.begin() + maxDistance.second + 1));
        auto part2 = smoothCurve(std::vector<Point>(current.begin() + maxDistance.second, current.end()));
        result.insert(result.end(), part1.begin(), part1.end());
        result.insert(result.end(), part2.begin() + 1, part2.end());
    } else {
        result.push_back(start);
        result.push_back(end);
    }
    return result;
}

PFigure recognize(const Track &_track, Model &model) {
    if (FIGURE_SELECT_GAP < 0 || CLOSED_FIGURE_GAP < 0) {
        throw std::logic_error("Recognition preset is not selected");
    }

    // this is to preserve the API (const Track&) and make future optimizations possible
    // i.e. avoid copying if it's not necessary
    Track track = _track;
    if (track.empty()) { return nullptr; }

    // Very small tracks are clicks
    if (getClosedCircumference(track) <= CLOSED_FIGURE_GAP) {
        return recognizeClicks(track[0], model);
    }

    // Moving and connecting
    if (PFigure result = recognizeGrabs(track, model)) {
        return result;
    }

    // Connecting interior-->interior
    if (PFigure result = recognizeConnections(track, model)) {
        return result;
    }

    // Drawing new figures
    std::vector<PFigure> candidates;
    if (!cutToClosed(track))  {
        candidates.push_back(make_shared<Segment>(track[0], track[track.size() - 1]));
    } else {
        candidates.push_back(make_shared<Ellipse>(getBoundingBox(track)));
        candidates.push_back(make_shared<Rectangle>(getBestFitRectangle(track)));
    }

    if (candidates.empty()) { return nullptr; }

    std::vector<double> fits;
    for (PFigure figure : candidates) {
        fits.push_back(fitsToTrack(track, figure));
    }
    size_t id = max_element(fits.begin(), fits.end()) - fits.begin();
    std::cout << "max_fit = " << fits[id] << "; id = " << id << "\n";
    if (fits[id] >= MIN_FIT_POINTS_AMOUNT) { // we allow some of points to fall out of our track
        auto boundedFigure = dynamic_pointer_cast<BoundedFigure>(candidates[id]);
        if (boundedFigure) {
            squareBoundedFigure(boundedFigure);
        }
        model.addFigure(candidates[id]);
        return candidates[id];
    }

    std::vector<Point> points(track.points.begin(), track.points.end());
    auto result = make_shared<Curve>(smoothCurve(points));
    model.addFigure(result);
    return result;
}
<commit_msg>recognition: fitsToTrack() algorithm change: no more fixed TRACK_FIT_GAP<commit_after>#include "model.h"
#include "recognition.h"
#include <memory>
#include <algorithm>
#include <vector>

using std::min;
using std::max;
using std::make_shared;
using std::dynamic_pointer_cast;

int FIGURE_SELECT_GAP = -1;
int CLOSED_FIGURE_GAP = -1;
const int TRACK_FIT_GAP = 10;
const int DELETION_MOVE_MINIMAL_LENGTH = 5;
const int DELETION_MOVE_MAX_ANGLE = 30;
const int DELETION_MOVE_MIN_COUNT = 3;
const double SQUARING_MIN_RATIO = 0.8;
const double MIN_FIT_POINTS_AMOUNT = 0.75;

void setRecognitionPreset(RecognitionPreset preset) {
    switch (preset) {
    case RecognitionPreset::Mouse:
        FIGURE_SELECT_GAP = 10;
        CLOSED_FIGURE_GAP = 10;
        break;
    case RecognitionPreset::Touch:
        FIGURE_SELECT_GAP = 30;
        CLOSED_FIGURE_GAP = 50;
        break;
    };
}

int figureSelectGap() {
    return FIGURE_SELECT_GAP;
}

BoundingBox getBoundingBox(const Track &track) {
    BoundingBox res;
    res.leftUp = res.rightDown = track[0];
    for (Point p : track.points) {
        res.leftUp.x = min(res.leftUp.x, p.x);
        res.leftUp.y = min(res.leftUp.y, p.y);
        res.rightDown.x = max(res.rightDown.x, p.x);
        res.rightDown.y = max(res.rightDown.y, p.y);
    }
    return res;
}

double getCircumference(const Track &track) {
    double res = 0;
    for (size_t i = 0; i + 1 < track.size(); i++) {
        res += (track[i + 1] - track[i]).length();
    }
    return res;
}
double getClosedCircumference(const Track &track) {
    double res = getCircumference(track);
    res += (track[0] - track[track.size() - 1]).length();
    return res;
}

bool cutToClosed(Track &track) {
    auto &points = track.points;
    auto it = points.begin();
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    while (it != points.end() && (points[0] - *it).length() > CLOSED_FIGURE_GAP) {
        it++;
    }
    if (it == points.end()) {
        return false;
    }
    while (it != points.end() && (points[0] - *it).length() <= CLOSED_FIGURE_GAP) {
        it++;
    }
    points.erase(it, points.end());
    return true;
}

double fitsToTrack(const Track &track, const PFigure &figure) {
    BoundingBox box = getBoundingBox(track);
    double maxDistance = std::max(box.width(), box.height()) * 0.1;
    double failDistance = std::max(box.width(), box.height()) * 0.2;

    int goodCount = 0;
    for (Point p : track.points) {
        double dist = figure->getApproximateDistanceToBorder(p);
        if (dist > failDistance) { return 0; }
        goodCount += dist <= maxDistance;
    }

    return goodCount * 1.0 / track.size();
}

using namespace figures;

bool isDeletionTrack(const Track &track) {
    int cnt = 0;
    std::cout << "new\n";
    for (int i = 0; i < (int)track.size(); i++) {
        int prevPoint = i - 1;
        while (prevPoint >= 0 && (track[i] - track[prevPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            prevPoint--;
        }
        int nextPoint = i + 1;
        while (nextPoint < (int)track.size() && (track[i] - track[nextPoint]).length() < DELETION_MOVE_MINIMAL_LENGTH) {
            nextPoint++;
        }
        if (prevPoint >= 0 && nextPoint < (int)track.size()) {
            Point vec1 = track[nextPoint] - track[i];
            Point vec2 = track[prevPoint] - track[i];
            double ang = acos(Point::dotProduct(vec1, vec2) / (vec1.length() * vec2.length()));
            if (ang <= PI * DELETION_MOVE_MAX_ANGLE / 180) {
                cnt++;
                i = nextPoint;
            }
        }
    }
    std::cout << "deletion cnt = " << cnt << "\n";
    return cnt >= DELETION_MOVE_MIN_COUNT;
}

PFigure recognizeConnections(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
        if (!figA) { continue; }
        if (!figure->isInsideOrOnBorder(start)) { continue; }
        for (PFigure figure2 : model) {
            if (figure == figure2) {
                continue;
            }
            auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
            if (figB && figB->isInsideOrOnBorder(end)) {
                auto result = make_shared<SegmentConnection>(figA, figB);
                model.addFigure(result);
                return result;
            }
        }
    }
    return nullptr;
}

PFigure recognizeGrabs(const Track &track, Model &model) {
    Point start = track[0];
    Point end = track[track.size() - 1];

    for (auto it = model.begin(); it != model.end(); it++) {
        PFigure figure = *it;
        if (figure->getApproximateDistanceToBorder(start) <= FIGURE_SELECT_GAP) { // grabbed
            // recognize deletion
            if (isDeletionTrack(track)) {
                model.removeFigure(it);
                return figure;
            }

            // try connection
            auto figA = dynamic_pointer_cast<BoundedFigure>(figure);
            if (figA) {
                for (PFigure figure2 : model) {
                    if (figure == figure2) {
                        continue;
                    }
                    auto figB = dynamic_pointer_cast<BoundedFigure>(figure2);
                    if (figB && figB->getApproximateDistanceToBorder(end) <= FIGURE_SELECT_GAP) {
                        auto result = make_shared<SegmentConnection>(figA, figB);
                        model.addFigure(result);
                        return result;
                    }
                }
            }

            // now we try translation
            // but only if figure was selected previously (#65)
            if (figure == model.selectedFigure) {
                figure->translate(end - start);
                model.recalculate();
                return figure;
            }
        }
    }
    return nullptr;
}

void squareBoundedFigure(PBoundedFigure figure) {
    BoundingBox box = figure->getBoundingBox();
    double siz1 = box.width();
    double siz2 = box.height();
    if (siz1 > siz2) {
        std::swap(siz1, siz2);
    }
    std::cout << "bounded size=" << siz1 << ";" << siz2 << "; k=" << (siz1 / siz2) << "\n";
    if (siz1 / siz2 < SQUARING_MIN_RATIO) { return; }

    double siz = (siz1 + siz2) / 2;
    box.rightDown = box.leftUp + Point(siz, siz);
    figure->setBoundingBox(box);
}

struct SelectionFit {
    bool isSegment;
    double distance;
    PFigure figure;

    SelectionFit() : isSegment(false), distance(HUGE_VAL), figure(nullptr) {}
    bool operator<(const SelectionFit &other) const {
        if (isSegment != other.isSegment) { return isSegment > other.isSegment; }
        return distance < other.distance;
    }
};
PFigure recognizeClicks(const Point &click, Model &model) {
    SelectionFit bestFit;
    for (PFigure figure : model) {
        SelectionFit currentFit;
        currentFit.isSegment = !!dynamic_pointer_cast<Segment>(figure);
        currentFit.distance = figure->getApproximateDistanceToBorder(click);
        currentFit.figure = figure;
        if (currentFit.distance >= FIGURE_SELECT_GAP) { continue; }
        bestFit = min(bestFit, currentFit);
    }
    if (bestFit.figure) {
        model.selectedFigure = bestFit.figure;
    } else {
        bool found = false;
        for (PFigure figure : model) {
            if (figure->isInsideOrOnBorder(click)) {
                model.selectedFigure = figure;
                found = true;
            }
        }
        if (!found) {
            model.selectedFigure = nullptr;
        }
    }

    std::shared_ptr<Segment> segm = dynamic_pointer_cast<Segment>(model.selectedFigure);
    if (segm) {
        if ((click - segm->getA()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedA(!segm->getArrowedA());
        }
        if ((click - segm->getB()).length() <= FIGURE_SELECT_GAP) {
            segm->setArrowedB(!segm->getArrowedB());
        }
        return segm;
    }
    return nullptr;
}

/*
 * Consider a continuos polyline. This function returns its center of mass.
 * Please note that this differs from center of mass of all points of track,
 * we calculate weighted sum of segments' centers (weight == length of segment)
 */
Point getWeightedCenter(const Track &track) {
    Point result;
    double summaryLength = 0;
    for (size_t i = 0; i < track.size(); i++) {
        Point a = track[i];
        Point b = track[(i + 1) % track.size()];
        Point middle = (a + b) * 0.5;
        double length = (b - a).length();
        result += middle * length;
        summaryLength += length;
    }
    result = result * (1.0 / summaryLength);
    return result;
}

/*
 * Calculates best rectangle which will fit the track
 * For example, it considers that there may be some
 * outliners (they happen near corners when drawing on touch devices)
 * First, it calculate approximate center of the rectangle and then
 * it calculates average 'width' and 'height' of the rectangle.
 * Points which differ from the center no more than width/4 are
 * considered 'horizontal segments' and vice-versa for vertical.
 */
BoundingBox getBestFitRectangle(const Track &track) {
    BoundingBox total = getBoundingBox(track);
    double maxDx = total.width() / 4;
    double maxDy = total.height() / 4;
    Point center = getWeightedCenter(track);

    double sumDx = 0, sumDy = 0;
    int countDx = 0, countDy = 0;
    for (Point p : track.points) {
        double dx = fabs(p.x - center.x);
        double dy = fabs(p.y - center.y);
        if (dx < maxDx && dy >= maxDy) {
            sumDy += dy;
            countDy++;
        }
        if (dy < maxDy && dx >= maxDx) {
            sumDx += dx;
            countDx++;
        }
    }
    Point diff(
        countDx ? sumDx / countDx : maxDx,
        countDy ? sumDy / countDy : maxDy
    );
    return BoundingBox({ center - diff, center + diff });
}

std::vector<Point> smoothCurve(std::vector<Point> current) {
    assert(!current.empty());

    Point start = current[0];
    Point end = current.back();
    if ((start - end).length() <= CLOSED_FIGURE_GAP) {
        end = current.back() = start;
    }
    Segment segment(start, end);

    std::pair<double, size_t> maxDistance(0, 0);
    for (size_t i = 0; i < current.size(); i++) {
        double d = segment.getApproximateDistanceToBorder(current[i]);
        maxDistance = max(maxDistance, std::make_pair(d, i));
    }

    std::vector<Point> result;
    if (maxDistance.first > TRACK_FIT_GAP) {
        auto part1 = smoothCurve(std::vector<Point>(current.begin(), current.begin() + maxDistance.second + 1));
        auto part2 = smoothCurve(std::vector<Point>(current.begin() + maxDistance.second, current.end()));
        result.insert(result.end(), part1.begin(), part1.end());
        result.insert(result.end(), part2.begin() + 1, part2.end());
    } else {
        result.push_back(start);
        result.push_back(end);
    }
    return result;
}

PFigure recognize(const Track &_track, Model &model) {
    if (FIGURE_SELECT_GAP < 0 || CLOSED_FIGURE_GAP < 0) {
        throw std::logic_error("Recognition preset is not selected");
    }

    // this is to preserve the API (const Track&) and make future optimizations possible
    // i.e. avoid copying if it's not necessary
    Track track = _track;
    if (track.empty()) { return nullptr; }

    // Very small tracks are clicks
    if (getClosedCircumference(track) <= CLOSED_FIGURE_GAP) {
        return recognizeClicks(track[0], model);
    }

    // Moving and connecting
    if (PFigure result = recognizeGrabs(track, model)) {
        return result;
    }

    // Connecting interior-->interior
    if (PFigure result = recognizeConnections(track, model)) {
        return result;
    }

    // Drawing new figures
    std::vector<PFigure> candidates;
    if (!cutToClosed(track))  {
        candidates.push_back(make_shared<Segment>(track[0], track[track.size() - 1]));
    } else {
        candidates.push_back(make_shared<Ellipse>(getBoundingBox(track)));
        candidates.push_back(make_shared<Rectangle>(getBestFitRectangle(track)));
    }

    if (candidates.empty()) { return nullptr; }

    std::vector<double> fits;
    for (PFigure figure : candidates) {
        fits.push_back(fitsToTrack(track, figure));
    }
    size_t id = max_element(fits.begin(), fits.end()) - fits.begin();
    std::cout << "max_fit = " << fits[id] << "; id = " << id << "\n";
    if (fits[id] >= MIN_FIT_POINTS_AMOUNT) { // we allow some of points to fall out of our track
        auto boundedFigure = dynamic_pointer_cast<BoundedFigure>(candidates[id]);
        if (boundedFigure) {
            squareBoundedFigure(boundedFigure);
        }
        model.addFigure(candidates[id]);
        return candidates[id];
    }

    std::vector<Point> points(track.points.begin(), track.points.end());
    auto result = make_shared<Curve>(smoothCurve(points));
    model.addFigure(result);
    return result;
}
<|endoftext|>


<commit_before>/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2012 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// Test that mobility tracing works

#include "ns3/core-module.h"
#include "ns3/mobility-module.h"
#include "ns3/network-module.h"

using namespace ns3;

int main (int argc, char *argv[])
{
  CommandLine cmd;
  cmd.Parse (argc, argv);

  // One train & three stations
  NodeContainer train;
  train.Create(1);
  NodeContainer sta;
  sta.Create (5);

  // How to get/set sta's location and presentate them into the form of Vector(a,b,c)?
  // Ptr<ConstantPositionMobilityModel> Loc =  n->GetObject<ConstantPositionMobilityModel> ();
  // if (Loc == 0)
  //     {
  //    Loc = CreateObject<ConstantPositionMobilityModel> ();
  //    n->AggregateObject (Loc);
  //     }
 // Vector vec (10, 20, 0);
 // Loc->SetPosition (vec);
  MobilityHelper mobility;
  // Put everybody into a line
  Ptr<ListPositionAllocator> initialAlloc =
    CreateObject<ListPositionAllocator> ();
      initialAlloc->Add (Vector (16.0, 22, 0.));
      initialAlloc->Add (Vector (45.0, 37, 0.));
      initialAlloc->Add (Vector (53.0, 8, 0.));

  mobility.SetPositionAllocator(initialAlloc);

 mobility.SetMobilityModel("ns3::WaypointMobilityModel");
 mobility.Install(train.Get(0));
  // Set mobility random number streams to fixed values
  //mobility.AssignStreams (sta, 0);
 Ptr<WaypointMobilityModel> staWaypointMobility = DynamicCast<WaypointMobilityModel>( train.Get(0)->GetObject<MobilityModel>());
  // AsciiTraceHelper ascii;
  //MobilityHelper::EnableAsciiAll (ascii.CreateFileStream ("mobility-trace-example.mob"));
 staWaypointMobility->AddWaypoint(Waypoint(Seconds(1.0),Vector(10.0,9,0)));
 staWaypointMobility->AddWaypoint(Waypoint(Seconds(4.0), Vector(16.0,22,0)));
staWaypointMobility->AddWaypoint(Waypoint(Seconds(8.0), Vector(16.0,22,0)));
 staWaypointMobility->AddWaypoint (Waypoint (Seconds (13.0),Vector (45.0, 37, 0)));
 staWaypointMobility->AddWaypoint(Waypoint (Seconds (15.0),Vector (53.0, 8,0)));
  Simulator::Stop (Seconds (40.0));
  Simulator::Run ();
  Simulator::Destroy ();
}
<commit_msg>Printing out location using MobilityModel<commit_after>/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2012 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// Test that mobility tracing works

#include <iostream>

#include <ns3-dev/ns3/core-module.h>
#include <ns3-dev/ns3/network-module.h>
#include <ns3-dev/ns3/mobility-module.h>

using namespace ns3;

int main (int argc, char *argv[])
{
	CommandLine cmd;
	cmd.Parse (argc, argv);

	// One train & three stations
	NodeContainer train;
	train.Create(1);
	NodeContainer sta;
	sta.Create (5);

	// How to get/set sta's location and presentate them into the form of Vector(a,b,c)?
	// Ptr<ConstantPositionMobilityModel> Loc =  n->GetObject<ConstantPositionMobilityModel> ();
	// if (Loc == 0)
	//     {
	//    Loc = CreateObject<ConstantPositionMobilityModel> ();
	//    n->AggregateObject (Loc);
	//     }
	// Vector vec (10, 20, 0);
	// Loc->SetPosition (vec);
	MobilityHelper mobility;
	// Put everybody into a line
	Ptr<ListPositionAllocator> initialAlloc =
			CreateObject<ListPositionAllocator> ();
	initialAlloc->Add (Vector (16.0, 22, 0.));
	initialAlloc->Add (Vector (45.0, 37, 0.));
	initialAlloc->Add (Vector (53.0, 8, 0.));

	mobility.SetPositionAllocator(initialAlloc);

	mobility.SetMobilityModel("ns3::WaypointMobilityModel");
	mobility.Install(train.Get(0));
	// Set mobility random number streams to fixed values
	//mobility.AssignStreams (sta, 0);
	Ptr<WaypointMobilityModel> staWaypointMobility = DynamicCast<WaypointMobilityModel>( train.Get(0)->GetObject<MobilityModel>());

	Ptr<MobilityModel> mob = train.Get(0)->GetObject<MobilityModel>();

	Vector pos = mob->GetPosition();

	std::cout << "Vector test x: " << pos.x << " y: " << pos.y << std::endl;
	// AsciiTraceHelper ascii;
	//MobilityHelper::EnableAsciiAll (ascii.CreateFileStream ("mobility-trace-example.mob"));
	staWaypointMobility->AddWaypoint(Waypoint(Seconds(1.0),Vector(10.0,9,0)));
	staWaypointMobility->AddWaypoint(Waypoint(Seconds(4.0), Vector(16.0,22,0)));
	staWaypointMobility->AddWaypoint(Waypoint(Seconds(8.0), Vector(16.0,22,0)));
	staWaypointMobility->AddWaypoint (Waypoint (Seconds (13.0),Vector (45.0, 37, 0)));
	staWaypointMobility->AddWaypoint(Waypoint (Seconds (15.0),Vector (53.0, 8,0)));
	Simulator::Stop (Seconds (40.0));
	Simulator::Run ();
	Simulator::Destroy ();
}
<|endoftext|>


<commit_before>#define GLFW_INCLUDE_ES2

#include "tangram.h"
#include "platform.h"
#include "gl.h"

void window_size_callback(GLFWwindow* window, int width, int height)
{
    Tangram::resize(width, height);
}

int main(void)
{
    GLFWwindow* window;
    int width = 800;
    int height = 600;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(width, height, "GLFW Window", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    Tangram::initialize();
    Tangram::resize(width, height);

    glfwSetWindowSizeCallback(window, window_size_callback);

    double lastTime = glfwGetTime();

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        double currentTime = glfwGetTime();
        double delta = currentTime - lastTime;
        lastTime = currentTime;

        /* Render here */
        Tangram::update(delta);
        Tangram::render();

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    Tangram::teardown();
    glfwTerminate();
    return 0;
}
<commit_msg>erge branch 'master' of github.com:tangrams/tangram-es into lights<commit_after>#define GLFW_INCLUDE_ES2

#include "tangram.h"
#include "platform.h"
#include "gl.h"

// Input handling
// ==============

const double double_tap_time = 0.5; // seconds

bool was_panning = false;
double last_mouse_up = -double_tap_time; // First click should never trigger a double tap
double last_x_down = 0.0;
double last_y_down = 0.0;

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {

    if (button != GLFW_MOUSE_BUTTON_1) {
        return; // This event is for a mouse button that we don't care about
    }

    if (was_panning) {
        was_panning = false;
        return; // Clicks with movement don't count as taps
    }

    double x, y;
    glfwGetCursorPos(window, &x, &y);
    double time = glfwGetTime();

    if (action == GLFW_PRESS) {
        last_x_down = x;
        last_y_down = y;
        return;
    }

    if (time - last_mouse_up < double_tap_time) {
        Tangram::handleDoubleTapGesture(x, y);
    } else {
        Tangram::handleTapGesture(x, y);
    }

    last_mouse_up = time;

}

void cursor_pos_callback(GLFWwindow* window, double x, double y) {

    int action = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1);

    if (action == GLFW_PRESS) {

        if (was_panning) {
            Tangram::handlePanGesture(x - last_x_down, y - last_y_down);
        }

        was_panning = true;
        last_x_down = x;
        last_y_down = y;
    }

}

void scroll_callback(GLFWwindow* window, double scrollx, double scrolly) {

    static double scrolled = 0.0;
    scrolled += scrolly;

    // TODO: Update this for continuous zooming, using an arbitrary threshold for now
    if (scrolled * scrolled > 100.0) {

        double x, y;
        glfwGetCursorPos(window, &x, &y);
        Tangram::handlePinchGesture(x, y, scrolled > 0 ? 1.5 : 0.5);
        scrolled = 0.0;

    }
}

// Window handling
// ===============

void window_size_callback(GLFWwindow* window, int width, int height) {

    Tangram::resize(width, height);

}

// Main program
// ============

int main(void) {

    GLFWwindow* window;
    int width = 800;
    int height = 600;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(width, height, "GLFW Window", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    Tangram::initialize();
    Tangram::resize(width, height);

    glfwSetWindowSizeCallback(window, window_size_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);
    glfwSetCursorPosCallback(window, cursor_pos_callback);
    glfwSetScrollCallback(window, scroll_callback);

    glfwSwapInterval(1);

    double lastTime = glfwGetTime();

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window)) {

        double currentTime = glfwGetTime();
        double delta = currentTime - lastTime;
        lastTime = currentTime;

        /* Render here */
        Tangram::update(delta);
        Tangram::render();

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    Tangram::teardown();
    glfwTerminate();
    return 0;
}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*/

/*
  Parts of this code come from Konversation and are copyrighted to:
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2004 Peter Simonsson <psn@linux.se>
  Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
  Copyright (C) 2004-2009 Eli Mackenzie <argonel@gmail.com>
*/

#include "ircutil.h"
#include <QString>
#include <QRegExp>

/*!
    \class Irc::Util ircutil.h
    \brief The Irc::Util class provides IRC related utility functions.
 */

static QRegExp URL_PATTERN(QLatin1String("((www\\.(?!\\.)|(ssh|fish|irc|amarok|(f|sf|ht)tp(|s))://)(\\.?[\\d\\w/,\\':~\\^\\?=;#@\\-\\+\\%\\*\\{\\}\\!\\(\\)\\[\\]]|&)+)|""([-.\\d\\w]+@[-.\\d\\w]{2,}\\.[\\w]{2,})"), Qt::CaseInsensitive);

/*!
    Converts \a message to HTML. This function parses the
    message and replaces IRC-style formatting like colors,
    bold and underline to the corresponding HTML formatting.
    Furthermore, this function detects URLs and replaces
    them with appropriate HTML hyperlinks.
*/
QString IrcUtil::messageToHtml(const QString& message)
{
    QString processed = message;
    processed.replace(QLatin1Char('&'), QLatin1String("&"));
    processed.replace(QLatin1Char('<'), QLatin1String("<"));
    processed.replace(QLatin1Char('>'), QLatin1String(">"));
    processed.replace(QLatin1Char('"'), QLatin1String("""));
    processed.replace(QLatin1Char('\''), QLatin1String("'"));
    processed.replace(QLatin1Char('\t'), QLatin1String(" "));

    enum
    {
        None            = 0x0,
        Bold            = 0x1,
        Color           = 0x2,
        Italic          = 0x4,
        StrikeThrough   = 0x8,
        Underline       = 0x10,
        Inverse         = 0x20
    };
    int state = None;

    int pos = 0;
    bool parseColor = false;
    while (pos < processed.size())
    {
        if (parseColor)
        {
            int len = 2;
            bool ok = false;
            int code = processed.mid(pos, len).toInt(&ok);
            if (ok)
            {
                processed.remove(pos, len);
                QString color = colorCodeToName(code);
                processed = processed.arg(color);
                len = color.length();
            }
            pos += len;
            parseColor = false;
        }

        QString replacement;
        switch (processed.at(pos).unicode())
        {
        case '\x02': // bold
            if (state & Bold)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Bold;
            break;

        case '\x03': // color
            if (state & Color)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Color;
            parseColor = state & Color;
            break;

        //case '\x09': // italic
        case '\x1d': // italic
            if (state & Italic)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Italic;
            break;

        case '\x13': // strike-through
            if (state & StrikeThrough)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= StrikeThrough;
            break;

        case '\x15': // underline
        case '\x1f': // underline
            if (state & Underline)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Underline;
            break;

        case '\x16': // inverse
            if (state & Inverse)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Inverse;
            break;

        case '\x0f': // none
            replacement = QLatin1String("");
            state = None;
            break;

        default:
            break;
        }

        if (!replacement.isNull())
        {
            processed.replace(pos, 1, replacement);
            pos += replacement.length();
        }
        else
        {
            ++pos;
        }
    }

    pos = 0;
    while ((pos = URL_PATTERN.indexIn(processed, pos)) >= 0)
    {
        int len = URL_PATTERN.matchedLength();
        QString href = processed.mid(pos, len);

        // Don't consider trailing > as part of the link.
        QString append;
        if (href.endsWith(QLatin1String(">")))
        {
            append.append(href.right(4));
            href.chop(4);
        }

        // Don't consider trailing comma or semi-colon as part of the link.
        if (href.endsWith(QLatin1Char(',')) || href.endsWith(QLatin1Char(';')))
        {
            append.append(href.right(1));
            href.chop(1);
        }

        // Don't consider trailing closing parenthesis part of the link when
        // there's an opening parenthesis preceding in the beginning of the
        // URL or there is no opening parenthesis in the URL at all.
        if (pos > 0 && href.endsWith(QLatin1Char(')'))
            && (processed.at(pos-1) == QLatin1Char('(')
            || !href.contains(QLatin1Char('('))))
        {
            append.prepend(href.right(1));
            href.chop(1);
        }

        // Qt doesn't support (?<=pattern) so we do it here
        if (pos > 0 && processed.at(pos-1).isLetterOrNumber())
        {
            pos++;
            continue;
        }

        QString protocol;
        if (URL_PATTERN.cap(1).startsWith(QLatin1String("www."), Qt::CaseInsensitive))
            protocol = QLatin1String("http://");
        else if (URL_PATTERN.cap(1).isEmpty())
            protocol = QLatin1String("mailto:");

        QString source = href;
        source.replace(QLatin1String("&"), QLatin1String("&"));

        QString link = QString(QLatin1String("%3")).arg(protocol, source, href) + append;
        processed.replace(pos, len, link);
        pos += link.length();
    }

    return processed;
}

/*!
    Converts a color \a code to a color name.
*/
QString IrcUtil::colorCodeToName(int code)
{
    switch (code)
    {
    case 0:  return QLatin1String("white");
    case 1:  return QLatin1String("black");
    case 2:  return QLatin1String("navy");
    case 3:  return QLatin1String("green");
    case 4:  return QLatin1String("red");
    case 5:  return QLatin1String("maroon");
    case 6:  return QLatin1String("purple");
    case 7:  return QLatin1String("orange");
    case 8:  return QLatin1String("yellow");
    case 9:  return QLatin1String("lime");
    case 10: return QLatin1String("darkcyan");
    case 11: return QLatin1String("cyan");
    case 12: return QLatin1String("blue");
    case 13: return QLatin1String("magenta");
    case 14: return QLatin1String("gray");
    case 15: return QLatin1String("lightgray");
    default: return QLatin1String("black");
    }
}

<commit_msg>IrcUtil docs<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*/

/*
  Parts of this code come from Konversation and are copyrighted to:
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
  Copyright (C) 2004 Peter Simonsson <psn@linux.se>
  Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
  Copyright (C) 2004-2009 Eli Mackenzie <argonel@gmail.com>
*/

#include "ircutil.h"
#include <QString>
#include <QRegExp>

/*!
    \class IrcUtil ircutil.h <IrcUtil>
    \brief The IrcUtil class provides miscellaneous utility functions.
 */

static QRegExp URL_PATTERN(QLatin1String("((www\\.(?!\\.)|(ssh|fish|irc|amarok|(f|sf|ht)tp(|s))://)(\\.?[\\d\\w/,\\':~\\^\\?=;#@\\-\\+\\%\\*\\{\\}\\!\\(\\)\\[\\]]|&)+)|""([-.\\d\\w]+@[-.\\d\\w]{2,}\\.[\\w]{2,})"), Qt::CaseInsensitive);

/*!
    Converts \a message to HTML. This function parses the
    message and replaces IRC-style formatting like colors,
    bold and underline to the corresponding HTML formatting.
    Furthermore, this function detects URLs and replaces
    them with appropriate HTML hyperlinks.
*/
QString IrcUtil::messageToHtml(const QString& message)
{
    QString processed = message;
    processed.replace(QLatin1Char('&'), QLatin1String("&"));
    processed.replace(QLatin1Char('<'), QLatin1String("<"));
    processed.replace(QLatin1Char('>'), QLatin1String(">"));
    processed.replace(QLatin1Char('"'), QLatin1String("""));
    processed.replace(QLatin1Char('\''), QLatin1String("'"));
    processed.replace(QLatin1Char('\t'), QLatin1String(" "));

    enum
    {
        None            = 0x0,
        Bold            = 0x1,
        Color           = 0x2,
        Italic          = 0x4,
        StrikeThrough   = 0x8,
        Underline       = 0x10,
        Inverse         = 0x20
    };
    int state = None;

    int pos = 0;
    bool parseColor = false;
    while (pos < processed.size())
    {
        if (parseColor)
        {
            int len = 2;
            bool ok = false;
            int code = processed.mid(pos, len).toInt(&ok);
            if (ok)
            {
                processed.remove(pos, len);
                QString color = colorCodeToName(code);
                processed = processed.arg(color);
                len = color.length();
            }
            pos += len;
            parseColor = false;
        }

        QString replacement;
        switch (processed.at(pos).unicode())
        {
        case '\x02': // bold
            if (state & Bold)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Bold;
            break;

        case '\x03': // color
            if (state & Color)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Color;
            parseColor = state & Color;
            break;

        //case '\x09': // italic
        case '\x1d': // italic
            if (state & Italic)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Italic;
            break;

        case '\x13': // strike-through
            if (state & StrikeThrough)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= StrikeThrough;
            break;

        case '\x15': // underline
        case '\x1f': // underline
            if (state & Underline)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Underline;
            break;

        case '\x16': // inverse
            if (state & Inverse)
                replacement = QLatin1String("");
            else
                replacement = QLatin1String("");
            state ^= Inverse;
            break;

        case '\x0f': // none
            replacement = QLatin1String("");
            state = None;
            break;

        default:
            break;
        }

        if (!replacement.isNull())
        {
            processed.replace(pos, 1, replacement);
            pos += replacement.length();
        }
        else
        {
            ++pos;
        }
    }

    pos = 0;
    while ((pos = URL_PATTERN.indexIn(processed, pos)) >= 0)
    {
        int len = URL_PATTERN.matchedLength();
        QString href = processed.mid(pos, len);

        // Don't consider trailing > as part of the link.
        QString append;
        if (href.endsWith(QLatin1String(">")))
        {
            append.append(href.right(4));
            href.chop(4);
        }

        // Don't consider trailing comma or semi-colon as part of the link.
        if (href.endsWith(QLatin1Char(',')) || href.endsWith(QLatin1Char(';')))
        {
            append.append(href.right(1));
            href.chop(1);
        }

        // Don't consider trailing closing parenthesis part of the link when
        // there's an opening parenthesis preceding in the beginning of the
        // URL or there is no opening parenthesis in the URL at all.
        if (pos > 0 && href.endsWith(QLatin1Char(')'))
            && (processed.at(pos-1) == QLatin1Char('(')
            || !href.contains(QLatin1Char('('))))
        {
            append.prepend(href.right(1));
            href.chop(1);
        }

        // Qt doesn't support (?<=pattern) so we do it here
        if (pos > 0 && processed.at(pos-1).isLetterOrNumber())
        {
            pos++;
            continue;
        }

        QString protocol;
        if (URL_PATTERN.cap(1).startsWith(QLatin1String("www."), Qt::CaseInsensitive))
            protocol = QLatin1String("http://");
        else if (URL_PATTERN.cap(1).isEmpty())
            protocol = QLatin1String("mailto:");

        QString source = href;
        source.replace(QLatin1String("&"), QLatin1String("&"));

        QString link = QString(QLatin1String("%3")).arg(protocol, source, href) + append;
        processed.replace(pos, len, link);
        pos += link.length();
    }

    return processed;
}

/*!
    Converts a color \a code to a color name.
*/
QString IrcUtil::colorCodeToName(int code)
{
    switch (code)
    {
    case 0:  return QLatin1String("white");
    case 1:  return QLatin1String("black");
    case 2:  return QLatin1String("navy");
    case 3:  return QLatin1String("green");
    case 4:  return QLatin1String("red");
    case 5:  return QLatin1String("maroon");
    case 6:  return QLatin1String("purple");
    case 7:  return QLatin1String("orange");
    case 8:  return QLatin1String("yellow");
    case 9:  return QLatin1String("lime");
    case 10: return QLatin1String("darkcyan");
    case 11: return QLatin1String("cyan");
    case 12: return QLatin1String("blue");
    case 13: return QLatin1String("magenta");
    case 14: return QLatin1String("gray");
    case 15: return QLatin1String("lightgray");
    default: return QLatin1String("black");
    }
}

<|endoftext|>


<commit_before>// rogueGenocide.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include <string>
#include <map>

// event handler callback
class RogueGenoHandler : public bz_Plugin
{
public:
	virtual const char* Name (){return "Rogue Geoncide";}
	virtual void Init ( const char* config);

  virtual void Event ( bz_EventData *eventData );

  bool noSuicide;
};

BZ_PLUGIN(RogueGenoHandler)

void RogueGenoHandler::Init( const char* commandLine )
{
  Register(bz_ePlayerDieEvent);

  std::string param = commandLine;

  noSuicide = (param == "nosuicide");
}

void RogueGenoHandler::Event ( bz_EventData *eventData )
{
  switch (eventData->eventType) {
  default:
    // no clue
    break;

  // wait for a tank death and start checking for genocide and rogues
  case bz_ePlayerDieEvent:
    {
      bz_PlayerDieEventData_V1	*dieData = (bz_PlayerDieEventData_V1*)eventData;
      //if its not a genocide kill, dont care
      if (dieData->flagKilledWith != "G" )
	break;
      // if the tank killed was not a rogue, let the server/client do the normal killing
      if (dieData->team != eRogueTeam )
	break;
      // option to disallow rogues getting points for shooting themselves
      if ( noSuicide && dieData->killerID == dieData->playerID )
	break;

      // if the tank killed was a rogue, kill all rogues.
      bz_APIIntList	*playerList = bz_newIntList();

      bz_getPlayerIndexList(playerList);

      for ( unsigned int i = 0; i < playerList->size(); i++)
      {
	int targetID = (*playerList)[i];
	bz_BasePlayerRecord *playRec = bz_getPlayerByIndex ( targetID );
	if (!playRec) continue;

	// the sucker is a spawned rogue, kill him.  This generates another death event,
	// so if you kill another rogue with geno while you are a rogue you end up dead too.
	// and you get both messages (victim and be careful)
	if ( playRec->spawned && playRec->team == eRogueTeam )
	{
	  bz_killPlayer( targetID, false, dieData->killerID, "G" );
	  bz_sendTextMessage(BZ_SERVER, targetID, "You were a victim of Rogue Genocide");

	  // oops, I ended up killing myself (directly or indirectly) with Genocide!
	  if ( targetID == dieData->killerID )
	    bz_sendTextMessage(BZ_SERVER, targetID, "You should be more careful with Genocide!");
	}

	bz_freePlayerRecord(playRec);
      }
      bz_deleteIntList(playerList);
    }
    break;
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<commit_msg>Spell genocide correctly.<commit_after>// rogueGenocide.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include <string>
#include <map>

// event handler callback
class RogueGenoHandler : public bz_Plugin
{
public:
	virtual const char* Name (){return "Rogue Genocide";}
	virtual void Init ( const char* config);

  virtual void Event ( bz_EventData *eventData );

  bool noSuicide;
};

BZ_PLUGIN(RogueGenoHandler)

void RogueGenoHandler::Init( const char* commandLine )
{
  Register(bz_ePlayerDieEvent);

  std::string param = commandLine;

  noSuicide = (param == "nosuicide");
}

void RogueGenoHandler::Event ( bz_EventData *eventData )
{
  switch (eventData->eventType) {
  default:
    // no clue
    break;

  // wait for a tank death and start checking for genocide and rogues
  case bz_ePlayerDieEvent:
    {
      bz_PlayerDieEventData_V1	*dieData = (bz_PlayerDieEventData_V1*)eventData;
      //if its not a genocide kill, dont care
      if (dieData->flagKilledWith != "G" )
	break;
      // if the tank killed was not a rogue, let the server/client do the normal killing
      if (dieData->team != eRogueTeam )
	break;
      // option to disallow rogues getting points for shooting themselves
      if ( noSuicide && dieData->killerID == dieData->playerID )
	break;

      // if the tank killed was a rogue, kill all rogues.
      bz_APIIntList	*playerList = bz_newIntList();

      bz_getPlayerIndexList(playerList);

      for ( unsigned int i = 0; i < playerList->size(); i++)
      {
	int targetID = (*playerList)[i];
	bz_BasePlayerRecord *playRec = bz_getPlayerByIndex ( targetID );
	if (!playRec) continue;

	// the sucker is a spawned rogue, kill him.  This generates another death event,
	// so if you kill another rogue with geno while you are a rogue you end up dead too.
	// and you get both messages (victim and be careful)
	if ( playRec->spawned && playRec->team == eRogueTeam )
	{
	  bz_killPlayer( targetID, false, dieData->killerID, "G" );
	  bz_sendTextMessage(BZ_SERVER, targetID, "You were a victim of Rogue Genocide");

	  // oops, I ended up killing myself (directly or indirectly) with Genocide!
	  if ( targetID == dieData->killerID )
	    bz_sendTextMessage(BZ_SERVER, targetID, "You should be more careful with Genocide!");
	}

	bz_freePlayerRecord(playRec);
      }
      bz_deleteIntList(playerList);
    }
    break;
  }
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<|endoftext|>


<commit_before>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "tpool.h"
#include "direct.h"
#include "lb_instance.hxx"
#include "lb_setup.hxx"
#include "lb_connection.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "hstock.hxx"
#include "stock.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "log-glue.h"
#include "lb_config.hxx"
#include "lb_hmonitor.hxx"
#include "ssl_init.hxx"
#include "child_manager.hxx"
#include "thread_pool.hxx"
#include "fb_pool.hxx"
#include "capabilities.hxx"
#include "isolate.hxx"
#include "util/Error.hxx"

#include <daemon/log.h>
#include <daemon/daemonize.h>

#include <assert.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <pthread.h>

#ifdef __linux
#include <sys/prctl.h>

#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
#endif

#include <event.h>

static constexpr cap_value_t cap_keep_list[1] = {
    /* keep the NET_RAW capability to be able to to use the socket
       option IP_TRANSPARENT */
    CAP_NET_RAW,
};

static constexpr struct timeval launch_worker_now = {
    0,
    10000,
};

static constexpr struct timeval launch_worker_delayed = {
    10,
    0,
};

static bool is_watchdog;
static pid_t worker_pid;
static struct event launch_worker_event;

static void
worker_callback(int status, void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    int exit_status = WEXITSTATUS(status);
    if (WIFSIGNALED(status))
        fprintf(stderr, "worker %d died from signal %d%s\n",
                worker_pid, WTERMSIG(status),
                WCOREDUMP(status) ? " (core dumped)" : "");
    else if (exit_status == 0)
        fprintf(stderr, "worker %d exited with success\n",
                worker_pid);
    else
        fprintf(stderr, "worker %d exited with status %d\n",
                worker_pid, exit_status);

    worker_pid = 0;

    if (!instance->should_exit)
        evtimer_add(&launch_worker_event, &launch_worker_delayed);
}

static void
launch_worker_callback(int fd gcc_unused, short event gcc_unused,
                       void *ctx)
{
    assert(is_watchdog);
    assert(worker_pid <= 0);

    struct lb_instance *instance = (struct lb_instance *)ctx;

    /* in libevent 2.0.16, it is necessary to re-add all EV_SIGNAL
       events after forking; this bug is not present in 1.4.13 and
       2.0.19 */
    deinit_signals(instance);
    children_event_del();

    worker_pid = fork();
    if (worker_pid < 0) {
        fprintf(stderr, "Failed to fork: %s\n", strerror(errno));

        init_signals(instance);
        children_event_add();

        evtimer_add(&launch_worker_event, &launch_worker_delayed);
        return;
    }

    if (worker_pid == 0) {
        event_reinit(instance->event_base);
        init_signals(instance);

        children_init();
        all_listeners_event_add(instance);

        enable_all_controls(instance);

        /* run monitors only in the worker process */
        lb_hmonitor_enable();
        return;
    }

    init_signals(instance);
    children_event_add();

    child_register(worker_pid, "worker", worker_callback, instance);
}

static void
shutdown_callback(void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    if (instance->should_exit)
        return;

    instance->should_exit = true;
    deinit_signals(instance);
    thread_pool_stop();

    if (is_watchdog && worker_pid > 0)
        kill(worker_pid, SIGTERM);

    children_shutdown();

    thread_pool_join();
    thread_pool_deinit();

    if (is_watchdog)
        evtimer_del(&launch_worker_event);

    deinit_all_controls(instance);

    while (!instance->connections.empty())
        lb_connection_close(&instance->connections.front());

    deinit_all_listeners(instance);

    lb_hmonitor_deinit();

    pool_commit();

    if (instance->tcp_balancer != nullptr)
        tcp_balancer_free(instance->tcp_balancer);

    if (instance->tcp_stock != nullptr)
        hstock_free(instance->tcp_stock);

    if (instance->balancer != nullptr)
        balancer_free(instance->balancer);

    if (instance->pipe_stock != nullptr)
        stock_free(instance->pipe_stock);

    fb_pool_disable();

    pool_commit();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    daemonize_reopen_logfile();

    unsigned n_ssl_sessions = instance->FlushSSLSessionCache(LONG_MAX);
    daemon_log(3, "flushed %u SSL sessions\n", n_ssl_sessions);

    fb_pool_compress();
}

void
init_signals(struct lb_instance *instance)
{
    signal(SIGPIPE, SIG_IGN);

    shutdown_listener_init(&instance->shutdown_listener,
                           shutdown_callback, instance);

    event_set(&instance->sighup_event, SIGHUP, EV_SIGNAL|EV_PERSIST,
              reload_event_callback, instance);
    event_add(&instance->sighup_event, nullptr);
}

void
deinit_signals(struct lb_instance *instance)
{
    shutdown_listener_deinit(&instance->shutdown_listener);
    event_del(&instance->sighup_event);
}

int main(int argc, char **argv)
{
    struct lb_instance instance;

#ifdef HAVE_OLD_GTHREAD
    /* deprecated in GLib 2.32 */
    g_thread_init(nullptr);
#endif

    instance.pool = pool_new_libc(nullptr, "global");
    tpool_init(instance.pool);

    /* configuration */

    parse_cmdline(&instance.cmdline, instance.pool, argc, argv);

    GError *error = nullptr;
    instance.config = lb_config_load(instance.pool,
                                     instance.cmdline.config_path,
                                     &error);
    if (instance.config == nullptr) {
        fprintf(stderr, "%s\n", error->message);
        g_error_free(error);
        return EXIT_FAILURE;
    }

    if (instance.cmdline.check)
        return EXIT_SUCCESS;

    /* initialize */

    lb_hmonitor_init(instance.pool);

    ssl_global_init();

    direct_global_init();

    instance.event_base = event_init();
    fb_pool_init(true);

    init_signals(&instance);

    /* reduce glibc's thread cancellation overhead */
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);

    children_init();

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.pool,
                                       instance.cmdline.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    instance.pipe_stock = pipe_stock_new(instance.pool);

    failure_init();
    bulldog_init(instance.cmdline.bulldog_path);

    if (!init_all_controls(&instance, &error)) {
        fprintf(stderr, "%s\n", error->message);
        g_error_free(error);
        return EXIT_FAILURE;
    }

    {
        Error error2;
        if (!init_all_listeners(instance, error2)) {
            deinit_all_controls(&instance);
            fprintf(stderr, "%s\n", error2.GetMessage());
            return EXIT_FAILURE;
        }
    }

    /* daemonize */

    if (daemonize() < 0)
        exit(2);

    /* launch the access logger */

    if (!log_global_init(instance.cmdline.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.cmdline.user) < 0)
        return EXIT_FAILURE;

    isolate_from_filesystem();

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_post_setuid(cap_keep_list, G_N_ELEMENTS(cap_keep_list));

#ifdef __linux
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif

    /* main loop */

    if (instance.cmdline.watchdog) {
        /* watchdog */

        all_listeners_event_del(&instance);

        is_watchdog = true;
        evtimer_set(&launch_worker_event, launch_worker_callback, &instance);
        evtimer_add(&launch_worker_event, &launch_worker_now);
    } else {
        /* this is already the worker process: enable monitors here */
        lb_hmonitor_enable();
    }

    event_dispatch();

    /* cleanup */

    children_shutdown();

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    deinit_all_listeners(&instance);
    deinit_all_controls(&instance);

    fb_pool_deinit();

    event_base_free(instance.event_base);

    tpool_deinit();
    delete instance.config;

    gcc_unused int ref = pool_unref(instance.pool);
    assert(ref == 0);
    pool_commit();

    pool_recycler_clear();

    ssl_global_deinit();

    daemonize_cleanup();

    direct_global_deinit();
}
<commit_msg>lb_main: clean up after --check<commit_after>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "tpool.h"
#include "direct.h"
#include "lb_instance.hxx"
#include "lb_setup.hxx"
#include "lb_connection.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "hstock.hxx"
#include "stock.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "log-glue.h"
#include "lb_config.hxx"
#include "lb_hmonitor.hxx"
#include "ssl_init.hxx"
#include "child_manager.hxx"
#include "thread_pool.hxx"
#include "fb_pool.hxx"
#include "capabilities.hxx"
#include "isolate.hxx"
#include "util/Error.hxx"

#include <daemon/log.h>
#include <daemon/daemonize.h>

#include <assert.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <pthread.h>

#ifdef __linux
#include <sys/prctl.h>

#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
#endif

#include <event.h>

static constexpr cap_value_t cap_keep_list[1] = {
    /* keep the NET_RAW capability to be able to to use the socket
       option IP_TRANSPARENT */
    CAP_NET_RAW,
};

static constexpr struct timeval launch_worker_now = {
    0,
    10000,
};

static constexpr struct timeval launch_worker_delayed = {
    10,
    0,
};

static bool is_watchdog;
static pid_t worker_pid;
static struct event launch_worker_event;

static void
worker_callback(int status, void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    int exit_status = WEXITSTATUS(status);
    if (WIFSIGNALED(status))
        fprintf(stderr, "worker %d died from signal %d%s\n",
                worker_pid, WTERMSIG(status),
                WCOREDUMP(status) ? " (core dumped)" : "");
    else if (exit_status == 0)
        fprintf(stderr, "worker %d exited with success\n",
                worker_pid);
    else
        fprintf(stderr, "worker %d exited with status %d\n",
                worker_pid, exit_status);

    worker_pid = 0;

    if (!instance->should_exit)
        evtimer_add(&launch_worker_event, &launch_worker_delayed);
}

static void
launch_worker_callback(int fd gcc_unused, short event gcc_unused,
                       void *ctx)
{
    assert(is_watchdog);
    assert(worker_pid <= 0);

    struct lb_instance *instance = (struct lb_instance *)ctx;

    /* in libevent 2.0.16, it is necessary to re-add all EV_SIGNAL
       events after forking; this bug is not present in 1.4.13 and
       2.0.19 */
    deinit_signals(instance);
    children_event_del();

    worker_pid = fork();
    if (worker_pid < 0) {
        fprintf(stderr, "Failed to fork: %s\n", strerror(errno));

        init_signals(instance);
        children_event_add();

        evtimer_add(&launch_worker_event, &launch_worker_delayed);
        return;
    }

    if (worker_pid == 0) {
        event_reinit(instance->event_base);
        init_signals(instance);

        children_init();
        all_listeners_event_add(instance);

        enable_all_controls(instance);

        /* run monitors only in the worker process */
        lb_hmonitor_enable();
        return;
    }

    init_signals(instance);
    children_event_add();

    child_register(worker_pid, "worker", worker_callback, instance);
}

static void
shutdown_callback(void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    if (instance->should_exit)
        return;

    instance->should_exit = true;
    deinit_signals(instance);
    thread_pool_stop();

    if (is_watchdog && worker_pid > 0)
        kill(worker_pid, SIGTERM);

    children_shutdown();

    thread_pool_join();
    thread_pool_deinit();

    if (is_watchdog)
        evtimer_del(&launch_worker_event);

    deinit_all_controls(instance);

    while (!instance->connections.empty())
        lb_connection_close(&instance->connections.front());

    deinit_all_listeners(instance);

    lb_hmonitor_deinit();

    pool_commit();

    if (instance->tcp_balancer != nullptr)
        tcp_balancer_free(instance->tcp_balancer);

    if (instance->tcp_stock != nullptr)
        hstock_free(instance->tcp_stock);

    if (instance->balancer != nullptr)
        balancer_free(instance->balancer);

    if (instance->pipe_stock != nullptr)
        stock_free(instance->pipe_stock);

    fb_pool_disable();

    pool_commit();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    daemonize_reopen_logfile();

    unsigned n_ssl_sessions = instance->FlushSSLSessionCache(LONG_MAX);
    daemon_log(3, "flushed %u SSL sessions\n", n_ssl_sessions);

    fb_pool_compress();
}

void
init_signals(struct lb_instance *instance)
{
    signal(SIGPIPE, SIG_IGN);

    shutdown_listener_init(&instance->shutdown_listener,
                           shutdown_callback, instance);

    event_set(&instance->sighup_event, SIGHUP, EV_SIGNAL|EV_PERSIST,
              reload_event_callback, instance);
    event_add(&instance->sighup_event, nullptr);
}

void
deinit_signals(struct lb_instance *instance)
{
    shutdown_listener_deinit(&instance->shutdown_listener);
    event_del(&instance->sighup_event);
}

int main(int argc, char **argv)
{
    struct lb_instance instance;

#ifdef HAVE_OLD_GTHREAD
    /* deprecated in GLib 2.32 */
    g_thread_init(nullptr);
#endif

    instance.pool = pool_new_libc(nullptr, "global");
    tpool_init(instance.pool);

    /* configuration */

    parse_cmdline(&instance.cmdline, instance.pool, argc, argv);

    GError *error = nullptr;
    instance.config = lb_config_load(instance.pool,
                                     instance.cmdline.config_path,
                                     &error);
    if (instance.config == nullptr) {
        fprintf(stderr, "%s\n", error->message);
        g_error_free(error);
        return EXIT_FAILURE;
    }

    if (instance.cmdline.check) {
        tpool_deinit();
        delete instance.config;
        pool_unref(instance.pool);
        pool_recycler_clear();
        return EXIT_SUCCESS;
    }

    /* initialize */

    lb_hmonitor_init(instance.pool);

    ssl_global_init();

    direct_global_init();

    instance.event_base = event_init();
    fb_pool_init(true);

    init_signals(&instance);

    /* reduce glibc's thread cancellation overhead */
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);

    children_init();

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.pool,
                                       instance.cmdline.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    instance.pipe_stock = pipe_stock_new(instance.pool);

    failure_init();
    bulldog_init(instance.cmdline.bulldog_path);

    if (!init_all_controls(&instance, &error)) {
        fprintf(stderr, "%s\n", error->message);
        g_error_free(error);
        return EXIT_FAILURE;
    }

    {
        Error error2;
        if (!init_all_listeners(instance, error2)) {
            deinit_all_controls(&instance);
            fprintf(stderr, "%s\n", error2.GetMessage());
            return EXIT_FAILURE;
        }
    }

    /* daemonize */

    if (daemonize() < 0)
        exit(2);

    /* launch the access logger */

    if (!log_global_init(instance.cmdline.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.cmdline.user) < 0)
        return EXIT_FAILURE;

    isolate_from_filesystem();

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_post_setuid(cap_keep_list, G_N_ELEMENTS(cap_keep_list));

#ifdef __linux
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif

    /* main loop */

    if (instance.cmdline.watchdog) {
        /* watchdog */

        all_listeners_event_del(&instance);

        is_watchdog = true;
        evtimer_set(&launch_worker_event, launch_worker_callback, &instance);
        evtimer_add(&launch_worker_event, &launch_worker_now);
    } else {
        /* this is already the worker process: enable monitors here */
        lb_hmonitor_enable();
    }

    event_dispatch();

    /* cleanup */

    children_shutdown();

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    deinit_all_listeners(&instance);
    deinit_all_controls(&instance);

    fb_pool_deinit();

    event_base_free(instance.event_base);

    tpool_deinit();
    delete instance.config;

    gcc_unused int ref = pool_unref(instance.pool);
    assert(ref == 0);
    pool_commit();

    pool_recycler_clear();

    ssl_global_deinit();

    daemonize_cleanup();

    direct_global_deinit();
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "leveldb.h"
#include "util.h"

#include <leveldb/env.h>
#include <leveldb/cache.h>
#include <leveldb/filter_policy.h>
#include <memenv/memenv.h>

#include <boost/filesystem.hpp>

void HandleError(const leveldb::Status &status) throw(leveldb_error) {
    if (status.ok())
        return;
    if (status.IsCorruption())
        throw leveldb_error("Database corrupted");
    if (status.IsIOError())
        throw leveldb_error("Database I/O error");
    if (status.IsNotFound())
        throw leveldb_error("Database entry missing");
    throw leveldb_error("Unknown database error");
}

static leveldb::Options GetOptions(size_t nCacheSize) {
    leveldb::Options options;
    options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
    options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
    options.filter_policy = leveldb::NewBloomFilterPolicy(10);
    options.compression = leveldb::kNoCompression;
    return options;
}

CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) {
    penv = NULL;
    readoptions.verify_checksums = true;
    iteroptions.verify_checksums = true;
    iteroptions.fill_cache = false;
    syncoptions.sync = true;
    options = GetOptions(nCacheSize);
    options.create_if_missing = true;
    if (fMemory) {
        penv = leveldb::NewMemEnv(leveldb::Env::Default());
        options.env = penv;
    } else {
        if (fWipe) {
            printf("Wiping LevelDB in %s\n", path.string().c_str());
            leveldb::DestroyDB(path.string(), options);
        }
        boost::filesystem::create_directory(path);
        printf("Opening LevelDB in %s\n", path.string().c_str());
    }
    leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
    if (!status.ok())
        throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str()));
    printf("Opened LevelDB successfully\n");
}

CLevelDB::~CLevelDB() {
    delete pdb;
    pdb = NULL;
    delete options.filter_policy;
    options.filter_policy = NULL;
    delete options.block_cache;
    options.block_cache = NULL;
    delete penv;
    options.env = NULL;
}

bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) {
    leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
    if (!status.ok()) {
        printf("LevelDB write failure: %s\n", status.ToString().c_str());
        HandleError(status);
        return false;
    }
    return true;
}
<commit_msg>Reduce number of open LevelDB files to 64<commit_after>// Copyright (c) 2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "leveldb.h"
#include "util.h"

#include <leveldb/env.h>
#include <leveldb/cache.h>
#include <leveldb/filter_policy.h>
#include <memenv/memenv.h>

#include <boost/filesystem.hpp>

void HandleError(const leveldb::Status &status) throw(leveldb_error) {
    if (status.ok())
        return;
    if (status.IsCorruption())
        throw leveldb_error("Database corrupted");
    if (status.IsIOError())
        throw leveldb_error("Database I/O error");
    if (status.IsNotFound())
        throw leveldb_error("Database entry missing");
    throw leveldb_error("Unknown database error");
}

static leveldb::Options GetOptions(size_t nCacheSize) {
    leveldb::Options options;
    options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
    options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
    options.filter_policy = leveldb::NewBloomFilterPolicy(10);
    options.compression = leveldb::kNoCompression;
    options.max_open_files = 64;
    return options;
}

CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) {
    penv = NULL;
    readoptions.verify_checksums = true;
    iteroptions.verify_checksums = true;
    iteroptions.fill_cache = false;
    syncoptions.sync = true;
    options = GetOptions(nCacheSize);
    options.create_if_missing = true;
    if (fMemory) {
        penv = leveldb::NewMemEnv(leveldb::Env::Default());
        options.env = penv;
    } else {
        if (fWipe) {
            printf("Wiping LevelDB in %s\n", path.string().c_str());
            leveldb::DestroyDB(path.string(), options);
        }
        boost::filesystem::create_directory(path);
        printf("Opening LevelDB in %s\n", path.string().c_str());
    }
    leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
    if (!status.ok())
        throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str()));
    printf("Opened LevelDB successfully\n");
}

CLevelDB::~CLevelDB() {
    delete pdb;
    pdb = NULL;
    delete options.filter_policy;
    options.filter_policy = NULL;
    delete options.block_cache;
    options.block_cache = NULL;
    delete penv;
    options.env = NULL;
}

bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) {
    leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
    if (!status.ok()) {
        printf("LevelDB write failure: %s\n", status.ToString().c_str());
        HandleError(status);
        return false;
    }
    return true;
}
<|endoftext|>


<commit_before>#include "Options.hpp"

#include "types.hpp"
#include "constants.hpp"
#include "errors.hpp"
#include "utility.hpp"

#include "sys/sysctl.hpp"

#include <iostream>  /* std::cout, std::cerr */
#include <chrono>    /* std::chrono::steady_clock::now() */
#include <thread>    /* std::this_thread::sleep_until() */

#include <sys/resource.h>  /* CPUSTATES */

namespace {

using nih::Option;
using nih::make_Options;

using errors::Exit;
using errors::Exception;
using errors::fail;

using utility::to_value;
using namespace utility::literals;

/**
 * An enum for command line parsing.
 */
enum class OE {
	USAGE,           /**< Print help */
	DUR_LENGTH,      /**< Set the duration of the recording */
	IVAL_POLL,       /**< Set polling interval */
	FILE_PID,        /**< Set pidfile */
	FLAG_VERBOSE,    /**< Verbose output on stderr */
	OPT_UNKNOWN,     /**< Obligatory */
	OPT_NOOPT,       /**< Obligatory */
	OPT_DASH,        /**< Obligatory */
	OPT_LDASH,       /**< Obligatory */
	OPT_DONE         /**< Obligatory */
};

/**
 * The short usage string.
 */
char const * const USAGE = "[-hv] [-l duration] [-p ival] [-P file]";

/**
 * Definitions of command line options.
 */
Option<OE> const OPTIONS[]{
	{OE::USAGE,           'h', "help",    "",         "Show usage and exit"},
	{OE::FLAG_VERBOSE,    'v', "verbose", "",         "Be verbose"},
	{OE::DUR_LENGTH,      'l', "length",  "duration", "The duration of the recording"},
	{OE::IVAL_POLL,       'p', "poll",    "ival",     "The polling interval"},
	{OE::FILE_PID,        'P', "pid",     "file",     "Alternative PID file"},
};

/**
 * Parse command line arguments.
 *
 * @param argc,argv
 *	The command line arguments
 */
void read_args(int const argc, char const * const argv[]) {
	auto getopt = make_Options(argc, argv, USAGE, OPTIONS);

	while (true) switch (getopt()) {
	case OE::USAGE:
		std::cerr << getopt.usage();
		throw Exception{Exit::OK, 0, ""};
	case OE::OPT_UNKNOWN:
	case OE::OPT_NOOPT:
	case OE::OPT_DASH:
	case OE::OPT_LDASH:
		fail(Exit::ECLARG, 0, "unexpected command line argument: "_s +
		                      getopt[0] + "\n\n" + getopt.usage());
	case OE::OPT_DONE:
	default:
		return;
	}
}

using namespace types;
using namespace constants;

} /* namespace */

int main(int argc, char * argv[]) {
	try {
		read_args(argc, argv);
	} catch (Exception & e) {
		if (e.msg != "") {
			std::cerr << "loadrec: " << e.msg << '\n';
		}
		return to_value(e.exitcode);
	}
	sys::ctl::SysctlOnce<coreid_t, 2> const ncpu{0, {CTL_HW, HW_NCPU}};
	sys::ctl::Sysctl<2> const cp_times_ctl = {CP_TIMES};

	auto cp_times = std::unique_ptr<cptime_t[][CPUSTATES]>(
	    new cptime_t[2 * ncpu][CPUSTATES]{});

	auto time = std::chrono::steady_clock::now();
	//auto const start = time;
	size_t sample = 0;
	while (true) {
		std::this_thread::sleep_until(time += ms{50});
		cp_times_ctl.get(cp_times[sample * ncpu],
		                 ncpu * sizeof(cp_times[0]));
		for (size_t i = 0; i < ncpu; ++i) {
			std::cout << "cpu" << i << ':';
			for (size_t q = 0; q < CPUSTATES; ++q) {
				std::cout << ' '
				          << (cp_times[sample * ncpu + i][q] -
				              cp_times[((sample + 1) % 2) * ncpu + i][q]);
			}
			std::cout << '\n';
		}
		sample = (sample + 1) % 2;
	}
}

<commit_msg>Add output argument and rename duration<commit_after>#include "Options.hpp"

#include "types.hpp"
#include "constants.hpp"
#include "errors.hpp"
#include "utility.hpp"

#include "sys/sysctl.hpp"

#include <iostream>  /* std::cout, std::cerr */
#include <chrono>    /* std::chrono::steady_clock::now() */
#include <thread>    /* std::this_thread::sleep_until() */

#include <sys/resource.h>  /* CPUSTATES */

namespace {

using nih::Option;
using nih::make_Options;

using errors::Exit;
using errors::Exception;
using errors::fail;

using utility::to_value;
using namespace utility::literals;

/**
 * An enum for command line parsing.
 */
enum class OE {
	USAGE,           /**< Print help */
	IVAL_DURATION,   /**< Set the duration of the recording */
	IVAL_POLL,       /**< Set polling interval */
	FILE_OUTPUT,     /**< Set output file */
	FILE_PID,        /**< Set pidfile */
	FLAG_VERBOSE,    /**< Verbose output on stderr */
	OPT_UNKNOWN,     /**< Obligatory */
	OPT_NOOPT,       /**< Obligatory */
	OPT_DASH,        /**< Obligatory */
	OPT_LDASH,       /**< Obligatory */
	OPT_DONE         /**< Obligatory */
};

/**
 * The short usage string.
 */
char const * const USAGE = "[-hv] [-d ival] [-p ival] [-o file] [-P file]";

/**
 * Definitions of command line options.
 */
Option<OE> const OPTIONS[]{
	{OE::USAGE,         'h', "help",     "",     "Show usage and exit"},
	{OE::FLAG_VERBOSE,  'v', "verbose",  "",     "Be verbose"},
	{OE::IVAL_DURATION, 'd', "duration", "ival", "The duration of the recording"},
	{OE::IVAL_POLL,     'p', "poll",     "ival", "The polling interval"},
	{OE::FILE_OUTPUT,   'o', "output",   "file", "Output to file"},
	{OE::FILE_PID,      'P', "pid",      "file", "Alternative PID file"}
};

/**
 * Parse command line arguments.
 *
 * @param argc,argv
 *	The command line arguments
 */
void read_args(int const argc, char const * const argv[]) {
	auto getopt = make_Options(argc, argv, USAGE, OPTIONS);

	while (true) switch (getopt()) {
	case OE::USAGE:
		std::cerr << getopt.usage();
		throw Exception{Exit::OK, 0, ""};
	case OE::OPT_UNKNOWN:
	case OE::OPT_NOOPT:
	case OE::OPT_DASH:
	case OE::OPT_LDASH:
		fail(Exit::ECLARG, 0, "unexpected command line argument: "_s +
		                      getopt[0] + "\n\n" + getopt.usage());
	case OE::OPT_DONE:
	default:
		return;
	}
}

using namespace types;
using namespace constants;

} /* namespace */

int main(int argc, char * argv[]) {
	try {
		read_args(argc, argv);
	} catch (Exception & e) {
		if (e.msg != "") {
			std::cerr << "loadrec: " << e.msg << '\n';
		}
		return to_value(e.exitcode);
	}
	sys::ctl::SysctlOnce<coreid_t, 2> const ncpu{0, {CTL_HW, HW_NCPU}};
	sys::ctl::Sysctl<2> const cp_times_ctl = {CP_TIMES};

	auto cp_times = std::unique_ptr<cptime_t[][CPUSTATES]>(
	    new cptime_t[2 * ncpu][CPUSTATES]{});

	auto time = std::chrono::steady_clock::now();
	//auto const start = time;
	size_t sample = 0;
	while (true) {
		std::this_thread::sleep_until(time += ms{50});
		cp_times_ctl.get(cp_times[sample * ncpu],
		                 ncpu * sizeof(cp_times[0]));
		for (size_t i = 0; i < ncpu; ++i) {
			std::cout << "cpu" << i << ':';
			for (size_t q = 0; q < CPUSTATES; ++q) {
				std::cout << ' '
				          << (cp_times[sample * ncpu + i][q] -
				              cp_times[((sample + 1) % 2) * ncpu + i][q]);
			}
			std::cout << '\n';
		}
		sample = (sample + 1) % 2;
	}
}

<|endoftext|>