<commit_before>/*
 *    Persons Model Contact Item
 *    Represents person's contact item in the model
 *    Copyright (C) 2012  Martin Klapetek <martin.klapetek@gmail.com>
 *    Copyright (C) 2012  Aleix Pol Gonzalez <aleixpol@blue-systems.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.1 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.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "contactitem.h"
#include "personpluginmanager.h"
#include "basepersonsdatasource.h"
#include "personitem.h"

#include <QAction>

#include <KIcon>
#include <KDebug>

#include <Nepomuk2/Resource>
#include <Nepomuk2/Variant>
#include <Nepomuk2/ResourceManager>

#include <Nepomuk2/Vocabulary/NCO>
#include <Nepomuk2/Vocabulary/NIE>
#include <Soprano/Vocabulary/NAO>
#include <Soprano/Model>
#include <Soprano/QueryResultIterator>

using namespace KPeople;

class ContactItemPrivate {
public:
    QUrl uri;
    QHash<int, QVariant> data;

    bool isBeingUpdated;

    ///@returns a list of the roles that we'll expect to be a QVariantList
    static QSet<int> listRoles() {
        static QSet<int> s_listRoles;
        if(s_listRoles.isEmpty()) {
            //TODO: document this in persons-model.h
            s_listRoles.insert(PersonsModel::PhotosRole);
            s_listRoles.insert(PersonsModel::EmailsRole);
            s_listRoles.insert(PersonsModel::PhonesRole);
        }
        return s_listRoles;
    }
};

ContactItem::ContactItem(const QUrl &uri)
: d_ptr(new ContactItemPrivate)
{
    //     setData(uri, PersonsModel::UriRole);
    d_ptr->uri = uri;
    d_ptr->isBeingUpdated = false;
}

ContactItem::~ContactItem()
{
    delete d_ptr;
}

void ContactItem::setContactData(int role, const QVariant &v)
{
    Q_D(ContactItem);

    if (v.isNull()) {
        //don't insert empty values, in case of list roles this
        //would insert QVariant(QVariantList(QVariant())),
        //empty QVariant will be returned from data(...) instead
        return;
    }

    QHash< int, QVariant >::iterator it = d->data.find(role);
    QVariant value = d->listRoles().contains(role) ? (QVariantList() << v) : v;
    if (it == d->data.end()) {
        d->data.insert(role, value);
        emitDataChanged();
    } else if (*it != value) {
        Q_ASSERT(value.type() == it->type());
        *it = value;
        emitDataChanged();
    }
}

//TODO update so value can be a list. This will then concat onto any existing list.

void ContactItem::addContactData(int role, const QVariant &value)
{
    Q_D(ContactItem);
    if (value.isNull()) {
        //don't insert empty values, in case of list roles this
        //would insert QVariant(QVariantList(QVariant())),
        //empty QVariant will be returned from data(...) instead
        return;
    }
    QHash<int, QVariant>::iterator it = d->data.find(role);
    if (!d->listRoles().contains(role)) {
        setContactData(role, value);
    } else {
        if (it == d->data.end()) {
            d->data.insert(role, QVariantList() << value);
            emitDataChanged();
        } else if (*it != value) {
            Q_ASSERT(it->type() == QVariant::List);
            QVariantList current = it->toList();
            Q_ASSERT(current.isEmpty() || current.first().type() == value.type());
            current.append(value);
            *it = current;
            emitDataChanged();
        }
    }
}

void ContactItem::removeData(int role)
{
    Q_D(ContactItem);
    if (d->data.remove(role)) {
        emitDataChanged();
    }
}

QUrl ContactItem::uri() const
{
    Q_D(const ContactItem);
    return d->uri;
}

QVariant ContactItem::data(int role) const
{
    Q_D(const ContactItem);

    QHash<int, QVariant>::const_iterator it = d->data.constFind(role);
    if (it!=d->data.constEnd()) {
        return *it;
    }

    switch(role) {
        case Qt::DisplayRole:
            if (!data(PersonsModel::NicknamesRole).isNull()) {
                return data(PersonsModel::NicknamesRole);
            }
            if (!data(PersonsModel::FullNamesRole).isNull()) {
                return data(PersonsModel::FullNamesRole);
            }
            if (!data(PersonsModel::EmailsRole).isNull()) {
                return data(PersonsModel::EmailsRole);
            }
            if (!data(PersonsModel::IMsRole).isNull()) {
                return data(PersonsModel::IMsRole);
            }
            if (!data(PersonsModel::PhonesRole).isNull()) {
                return data(PersonsModel::PhonesRole);
            }

            return QString("Unknown contact");
        case Qt::DecorationRole: {
            const QVariantList photos = d->data.value(PersonsModel::PhotosRole).toList();
            return photos.isEmpty() ? KIcon("im-user") : KIcon(photos.first().toUrl().toLocalFile());
        }
        case PersonsModel::UriRole: return d->uri; break;
        case PersonsModel::PresenceTypeRole:
        case PersonsModel::PresenceDisplayRole:
        case PersonsModel::PresenceDecorationRole:
        case PersonsModel::PresenceIconNameRole:
            return PersonPluginManager::presencePlugin()->dataForContact(data(PersonsModel::IMsRole).toString(), role);
    }

    return QStandardItem::data(role);
}

void ContactItem::loadData()
{
    Q_D(ContactItem);

    if (d->isBeingUpdated) {
        kDebug() << "Contact already being updated";
        return;
    }

    d->data.clear();

    PersonsModel *m = qobject_cast<PersonsModel*>(model());

    Q_ASSERT(m); //crash if this contact is not part of the model

    d->isBeingUpdated = true;
    m->updateContact(this);
}

void ContactItem::finishLoadingData()
{
    Q_D(ContactItem);
    d->isBeingUpdated = false;
    emitDataChanged();
    dynamic_cast<PersonItem*>(parent())->contactDataChanged();
}
<commit_msg>Unknown contact string should be translatable<commit_after>/*
 *    Persons Model Contact Item
 *    Represents person's contact item in the model
 *    Copyright (C) 2012  Martin Klapetek <martin.klapetek@gmail.com>
 *    Copyright (C) 2012  Aleix Pol Gonzalez <aleixpol@blue-systems.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.1 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.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "contactitem.h"
#include "personpluginmanager.h"
#include "basepersonsdatasource.h"
#include "personitem.h"

#include <QAction>

#include <KIcon>
#include <KDebug>

#include <Nepomuk2/Resource>
#include <Nepomuk2/Variant>
#include <Nepomuk2/ResourceManager>

#include <Nepomuk2/Vocabulary/NCO>
#include <Nepomuk2/Vocabulary/NIE>
#include <Soprano/Vocabulary/NAO>
#include <Soprano/Model>
#include <Soprano/QueryResultIterator>

using namespace KPeople;

class ContactItemPrivate {
public:
    QUrl uri;
    QHash<int, QVariant> data;

    bool isBeingUpdated;

    ///@returns a list of the roles that we'll expect to be a QVariantList
    static QSet<int> listRoles() {
        static QSet<int> s_listRoles;
        if(s_listRoles.isEmpty()) {
            //TODO: document this in persons-model.h
            s_listRoles.insert(PersonsModel::PhotosRole);
            s_listRoles.insert(PersonsModel::EmailsRole);
            s_listRoles.insert(PersonsModel::PhonesRole);
        }
        return s_listRoles;
    }
};

ContactItem::ContactItem(const QUrl &uri)
: d_ptr(new ContactItemPrivate)
{
    //     setData(uri, PersonsModel::UriRole);
    d_ptr->uri = uri;
    d_ptr->isBeingUpdated = false;
}

ContactItem::~ContactItem()
{
    delete d_ptr;
}

void ContactItem::setContactData(int role, const QVariant &v)
{
    Q_D(ContactItem);

    if (v.isNull()) {
        //don't insert empty values, in case of list roles this
        //would insert QVariant(QVariantList(QVariant())),
        //empty QVariant will be returned from data(...) instead
        return;
    }

    QHash< int, QVariant >::iterator it = d->data.find(role);
    QVariant value = d->listRoles().contains(role) ? (QVariantList() << v) : v;
    if (it == d->data.end()) {
        d->data.insert(role, value);
        emitDataChanged();
    } else if (*it != value) {
        Q_ASSERT(value.type() == it->type());
        *it = value;
        emitDataChanged();
    }
}

//TODO update so value can be a list. This will then concat onto any existing list.

void ContactItem::addContactData(int role, const QVariant &value)
{
    Q_D(ContactItem);
    if (value.isNull()) {
        //don't insert empty values, in case of list roles this
        //would insert QVariant(QVariantList(QVariant())),
        //empty QVariant will be returned from data(...) instead
        return;
    }
    QHash<int, QVariant>::iterator it = d->data.find(role);
    if (!d->listRoles().contains(role)) {
        setContactData(role, value);
    } else {
        if (it == d->data.end()) {
            d->data.insert(role, QVariantList() << value);
            emitDataChanged();
        } else if (*it != value) {
            Q_ASSERT(it->type() == QVariant::List);
            QVariantList current = it->toList();
            Q_ASSERT(current.isEmpty() || current.first().type() == value.type());
            current.append(value);
            *it = current;
            emitDataChanged();
        }
    }
}

void ContactItem::removeData(int role)
{
    Q_D(ContactItem);
    if (d->data.remove(role)) {
        emitDataChanged();
    }
}

QUrl ContactItem::uri() const
{
    Q_D(const ContactItem);
    return d->uri;
}

QVariant ContactItem::data(int role) const
{
    Q_D(const ContactItem);

    QHash<int, QVariant>::const_iterator it = d->data.constFind(role);
    if (it!=d->data.constEnd()) {
        return *it;
    }

    switch(role) {
        case Qt::DisplayRole:
            if (!data(PersonsModel::NicknamesRole).isNull()) {
                return data(PersonsModel::NicknamesRole);
            }
            if (!data(PersonsModel::FullNamesRole).isNull()) {
                return data(PersonsModel::FullNamesRole);
            }
            if (!data(PersonsModel::EmailsRole).isNull()) {
                return data(PersonsModel::EmailsRole);
            }
            if (!data(PersonsModel::IMsRole).isNull()) {
                return data(PersonsModel::IMsRole);
            }
            if (!data(PersonsModel::PhonesRole).isNull()) {
                return data(PersonsModel::PhonesRole);
            }

            return i18n("Unknown contact");
        case Qt::DecorationRole: {
            const QVariantList photos = d->data.value(PersonsModel::PhotosRole).toList();
            return photos.isEmpty() ? KIcon("im-user") : KIcon(photos.first().toUrl().toLocalFile());
        }
        case PersonsModel::UriRole: return d->uri; break;
        case PersonsModel::PresenceTypeRole:
        case PersonsModel::PresenceDisplayRole:
        case PersonsModel::PresenceDecorationRole:
        case PersonsModel::PresenceIconNameRole:
            return PersonPluginManager::presencePlugin()->dataForContact(data(PersonsModel::IMsRole).toString(), role);
    }

    return QStandardItem::data(role);
}

void ContactItem::loadData()
{
    Q_D(ContactItem);

    if (d->isBeingUpdated) {
        kDebug() << "Contact already being updated";
        return;
    }

    d->data.clear();

    PersonsModel *m = qobject_cast<PersonsModel*>(model());

    Q_ASSERT(m); //crash if this contact is not part of the model

    d->isBeingUpdated = true;
    m->updateContact(this);
}

void ContactItem::finishLoadingData()
{
    Q_D(ContactItem);
    d->isBeingUpdated = false;
    emitDataChanged();
    dynamic_cast<PersonItem*>(parent())->contactDataChanged();
}
<|endoftext|>


<commit_before>#ifndef REDIS_REDIS_GRAMMAR_HPP_
#define REDIS_REDIS_GRAMMAR_HPP_

#include "redis/redis_proto.hpp"
#include "redis/redis.hpp"
#include "redis/redis_ext.hpp"
#include "redis/pubsub.hpp"

#include <boost/variant.hpp>

// Spirit QI
// Note this result could have been achieved with <boost/spirit/include/qi.hpp> but I expanded
// it out in the hopes that this would reduce compile time, it did by about half a second
#include <boost/spirit/home/qi/action/action.hpp>
#include <boost/spirit/home/qi/char/char.hpp>
#include <boost/spirit/home/qi/directive/no_case.hpp>
#include <boost/spirit/home/qi/directive/repeat.hpp>
#include <boost/spirit/home/qi/directive/as.hpp>
#include <boost/spirit/home/qi/nonterminal/rule.hpp>
#include <boost/spirit/home/qi/nonterminal/grammar.hpp>
#include <boost/spirit/home/qi/numeric/uint.hpp>
#include <boost/spirit/home/qi/numeric/int.hpp>
#include <boost/spirit/home/qi/numeric/real.hpp>
#include <boost/spirit/home/qi/operator/sequence.hpp>
#include <boost/spirit/home/qi/operator/alternative.hpp>
#include <boost/spirit/home/qi/operator/kleene.hpp>
#include <boost/spirit/home/qi/operator/plus.hpp>
#include <boost/spirit/home/qi/operator/not_predicate.hpp>
#include <boost/spirit/home/qi/auxiliary/eps.hpp>
#include <boost/spirit/home/qi/parse.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/qi/string/lit.hpp>

#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>

//These aliases greatly reduce typeing. I avoided "using namespce qi" to avoid any
//confusion in the code as to where some of these weird spirit symbols came from
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace px = boost::phoenix;

template <class Iterator>
struct redis_grammar : qi::grammar<Iterator>, redis_output_writer, redis_ext {
    redis_grammar(tcp_conn_t *conn, namespace_interface_t<redis_protocol_t> *intface, pubsub_runtime_t *runtime_);

private:
    pubsub_runtime_t *runtime;

    // Support rules
    qi::rule<Iterator> eol;
    qi::rule<Iterator, unsigned(unsigned)> args;
    qi::rule<Iterator, unsigned()> args_n;
    qi::rule<Iterator, void(std::string)> cname;
    qi::rule<Iterator, unsigned()> arg_bytes;
    qi::rule<Iterator, std::string(unsigned)> string_data;
    qi::rule<Iterator, std::string(), qi::locals<unsigned> > string_arg;
    qi::rule<Iterator, unsigned()> unsigned_data;
    qi::rule<Iterator, unsigned()> unsigned_arg;
    qi::rule<Iterator, int()> int_data;
    qi::rule<Iterator, int()> int_arg;
    qi::rule<Iterator, float()> float_data;
    qi::rule<Iterator, float()> float_arg;

    qi::rule<Iterator, void(unsigned, std::string)> command;
    qi::rule<Iterator, std::vector<std::string>(std::string), qi::locals<unsigned> > command_n;
    qi::rule<Iterator, void(), qi::locals<unsigned> > arbitrary_command;

    // Command blocks
    qi::rule<Iterator> commands;
    qi::rule<Iterator> keys1;
    qi::rule<Iterator> keys2;
    qi::rule<Iterator> strings1;
    qi::rule<Iterator> strings2;
    qi::rule<Iterator> hashes1;
    qi::rule<Iterator> hashes2;
    qi::rule<Iterator> start;
    qi::rule<Iterator> sets1;

    qi::rule<Iterator> sets2;
    qi::rule<Iterator> lists1;
    qi::rule<Iterator> lists2;
    qi::rule<Iterator> sortedsets1;
    qi::rule<Iterator> sortedsets2;
    qi::rule<Iterator> sortedsets3;
    qi::rule<Iterator> pubsub_ext;
    qi::rule<Iterator> pubsub;

    // Pub/sub support

    // Randomly generated (and so hopefully unique) id for this connection. Used to identify this connection
    // when subscribing and unscribing from channels
    uint64_t connection_id;
    uint64_t subscribed_channels;

    void first_subscribe(std::vector<std::string> &channels, bool patterned);

    void subscribe(std::vector<std::string> &channels, bool patterned);

    void unsubscribe(std::vector<std::string> &channels, bool patterned);

    void publish(std::string &channel, std::string &message);

    void parse_pubsub();

    void pubsub_error();

    // Constructor helpers, split between files to make compilation faster.
    void help_construct_1();
    void help_construct_2();
    void help_construct_3();
    void help_construct_4a();
    void help_construct_4b();
    void help_construct_4c();
    void help_construct_5();
    void help_construct_6();
};

#endif  // REDIS_REDIS_GRAMMAR_HPP_
<commit_msg>Removed a include.<commit_after>#ifndef REDIS_REDIS_GRAMMAR_HPP_
#define REDIS_REDIS_GRAMMAR_HPP_

#include "redis/redis.hpp"
#include "redis/redis_ext.hpp"
#include "redis/pubsub.hpp"

#include <boost/variant.hpp>

// Spirit QI
// Note this result could have been achieved with <boost/spirit/include/qi.hpp> but I expanded
// it out in the hopes that this would reduce compile time, it did by about half a second
#include <boost/spirit/home/qi/action/action.hpp>
#include <boost/spirit/home/qi/char/char.hpp>
#include <boost/spirit/home/qi/directive/no_case.hpp>
#include <boost/spirit/home/qi/directive/repeat.hpp>
#include <boost/spirit/home/qi/directive/as.hpp>
#include <boost/spirit/home/qi/nonterminal/rule.hpp>
#include <boost/spirit/home/qi/nonterminal/grammar.hpp>
#include <boost/spirit/home/qi/numeric/uint.hpp>
#include <boost/spirit/home/qi/numeric/int.hpp>
#include <boost/spirit/home/qi/numeric/real.hpp>
#include <boost/spirit/home/qi/operator/sequence.hpp>
#include <boost/spirit/home/qi/operator/alternative.hpp>
#include <boost/spirit/home/qi/operator/kleene.hpp>
#include <boost/spirit/home/qi/operator/plus.hpp>
#include <boost/spirit/home/qi/operator/not_predicate.hpp>
#include <boost/spirit/home/qi/auxiliary/eps.hpp>
#include <boost/spirit/home/qi/parse.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/qi/string/lit.hpp>

#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>

//These aliases greatly reduce typeing. I avoided "using namespce qi" to avoid any
//confusion in the code as to where some of these weird spirit symbols came from
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace px = boost::phoenix;

template <class Iterator>
struct redis_grammar : qi::grammar<Iterator>, redis_output_writer, redis_ext {
    redis_grammar(tcp_conn_t *conn, namespace_interface_t<redis_protocol_t> *intface, pubsub_runtime_t *runtime_);

private:
    pubsub_runtime_t *runtime;

    // Support rules
    qi::rule<Iterator> eol;
    qi::rule<Iterator, unsigned(unsigned)> args;
    qi::rule<Iterator, unsigned()> args_n;
    qi::rule<Iterator, void(std::string)> cname;
    qi::rule<Iterator, unsigned()> arg_bytes;
    qi::rule<Iterator, std::string(unsigned)> string_data;
    qi::rule<Iterator, std::string(), qi::locals<unsigned> > string_arg;
    qi::rule<Iterator, unsigned()> unsigned_data;
    qi::rule<Iterator, unsigned()> unsigned_arg;
    qi::rule<Iterator, int()> int_data;
    qi::rule<Iterator, int()> int_arg;
    qi::rule<Iterator, float()> float_data;
    qi::rule<Iterator, float()> float_arg;

    qi::rule<Iterator, void(unsigned, std::string)> command;
    qi::rule<Iterator, std::vector<std::string>(std::string), qi::locals<unsigned> > command_n;
    qi::rule<Iterator, void(), qi::locals<unsigned> > arbitrary_command;

    // Command blocks
    qi::rule<Iterator> commands;
    qi::rule<Iterator> keys1;
    qi::rule<Iterator> keys2;
    qi::rule<Iterator> strings1;
    qi::rule<Iterator> strings2;
    qi::rule<Iterator> hashes1;
    qi::rule<Iterator> hashes2;
    qi::rule<Iterator> start;
    qi::rule<Iterator> sets1;

    qi::rule<Iterator> sets2;
    qi::rule<Iterator> lists1;
    qi::rule<Iterator> lists2;
    qi::rule<Iterator> sortedsets1;
    qi::rule<Iterator> sortedsets2;
    qi::rule<Iterator> sortedsets3;
    qi::rule<Iterator> pubsub_ext;
    qi::rule<Iterator> pubsub;

    // Pub/sub support

    // Randomly generated (and so hopefully unique) id for this connection. Used to identify this connection
    // when subscribing and unscribing from channels
    uint64_t connection_id;
    uint64_t subscribed_channels;

    void first_subscribe(std::vector<std::string> &channels, bool patterned);

    void subscribe(std::vector<std::string> &channels, bool patterned);

    void unsubscribe(std::vector<std::string> &channels, bool patterned);

    void publish(std::string &channel, std::string &message);

    void parse_pubsub();

    void pubsub_error();

    // Constructor helpers, split between files to make compilation faster.
    void help_construct_1();
    void help_construct_2();
    void help_construct_3();
    void help_construct_4a();
    void help_construct_4b();
    void help_construct_4c();
    void help_construct_5();
    void help_construct_6();
};

#endif  // REDIS_REDIS_GRAMMAR_HPP_
<|endoftext|>


<commit_before>//: ----------------------------------------------------------------------------
//: Copyright (C) 2015 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: \file:    cb.cc
//: \details: TODO
//: \author:  Reed P. Morrison
//: \date:    07/20/2015
//:
//:   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.
//:
//: ----------------------------------------------------------------------------

//: ----------------------------------------------------------------------------
//: Includes
//: ----------------------------------------------------------------------------
#include "cb.h"
#include "ndebug.h"
#include "clnt_session.h"
#include "hlx/rqst.h"
#include "hlx/resp.h"
#include "hlx/trace.h"
#include "hlx/status.h"
#include <string.h>

//: ----------------------------------------------------------------------------
//: Macros
//: ----------------------------------------------------------------------------
#define CALC_OFFSET(_hmsg, _at) \
        ((_at - _hmsg->m_cur_buf) + _hmsg->m_cur_off)

#define CHECK_FOR_NULL_OK(_data) \
        do {\
                if(!_data) {\
                        return 0;\
                }\
        } while(0);

#define CHECK_FOR_NULL_ERROR(_data) \
        do {\
                if(!_data) {\
                        return HLX_STATUS_ERROR;\
                }\
        } while(0);

namespace ns_hlx {

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int32_t http_parse(void *a_data, char *a_buf, uint32_t a_len, uint64_t a_off)
{
        CHECK_FOR_NULL_ERROR(a_data);
        hmsg *l_hmsg = static_cast<hmsg *>(a_data);
        l_hmsg->m_cur_off = a_off;
        l_hmsg->m_cur_buf = a_buf;
        size_t l_parse_status = 0;
        //NDBG_PRINT("%sHTTP_PARSER%s: m_read_buf: %p, m_read_buf_idx: %d, l_bytes_read: %d\n",
        //                ANSI_COLOR_BG_MAGENTA, ANSI_COLOR_OFF,
        //                a_buf, (int)a_off, (int)a_len);
        l_parse_status = http_parser_execute(l_hmsg->m_http_parser,
                                             l_hmsg->m_http_parser_settings,
                                             a_buf,
                                             a_len);
        //NDBG_PRINT("STATUS: %lu\n", l_parse_status);
        //m_read_buf_idx += l_bytes_read;
        if(l_parse_status < (size_t)a_len)
        {
                TRC_ERROR("Parse error.  Reason: %s: %s\n",
                           http_errno_name((enum http_errno)l_hmsg->m_http_parser->http_errno),
                           http_errno_description((enum http_errno)l_hmsg->m_http_parser->http_errno));
                return HLX_STATUS_ERROR;
        }
        return HLX_STATUS_OK;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_message_begin(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_url(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save && (l_hmsg->get_type() == hmsg::TYPE_RQST))
        {
                rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                l_rqst->m_p_url.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_rqst->m_p_url.m_len = a_length;
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_status(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        //l_clnt->m_status_code = a_parser->status_code;
        if(l_hmsg->get_type() == hmsg::TYPE_RESP)
        {
                resp *l_resp = static_cast<resp *>(l_hmsg);
                l_resp->set_status((http_status_t)a_parser->status_code);
                if(l_hmsg->m_save)
                {
                        l_resp->m_p_status.m_off = CALC_OFFSET(l_hmsg, a_at);
                        l_resp->m_p_status.m_len = a_length;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_header_field(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                cr_struct l_cr;
                l_cr.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_cr.m_len = a_length;
                l_hmsg->m_p_h_list_key.push_back(l_cr);

                // -----------------------------------------
                // signalling for expect headers
                // -----------------------------------------
                if(l_hmsg->get_type() == hmsg::TYPE_RQST)
                {
                        rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                        const char l_exp[] = "Expect";
                        uint32_t l_len = ((sizeof(l_exp)>a_length)?a_length:sizeof(l_exp));
                        if(strncasecmp(l_exp, a_at, l_len) == 0)
                        {
                                l_rqst->m_expect = true;
                        }
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_header_value(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                cr_struct l_cr;
                l_cr.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_cr.m_len = a_length;
                l_hmsg->m_p_h_list_val.push_back(l_cr);
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_headers_complete(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                l_hmsg->m_http_major = a_parser->http_major;
                l_hmsg->m_http_minor = a_parser->http_minor;
                if(l_hmsg->get_type() == hmsg::TYPE_RQST)
                {
                        rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                        l_rqst->m_method = a_parser->method;
                }
        }
        if(l_hmsg->get_type() == hmsg::TYPE_RESP)
        {
                if(!l_hmsg->m_expect_resp_body_flag)
                {
                        return 1;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_body(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                if(!l_hmsg->m_p_body.m_off)
                {
                        l_hmsg->m_p_body.m_off = CALC_OFFSET(l_hmsg, a_at);
                        l_hmsg->m_p_body.m_len = a_length;
                }
                else
                {
                        l_hmsg->m_p_body.m_len += a_length;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_message_complete(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        l_hmsg->m_complete = true;
        l_hmsg->m_supports_keep_alives = http_should_keep_alive(a_parser);
        return 0;
}

} //namespace ns_hlx {
<commit_msg>Speed up Expect header checking with short circuits.<commit_after>//: ----------------------------------------------------------------------------
//: Copyright (C) 2015 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: \file:    cb.cc
//: \details: TODO
//: \author:  Reed P. Morrison
//: \date:    07/20/2015
//:
//:   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.
//:
//: ----------------------------------------------------------------------------

//: ----------------------------------------------------------------------------
//: Includes
//: ----------------------------------------------------------------------------
#include "cb.h"
#include "ndebug.h"
#include "clnt_session.h"
#include "hlx/rqst.h"
#include "hlx/resp.h"
#include "hlx/trace.h"
#include "hlx/status.h"
#include <string.h>

//: ----------------------------------------------------------------------------
//: Macros
//: ----------------------------------------------------------------------------
#define CALC_OFFSET(_hmsg, _at) \
        ((_at - _hmsg->m_cur_buf) + _hmsg->m_cur_off)

#define CHECK_FOR_NULL_OK(_data) \
        do {\
                if(!_data) {\
                        return 0;\
                }\
        } while(0);

#define CHECK_FOR_NULL_ERROR(_data) \
        do {\
                if(!_data) {\
                        return HLX_STATUS_ERROR;\
                }\
        } while(0);

namespace ns_hlx {

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int32_t http_parse(void *a_data, char *a_buf, uint32_t a_len, uint64_t a_off)
{
        CHECK_FOR_NULL_ERROR(a_data);
        hmsg *l_hmsg = static_cast<hmsg *>(a_data);
        l_hmsg->m_cur_off = a_off;
        l_hmsg->m_cur_buf = a_buf;
        size_t l_parse_status = 0;
        //NDBG_PRINT("%sHTTP_PARSER%s: m_read_buf: %p, m_read_buf_idx: %d, l_bytes_read: %d\n",
        //                ANSI_COLOR_BG_MAGENTA, ANSI_COLOR_OFF,
        //                a_buf, (int)a_off, (int)a_len);
        l_parse_status = http_parser_execute(l_hmsg->m_http_parser,
                                             l_hmsg->m_http_parser_settings,
                                             a_buf,
                                             a_len);
        //NDBG_PRINT("STATUS: %lu\n", l_parse_status);
        //m_read_buf_idx += l_bytes_read;
        if(l_parse_status < (size_t)a_len)
        {
                TRC_ERROR("Parse error.  Reason: %s: %s\n",
                           http_errno_name((enum http_errno)l_hmsg->m_http_parser->http_errno),
                           http_errno_description((enum http_errno)l_hmsg->m_http_parser->http_errno));
                return HLX_STATUS_ERROR;
        }
        return HLX_STATUS_OK;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_message_begin(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_url(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save && (l_hmsg->get_type() == hmsg::TYPE_RQST))
        {
                rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                l_rqst->m_p_url.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_rqst->m_p_url.m_len = a_length;
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_status(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        //l_clnt->m_status_code = a_parser->status_code;
        if(l_hmsg->get_type() == hmsg::TYPE_RESP)
        {
                resp *l_resp = static_cast<resp *>(l_hmsg);
                l_resp->set_status((http_status_t)a_parser->status_code);
                if(l_hmsg->m_save)
                {
                        l_resp->m_p_status.m_off = CALC_OFFSET(l_hmsg, a_at);
                        l_resp->m_p_status.m_len = a_length;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_header_field(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                cr_struct l_cr;
                l_cr.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_cr.m_len = a_length;
                l_hmsg->m_p_h_list_key.push_back(l_cr);

                // signalling for expect headers
                if(l_hmsg->get_type() == hmsg::TYPE_RQST)
                {
                        const char l_exp[] = "Expect";
                        if((a_length == (sizeof(l_exp)-1)) && (a_at[0] == 'E'))
                        {
                                if(strncasecmp(l_exp, a_at, a_length) == 0)
                                {
                                        rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                                        l_rqst->m_expect = true;
                                }
                        }
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_header_value(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                cr_struct l_cr;
                l_cr.m_off = CALC_OFFSET(l_hmsg, a_at);
                l_cr.m_len = a_length;
                l_hmsg->m_p_h_list_val.push_back(l_cr);
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_headers_complete(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                l_hmsg->m_http_major = a_parser->http_major;
                l_hmsg->m_http_minor = a_parser->http_minor;
                if(l_hmsg->get_type() == hmsg::TYPE_RQST)
                {
                        rqst *l_rqst = static_cast<rqst *>(l_hmsg);
                        l_rqst->m_method = a_parser->method;
                }
        }
        if(l_hmsg->get_type() == hmsg::TYPE_RESP)
        {
                if(!l_hmsg->m_expect_resp_body_flag)
                {
                        return 1;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_body(http_parser* a_parser, const char *a_at, size_t a_length)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        if(l_hmsg->m_save)
        {
                if(!l_hmsg->m_p_body.m_off)
                {
                        l_hmsg->m_p_body.m_off = CALC_OFFSET(l_hmsg, a_at);
                        l_hmsg->m_p_body.m_len = a_length;
                }
                else
                {
                        l_hmsg->m_p_body.m_len += a_length;
                }
        }
        return 0;
}

//: ----------------------------------------------------------------------------
//: \details: TODO
//: \return:  TODO
//: \param:   TODO
//: ----------------------------------------------------------------------------
int hp_on_message_complete(http_parser* a_parser)
{
        hmsg *l_hmsg = static_cast <hmsg *>(a_parser->data);
        CHECK_FOR_NULL_OK(l_hmsg);
        l_hmsg->m_complete = true;
        l_hmsg->m_supports_keep_alives = http_should_keep_alive(a_parser);
        return 0;
}

} //namespace ns_hlx {
<|endoftext|>


<commit_before>// Copyright (c) 2011-2012 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <list>
#include <map>
#include <stack>
#include <string>

#include <amx/amx.h>
#include <amx/amxaux.h>

#include "amxdebuginfo.h"
#include "amxerror.h"
#include "amxpathfinder.h"
#include "amxscript.h"
#include "amxstacktrace.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "npcall.h"
#include "os.h"
#include "stacktrace.h"

#define AMX_EXEC_GDK (-10)

bool CrashDetect::error_detected_ = false;
std::stack<NPCall*> CrashDetect::np_calls_;;

// static
void CrashDetect::OnException(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::OnInterrupt(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleInterrupt();
	} else {
		logprintf("Server received interrupt signal");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::DieOrContinue() {
	if (server_cfg_.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

CrashDetect::CrashDetect(AMX *amx)
	: AMXService<CrashDetect>(amx)
	, amx_(amx)
	, prev_callback_(0)
	, server_cfg_("server.cfg")
{
}

int CrashDetect::Load() {
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amx_path_ = pathFinder.FindAMX(this->amx());
	amx_name_ = fileutils::GetFileName(amx_path_);

	if (!amx_path_.empty() && AMXDebugInfo::IsPresent(this->amx())) {
		debug_info_.Load(amx_path_);
	}

	amx_.DisableSysreqD();
	prev_callback_ = amx_.GetCallback();

	return AMX_ERR_NONE;
}

int CrashDetect::Unload() {
	return AMX_ERR_NONE;
}

int CrashDetect::DoAmxCallback(cell index, cell *result, cell *params) {
	NPCall call = NPCall::Native(amx(), index);
	np_calls_.push(&call);
	int error = prev_callback_(amx(), index, result, params);
	np_calls_.pop();
	return error;
}

int CrashDetect::DoAmxExec(cell *retval, int index) {
	NPCall call = NPCall::Public(amx(), index);
	np_calls_.push(&call);

	int error = ::amx_Exec(amx(), retval, index);
	if (error != AMX_ERR_NONE && !error_detected_) {
		HandleExecError(index, error);
	} else {
		error_detected_ = false;
	}

	np_calls_.pop();
	return error;
}

void CrashDetect::HandleExecError(int index, const AMXError &error) {
	CrashDetect::error_detected_ = true;

	if (error.code() == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, error.string());

	switch (error.code()) {
		case AMX_ERR_BOUNDS: {
			cell bound = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip() + sizeof(cell)));
			cell index = amx_.GetPri();
			if (index < 0) {
				logprintf(" Accessing element at negative index %d", index);
			} else {
				logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = amx_.GetNatives();
			int numNatives = amx_.GetNumNatives();
			for (int i = 0; i < numNatives; ++i) {
				if (natives[i].address == 0) {
					logprintf(" %s", amx_.GetName(natives[i].nameofs));
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_.GetStk(), amx_.GetHea());
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_.GetStk(), amx_.GetStp());
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_.GetHea(), amx_.GetHlw());
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip()));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_.GetCip());
			break;
		}
	}

	if (error.code() != AMX_ERR_NOTFOUND &&
		error.code() != AMX_ERR_INDEX &&
		error.code() != AMX_ERR_CALLBACK &&
		error.code() != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = server_cfg_.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void CrashDetect::HandleException() {
	logprintf("Server crashed while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

void CrashDetect::HandleInterrupt() {
	logprintf("Server received interrupt signal while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

// static
void CrashDetect::PrintAmxBacktrace() {
	if (np_calls_.empty()) {
		return;
	}

	AMXScript topAmx = np_calls_.top()->amx();

	cell frm = topAmx.GetFrm();
	cell cip = topAmx.GetCip();
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	std::stack<NPCall*> np_calls = np_calls_;

	while (!np_calls.empty() && cip != 0) {
		const NPCall *call = np_calls.top();
		AMXScript amx = call->amx();

		// We don't trace calls across AMX bounds i.e. outside of top-level
		// function's AMX instance.
		if (amx != topAmx) {
			assert(level != 0);
			break;
		}

		if (call->IsNative()) {
			AMX_NATIVE address = reinterpret_cast<AMX_NATIVE>(amx.GetNativeAddr(call->index()));
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePathFromAddr((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amx.GetNativeName(call->index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call->IsPublic()) {
			AMXDebugInfo &debug_info = CrashDetect::Get(amx)->debug_info_;

			amx.PushStack(cip); // push return address
			amx.PushStack(frm); // push frame pointer

			std::list<AMXStackFrame> frames;
			{
				AMXStackTrace trace(amx, amx.GetStk(), &debug_info);
				do {
					AMXStackFrame frame = trace.GetFrame();
					if (frame) {
						frames.push_back(frame);
					} else {
						break;
					}
				} while (trace.Next());
			}

			frm = amx.PopStack(); // pop frame pointer
			cip = amx.PopStack(); // pop return address

			if (frames.empty()) {
				ucell ep_addr = amx.GetPublicAddr(call->index());
				frames.push_front(AMXStackFrame(amx, frm, 0, ep_addr, &debug_info));
			} else {
				if (!debug_info.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(amx,
						bottom.GetFrameAddr(),
						bottom.GetRetAddr(),
						amx.GetPublicAddr(call->index()),
						&debug_info);
				}
			}

			std::string &amx_name = CrashDetect::Get(amx)->amx_name_;
			for (std::list<AMXStackFrame>::const_iterator iterator = frames.begin();
					iterator != frames.end(); ++iterator)
			{
				std::string from = " from " + amx_name;
				if (amx_name.empty() || debug_info.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, iterator->AsString().c_str(), from.c_str());
			}

			frm = call->frm();
			cip = call->cip();
		}

		np_calls.pop();
	}
}

// static
void CrashDetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePathFromAddr(frame.GetRetAddr());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.AsString().c_str(), from.c_str());
	}
}

// static
void CrashDetect::logprintf(const char *format, ...) {
	std::va_list va;
	va_start(va, format);

	std::string new_format_;
	new_format_.append("[debug] ");
	new_format_.append(format);

	vlogprintf(new_format_.c_str(), va);
	va_end(va);
}
<commit_msg>Fix variable name<commit_after>// Copyright (c) 2011-2012 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <list>
#include <map>
#include <stack>
#include <string>

#include <amx/amx.h>
#include <amx/amxaux.h>

#include "amxdebuginfo.h"
#include "amxerror.h"
#include "amxpathfinder.h"
#include "amxscript.h"
#include "amxstacktrace.h"
#include "compiler.h"
#include "configreader.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "logprintf.h"
#include "npcall.h"
#include "os.h"
#include "stacktrace.h"

#define AMX_EXEC_GDK (-10)

bool CrashDetect::error_detected_ = false;
std::stack<NPCall*> CrashDetect::np_calls_;;

// static
void CrashDetect::OnException(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleException();
	} else {
		logprintf("Server crashed due to an unknown error");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::OnInterrupt(void *context) {
	if (!np_calls_.empty()) {
		CrashDetect::Get(np_calls_.top()->amx())->HandleInterrupt();
	} else {
		logprintf("Server received interrupt signal");
	}
	PrintSystemBacktrace(context);
}

// static
void CrashDetect::DieOrContinue() {
	if (server_cfg_.GetOption("die_on_error", false)) {
		logprintf("Aborting...");
		std::exit(EXIT_FAILURE);
	}
}

CrashDetect::CrashDetect(AMX *amx)
	: AMXService<CrashDetect>(amx)
	, amx_(amx)
	, prev_callback_(0)
	, server_cfg_("server.cfg")
{
}

int CrashDetect::Load() {
	AMXPathFinder pathFinder;
	pathFinder.AddSearchPath("gamemodes");
	pathFinder.AddSearchPath("filterscripts");

	// Read a list of additional search paths from AMX_PATH.
	const char *AMX_PATH = getenv("AMX_PATH");
	if (AMX_PATH != 0) {
		std::string var(AMX_PATH);
		std::string path;
		std::string::size_type begin = 0;
		while (begin < var.length()) {
			std::string::size_type end = var.find(fileutils::kNativePathListSepChar, begin);
			if (end == std::string::npos) {
				end = var.length();
			}
			path.assign(var.begin() + begin, var.begin() + end);
			if (!path.empty()) {
				pathFinder.AddSearchPath(path);
			}
			begin = end + 1;
		}
	}

	amx_path_ = pathFinder.FindAMX(this->amx());
	amx_name_ = fileutils::GetFileName(amx_path_);

	if (!amx_path_.empty() && AMXDebugInfo::IsPresent(this->amx())) {
		debug_info_.Load(amx_path_);
	}

	amx_.DisableSysreqD();
	prev_callback_ = amx_.GetCallback();

	return AMX_ERR_NONE;
}

int CrashDetect::Unload() {
	return AMX_ERR_NONE;
}

int CrashDetect::DoAmxCallback(cell index, cell *result, cell *params) {
	NPCall call = NPCall::Native(amx(), index);
	np_calls_.push(&call);
	int error = prev_callback_(amx(), index, result, params);
	np_calls_.pop();
	return error;
}

int CrashDetect::DoAmxExec(cell *retval, int index) {
	NPCall call = NPCall::Public(amx(), index);
	np_calls_.push(&call);

	int error = ::amx_Exec(amx(), retval, index);
	if (error != AMX_ERR_NONE && !error_detected_) {
		HandleExecError(index, error);
	} else {
		error_detected_ = false;
	}

	np_calls_.pop();
	return error;
}

void CrashDetect::HandleExecError(int index, const AMXError &error) {
	CrashDetect::error_detected_ = true;

	if (error.code() == AMX_ERR_INDEX && index == AMX_EXEC_GDK) {
		return;
	}

	logprintf("Run time error %d: \"%s\"", error, error.string());

	switch (error.code()) {
		case AMX_ERR_BOUNDS: {
			cell bound = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip() + sizeof(cell)));
			cell index = amx_.GetPri();
			if (index < 0) {
				logprintf(" Accessing element at negative index %d", index);
			} else {
				logprintf(" Accessing element at index %d past array upper bound %d", index, bound);
			}
			break;
		}
		case AMX_ERR_NOTFOUND: {
			AMX_FUNCSTUBNT *natives = amx_.GetNatives();
			int num_natives = amx_.GetNumNatives();
			for (int i = 0; i < num_natives; ++i) {
				if (natives[i].address == 0) {
					logprintf(" %s", amx_.GetName(natives[i].nameofs));
				}
			}
			break;
		}
		case AMX_ERR_STACKERR:
			logprintf(" Stack pointer (STK) is 0x%X, heap pointer (HEA) is 0x%X", amx_.GetStk(), amx_.GetHea());
			break;
		case AMX_ERR_STACKLOW:
			logprintf(" Stack pointer (STK) is 0x%X, stack top (STP) is 0x%X", amx_.GetStk(), amx_.GetStp());
			break;
		case AMX_ERR_HEAPLOW:
			logprintf(" Heap pointer (HEA) is 0x%X, heap bottom (HLW) is 0x%X", amx_.GetHea(), amx_.GetHlw());
			break;
		case AMX_ERR_INVINSTR: {
			cell opcode = *(reinterpret_cast<cell*>(amx_.GetCode() + amx_.GetCip()));
			logprintf(" Unknown opcode 0x%x at address 0x%08X", opcode , amx_.GetCip());
			break;
		}
	}

	if (error.code() != AMX_ERR_NOTFOUND &&
		error.code() != AMX_ERR_INDEX &&
		error.code() != AMX_ERR_CALLBACK &&
		error.code() != AMX_ERR_INIT)
	{
		PrintAmxBacktrace();
	}

	std::string command = server_cfg_.GetOption("run_on_error", std::string());
	if (!command.empty()) {
		std::system(command.c_str());
	}

	DieOrContinue();
}

void CrashDetect::HandleException() {
	logprintf("Server crashed while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

void CrashDetect::HandleInterrupt() {
	logprintf("Server received interrupt signal while executing %s", amx_name_.c_str());
	PrintAmxBacktrace();
}

// static
void CrashDetect::PrintAmxBacktrace() {
	if (np_calls_.empty()) {
		return;
	}

	AMXScript topAmx = np_calls_.top()->amx();

	cell frm = topAmx.GetFrm();
	cell cip = topAmx.GetCip();
	int level = 0;

	if (cip == 0) {
		return;
	}

	logprintf("AMX backtrace:");

	std::stack<NPCall*> np_calls = np_calls_;

	while (!np_calls.empty() && cip != 0) {
		const NPCall *call = np_calls.top();
		AMXScript amx = call->amx();

		// We don't trace calls across AMX bounds i.e. outside of top-level
		// function's AMX instance.
		if (amx != topAmx) {
			assert(level != 0);
			break;
		}

		if (call->IsNative()) {
			AMX_NATIVE address = reinterpret_cast<AMX_NATIVE>(amx.GetNativeAddr(call->index()));
			if (address != 0) {
				std::string module = fileutils::GetFileName(os::GetModulePathFromAddr((void*)address));
				std::string from = " from " + module;
				if (module.empty()) {
					from.clear();
				}
				const char *name = amx.GetNativeName(call->index());
				if (name != 0) {
					logprintf("#%d native %s () [%08x]%s", level++, name, address, from.c_str());
				}
			}
		} else if (call->IsPublic()) {
			AMXDebugInfo &debug_info = CrashDetect::Get(amx)->debug_info_;

			amx.PushStack(cip); // push return address
			amx.PushStack(frm); // push frame pointer

			std::list<AMXStackFrame> frames;
			{
				AMXStackTrace trace(amx, amx.GetStk(), &debug_info);
				do {
					AMXStackFrame frame = trace.GetFrame();
					if (frame) {
						frames.push_back(frame);
					} else {
						break;
					}
				} while (trace.Next());
			}

			frm = amx.PopStack(); // pop frame pointer
			cip = amx.PopStack(); // pop return address

			if (frames.empty()) {
				ucell ep_addr = amx.GetPublicAddr(call->index());
				frames.push_front(AMXStackFrame(amx, frm, 0, ep_addr, &debug_info));
			} else {
				if (!debug_info.IsLoaded()) {
					AMXStackFrame &bottom = frames.back();
					bottom = AMXStackFrame(amx,
						bottom.GetFrameAddr(),
						bottom.GetRetAddr(),
						amx.GetPublicAddr(call->index()),
						&debug_info);
				}
			}

			std::string &amx_name = CrashDetect::Get(amx)->amx_name_;
			for (std::list<AMXStackFrame>::const_iterator iterator = frames.begin();
					iterator != frames.end(); ++iterator)
			{
				std::string from = " from " + amx_name;
				if (amx_name.empty() || debug_info.IsLoaded()) {
					from.clear();
				}
				logprintf("#%d %s%s", level++, iterator->AsString().c_str(), from.c_str());
			}

			frm = call->frm();
			cip = call->cip();
		}

		np_calls.pop();
	}
}

// static
void CrashDetect::PrintSystemBacktrace(void *context) {
	logprintf("System backtrace:");

	int level = 0;

	StackTrace trace(context);
	std::deque<StackFrame> frames = trace.GetFrames();

	for (std::deque<StackFrame>::const_iterator iterator = frames.begin();
			iterator != frames.end(); ++iterator) {
		const StackFrame &frame = *iterator;

		std::string module = os::GetModulePathFromAddr(frame.GetRetAddr());
		std::string from = " from " + module;
		if (module.empty()) {
			from.clear();
		}

		logprintf("#%d %s%s", level++, frame.AsString().c_str(), from.c_str());
	}
}

// static
void CrashDetect::logprintf(const char *format, ...) {
	std::va_list va;
	va_start(va, format);

	std::string new_format;
	new_format.append("[debug] ");
	new_format.append(format);

	vlogprintf(new_format.c_str(), va);
	va_end(va);
}
<|endoftext|>


<commit_before>#include "data/Sample.hpp"

Sample::Sample(string filename, unsigned column) {
    ifstream is(filename.c_str(), ios::in);
    Sanity::streamness(is, "Couldn't open " + filename);

    List<string> lines = StreamUtils::readLines(is);
    ConstIterator<string> it(lines);

    cerr << "Getting context information" << endl;
    string name = (it++).get();
    string subject = StringUtils::split((it++).get(), ':')[column-1];
    string t = StringUtils::split((it++).get(), ':')[column-1];
    DataSourceType type = t == "C" ? CONTINOUS : DISCRETE;

    float firstInterval = 0, intervalSizes = 0;
    if(type == CONTINOUS) {
        cerr << "Reading data intervals from user" << endl;
        string tmp;
        cout << " Start of the first interval: ";
        getline(cin, tmp, cin.widen('\n'));
        firstInterval = StringUtils::stringToFloat(tmp); // TODO Handle invalid input

        cout << " Size of the intervals: ";
        getline(cin, tmp, cin.widen('\n'));
        intervalSizes = StringUtils::stringToFloat(tmp); // TODO Handle invalid input
    }

    // Read all the data from the file in a sorted list
    cerr << "Reading file data" << endl;
    SortedList<float> dataRead;
    while(!it.end()) {
        List<string> columns = StringUtils::split(it.get(), ':');
        float value = StringUtils::stringToFloat(columns[column -1]);
        dataRead.add(value);
        ++it;
    }

    // Count the data and move them to a List<Data*>
    cerr << "Moving data read to a List<Data*>" << endl;
    List<Data*> data;
    unsigned totalCount = 0;
    if(type == DISCRETE) {
        ConstIterator<float> it(dataRead);
        float prev = (it++).get();
        unsigned count = 1;
        while(!it.end()) {
            if(it.get() == prev) {
                ++count;
            } else {
                data.add(new Data1D(count, prev));
                totalCount += count;
                prev = it.get();
                count = 1;
            }
            ++it;
        }
    } else {
        ConstIterator<float> it(dataRead);
        Sanity::truthness(it.get() >= firstInterval, "WHY YOU GIVE STUPID VALUES?");
        float prev = firstInterval;
        unsigned count = 0;
        while(!it.end()) {
            while(!it.end() && it.get() < prev + intervalSizes) {
                ++count;
                ++it;
            }
            data.add(new Data1D(count, prev));
            totalCount += count;
            count = 0;
            prev += intervalSizes;
        }
    }

    cerr << "Building data source" << endl;
    if(type == DISCRETE) {
        this->dataSource = new DiscreteDataSource(name, subject, totalCount, data);
    } else {
        this->dataSource = new ContinousDataSource(
            name, subject, totalCount,
            data, firstInterval, intervalSizes
        );
    }
}

Sample::Sample(string filename, unsigned column1, unsigned column2) {
    ifstream is(filename.c_str(), ios::in);
    Sanity::streamness(is, "Couldn't open " + filename);

    // These are the real indexes
    --column1;
    --column2;

    List<string> lines = StreamUtils::readLines(is);
    ConstIterator<string> it(lines);

    cerr << "Getting context information" << endl;
    string name = (it++).get();
    List<string> subjects = StringUtils::split((it++).get(), ':');
    List<string> types = StringUtils::split((it++).get(), ':');

    cerr << "Reading file data" << endl;
    List<Data*> data;
    while(!it.end()) {
        List<string> values = StringUtils::split(it.get(), ':');
        data.add(new Data2D(
            StringUtils::stringToFloat(values[column1]),
            StringUtils::stringToFloat(values[column2])
        ));
        ++it;
    }

    cerr << "Creating data source" << endl;
    this->dataSource = new DataSource2D(
        name,
        subjects[column1],
        subjects[column2],
        types[column1] == "C" ? CONTINOUS : DISCRETE,
        types[column2] == "C" ? CONTINOUS : DISCRETE,
        data
    );
}

Sample::Sample(const Sample& p) {
    // TODO Check if that's right
    ContinousDataSource* d = dynamic_cast<ContinousDataSource*>(p.dataSource);
    if(d) {
        this->dataSource = new ContinousDataSource(*d);
    } else {
        this->dataSource = new DataSource2D(*static_cast<DataSource2D*>(p.dataSource));
    }
}

Sample::~Sample() {
    delete this->dataSource;
}

void Sample::display() const {
    // TODO
    //this->dataSource->display();
}

Sample& Sample::operator=(Sample sample) {
    swap(this->dataSource, sample.dataSource);
    return *this;
}
<commit_msg>Store continous data series using the middle of the interval<commit_after>#include "data/Sample.hpp"

Sample::Sample(string filename, unsigned column) {
    ifstream is(filename.c_str(), ios::in);
    Sanity::streamness(is, "Couldn't open " + filename);

    List<string> lines = StreamUtils::readLines(is);
    ConstIterator<string> it(lines);

    cerr << "Getting context information" << endl;
    string name = (it++).get();
    string subject = StringUtils::split((it++).get(), ':')[column-1];
    string t = StringUtils::split((it++).get(), ':')[column-1];
    DataSourceType type = t == "C" ? CONTINOUS : DISCRETE;

    float firstInterval = 0, intervalSizes = 0;
    if(type == CONTINOUS) {
        cerr << "Reading data intervals from user" << endl;
        string tmp;
        cout << " Start of the first interval: ";
        getline(cin, tmp, cin.widen('\n'));
        firstInterval = StringUtils::stringToFloat(tmp); // TODO Handle invalid input

        cout << " Size of the intervals: ";
        getline(cin, tmp, cin.widen('\n'));
        intervalSizes = StringUtils::stringToFloat(tmp); // TODO Handle invalid input
    }

    // Read all the data from the file in a sorted list
    cerr << "Reading file data" << endl;
    SortedList<float> dataRead;
    while(!it.end()) {
        List<string> columns = StringUtils::split(it.get(), ':');
        float value = StringUtils::stringToFloat(columns[column -1]);
        dataRead.add(value);
        ++it;
    }

    // Count the data and move them to a List<Data*>
    cerr << "Moving data read to a List<Data*>" << endl;
    List<Data*> data;
    unsigned totalCount = 0;
    if(type == DISCRETE) {
        ConstIterator<float> it(dataRead);
        float prev = (it++).get();
        unsigned count = 1;
        while(!it.end()) {
            if(it.get() == prev) {
                ++count;
            } else {
                data.add(new Data1D(count, prev));
                totalCount += count;
                prev = it.get();
                count = 1;
            }
            ++it;
        }
    } else {
        ConstIterator<float> it(dataRead);
        Sanity::truthness(it.get() >= firstInterval, "WHY YOU GIVE STUPID VALUES?");
        float prev = firstInterval + intervalSizes / 2;
        unsigned count = 0;
        while(!it.end()) {
            while(!it.end() && it.get() < prev + intervalSizes / 2) {
                ++count;
                ++it;
            }
            data.add(new Data1D(count, prev));
            totalCount += count;
            count = 0;
            prev += intervalSizes;
        }
    }

    cerr << "Building data source" << endl;
    if(type == DISCRETE) {
        this->dataSource = new DiscreteDataSource(name, subject, totalCount, data);
    } else {
        this->dataSource = new ContinousDataSource(
            name, subject, totalCount,
            data, firstInterval, intervalSizes
        );
    }
}

Sample::Sample(string filename, unsigned column1, unsigned column2) {
    ifstream is(filename.c_str(), ios::in);
    Sanity::streamness(is, "Couldn't open " + filename);

    // These are the real indexes
    --column1;
    --column2;

    List<string> lines = StreamUtils::readLines(is);
    ConstIterator<string> it(lines);

    cerr << "Getting context information" << endl;
    string name = (it++).get();
    List<string> subjects = StringUtils::split((it++).get(), ':');
    List<string> types = StringUtils::split((it++).get(), ':');

    cerr << "Reading file data" << endl;
    List<Data*> data;
    while(!it.end()) {
        List<string> values = StringUtils::split(it.get(), ':');
        data.add(new Data2D(
            StringUtils::stringToFloat(values[column1]),
            StringUtils::stringToFloat(values[column2])
        ));
        ++it;
    }

    cerr << "Creating data source" << endl;
    this->dataSource = new DataSource2D(
        name,
        subjects[column1],
        subjects[column2],
        types[column1] == "C" ? CONTINOUS : DISCRETE,
        types[column2] == "C" ? CONTINOUS : DISCRETE,
        data
    );
}

Sample::Sample(const Sample& p) {
    // TODO Check if that's right
    ContinousDataSource* d = dynamic_cast<ContinousDataSource*>(p.dataSource);
    if(d) {
        this->dataSource = new ContinousDataSource(*d);
    } else {
        this->dataSource = new DataSource2D(*static_cast<DataSource2D*>(p.dataSource));
    }
}

Sample::~Sample() {
    delete this->dataSource;
}

void Sample::display() const {
    // TODO
    //this->dataSource->display();
}

Sample& Sample::operator=(Sample sample) {
    swap(this->dataSource, sample.dataSource);
    return *this;
}
<|endoftext|>


<commit_before>#include <Vriska/Threads/Timer.h>

#include <iostream>
namespace Vriska
{
    VRISKA_ACCESSIBLE
    Timer::Timer(ITimeoutable& callback, Time const & timeout) : _callback(callback), _timeout(timeout)
    {
    }

    VRISKA_ACCESSIBLE
    Timer::~Timer()
    {
    }

    VRISKA_ACCESSIBLE
    void Timer::setTimeout(Time const & timeout)
    {
        ScopedLock lock(_mutex);

        _timeout = timeout;
    }

    VRISKA_ACCESSIBLE
    void Timer::launch()
    {
        struct timeval ntime;
        int ret;
        bool loop;
        Time elapsed;

        do
        {
            {
                ScopedLock lock(_mutex);

                _timeout.toTimeval(ntime);
                elapsed = _timeout;
            }

            ret = ::select(0, NULL, NULL, NULL, &ntime);

            if (ret == -1)
                return;
            loop = _callback.onTimeout(elapsed);
        } while (loop);
    }
}
<commit_msg>Remove debug header<commit_after>#include <Vriska/Threads/Timer.h>

namespace Vriska
{
    VRISKA_ACCESSIBLE
    Timer::Timer(ITimeoutable& callback, Time const & timeout) : _callback(callback), _timeout(timeout)
    {
    }

    VRISKA_ACCESSIBLE
    Timer::~Timer()
    {
    }

    VRISKA_ACCESSIBLE
    void Timer::setTimeout(Time const & timeout)
    {
        ScopedLock lock(_mutex);

        _timeout = timeout;
    }

    VRISKA_ACCESSIBLE
    void Timer::launch()
    {
        struct timeval ntime;
        int ret;
        bool loop;
        Time elapsed;

        do
        {
            {
                ScopedLock lock(_mutex);

                _timeout.toTimeval(ntime);
                elapsed = _timeout;
            }

            ret = ::select(0, NULL, NULL, NULL, &ntime);

            if (ret == -1)
                return;
            loop = _callback.onTimeout(elapsed);
        } while (loop);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Smirnov Vladimir mapron1@gmail.com
 * Source code 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 or in file COPYING-APACHE-2.0.txt
 *
 * 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.h
 */

#include "AppUtils.h"

#include <CoordinatorClient.h>
#include <StringUtils.h>

#include <iostream>

int main(int argc, char** argv)
{
	using namespace Wuild;
	ConfiguredApplication app(argc, argv, "WuildProxyClient", "proxy");

	CoordinatorClient::Config config = app.m_remoteToolClientConfig.m_coordinator;
	if (!config.Validate())
		return 1;

	CoordinatorClient client;
	if (!client.SetConfig(config))
		return 1;

	client.SetInfoArrivedCallback([](const CoordinatorInfo& info){
		std::cout << "Coordinator connected tool servers: \n";
		for (const ToolServerInfo & toolServer : info.m_toolServers)
		{
			std::cout <<  toolServer.ToString(true, true) << "\n";
		}

		std::cout << "\n Coordinator current sessions: \n";
		for (const ToolServerSessionInfo & session : info.m_activeSessions)
		{
			std::cout <<  session.ToString(true, true) << "\n";
		}

		std::cout << "\n Coordinator latest finished sessions: \n";
		for (const ToolServerSessionInfo & session : info.m_latestSessions)
		{
			std::cout <<  session.ToString(false, true) << "\n";
		}

		Application::Interrupt(0);
	});

	client.Start();

	return ExecAppLoop();
}
<commit_msg>WuildCoordinatorStatus pretty output.<commit_after>/*
 * Copyright (C) 2017 Smirnov Vladimir mapron1@gmail.com
 * Source code 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 or in file COPYING-APACHE-2.0.txt
 *
 * 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.h
 */

#include "AppUtils.h"

#include <CoordinatorClient.h>
#include <StringUtils.h>
#include <TimePoint.h>

#include <iostream>
#include <set>

int main(int argc, char** argv)
{
	using namespace Wuild;
	ConfiguredApplication app(argc, argv, "WuildProxyClient", "proxy");

	CoordinatorClient::Config config = app.m_remoteToolClientConfig.m_coordinator;
	if (!config.Validate())
		return 1;

	CoordinatorClient client;
	if (!client.SetConfig(config))
		return 1;

	client.SetInfoArrivedCallback([](const CoordinatorInfo& info){
		std::cout << "Coordinator connected tool servers: \n";

		std::map<int64_t, int> sessionsUsed;
		std::set<std::string> toolIds;
		int running = 0, thread = 0, queued = 0;

		for (const ToolServerInfo & toolServer : info.m_toolServers)
		{
			std::cout <<  toolServer.m_connectionHost << ":" << toolServer.m_connectionPort <<
						  " load:" << toolServer.m_runningTasks << "/" << toolServer.m_totalThreads << "\n";
			running += toolServer.m_runningTasks;
			queued += toolServer.m_queuedTasks;
			thread += toolServer.m_totalThreads;
			for (const ToolServerInfo::ConnectedClientInfo & client : toolServer.m_connectedClients)
			{
				sessionsUsed[client.m_sessionId] += client.m_usedThreads ;
			}
			for (const std::string & t : toolServer.m_toolIds)
				toolIds.insert(t);
		}

		std::cout <<  "\nTotal load:" << running << "/" << thread << ", queue: " << queued << "\n";

		if (!sessionsUsed.empty())
		{
			std::cout << "\nRunning sessions:\n";
			for (const auto & s : sessionsUsed)
			{
				TimePoint stamp; stamp.SetUS(s.first); stamp.ToLocal();
				std::cout << "Started " << stamp.ToString() << ", used: " << s.second << "\n";
			}
		}

		std::cout << "\nAvailable tools: ";
		for (const auto & t : toolIds)
			std::cout << t << ", ";
		std::cout << "\n";
		Application::Interrupt(0);
	});

	client.Start();

	return ExecAppLoop();
}
<|endoftext|>


<commit_before>#ifndef __STRICT_FSTREAM_HPP
#define __STRICT_FSTREAM_HPP

#include <cassert>
#include <fstream>
#include <cstring>
#include <string>

/**
 * This namespace defines wrappers for std::ifstream, std::ofstream, and
 * std::fstream objects. The wrappers perform the following steps:
 * - check the open modes make sense
 * - check that the call to open() is successful
 * - (for input streams) check that the opened file is peek-able
 * - turn on the badbit in the exception mask
 */
namespace strict_fstream
{
/// Overload of error-reporting function, to enable use with VS (1)
/// and POSIX signature found in MUSL on Alpine (2)
/// Ref 1: http://stackoverflow.com/a/901316/717706
/// Ref 2: http://stackoverflow.com/a/41956165
static char* strerror_r_compat(int result, char* buffer, int err)
{
    if (result)
    {
        sprintf(buffer, "Unknown error: %d", err);
    }
    return buffer;
}

static char* strerror_r_compat(char* result, char*, int)
{
    return result;
}

static std::string strerror()
{
    std::string buff(80, '\0');
#ifdef _WIN32
    if (strerror_s(&buff[0], buff.size(), errno) != 0)
    {
        buff = "Unknown error";
    }
#else
    auto p = strerror_r_compat(strerror_r(errno, &buff[0], buff.size()), &buff[0], errno);
    std::string tmp(p, std::strlen(p));
    std::swap(buff, tmp);
#endif
    buff.resize(buff.find('\0'));
    return buff;
}

/// Exception class thrown by failed operations.
class Exception
    : public std::exception
{
public:
    Exception(const std::string& msg) : _msg(msg) {}
    const char * what() const noexcept { return _msg.c_str(); }
private:
    std::string _msg;
}; // class Exception

namespace detail
{

struct static_method_holder
{
    static std::string mode_to_string(std::ios_base::openmode mode)
    {
        static const int n_modes = 6;
        static const std::ios_base::openmode mode_val_v[n_modes] =
            {
                std::ios_base::in,
                std::ios_base::out,
                std::ios_base::app,
                std::ios_base::ate,
                std::ios_base::trunc,
                std::ios_base::binary
            };

        static const char * mode_name_v[n_modes] =
            {
                "in",
                "out",
                "app",
                "ate",
                "trunc",
                "binary"
            };
        std::string res;
        for (int i = 0; i < n_modes; ++i)
        {
            if (mode & mode_val_v[i])
            {
                res += (! res.empty()? "|" : "");
                res += mode_name_v[i];
            }
        }
        if (res.empty()) res = "none";
        return res;
    }
    static void check_mode(const std::string& filename, std::ios_base::openmode mode)
    {
        if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out");
        }
        else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out");
        }
        else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app");
        }
     }
    static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
    {
        if (s_p->fail())
        {
            throw Exception(std::string("strict_fstream: open('")
                            + filename + "'," + mode_to_string(mode) + "): open failed: "
                            + strerror());
        }
    }
    static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
    {
        bool peek_failed = true;
        try
        {
            is_p->peek();
            peek_failed = is_p->fail();
        }
        catch (std::ios_base::failure e) {}
        if (peek_failed)
        {
            throw Exception(std::string("strict_fstream: open('")
                            + filename + "'," + mode_to_string(mode) + "): peek failed: "
                            + strerror());
        }
        is_p->clear();
    }
}; // struct static_method_holder

} // namespace detail

class ifstream
    : public std::ifstream
{
public:
    ifstream() = default;
    ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        mode |= std::ios_base::in;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::ifstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
        detail::static_method_holder::check_peek(this, filename, mode);
    }
}; // class ifstream

class ofstream
    : public std::ofstream
{
public:
    ofstream() = default;
    ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
    {
        mode |= std::ios_base::out;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::ofstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
    }
}; // class ofstream

class fstream
    : public std::fstream
{
public:
    fstream() = default;
    fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::fstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
        detail::static_method_holder::check_peek(this, filename, mode);
    }
}; // class fstream

} // namespace strict_fstream

#endif
<commit_msg>Prefer inline for strerror_r_compat<commit_after>#ifndef __STRICT_FSTREAM_HPP
#define __STRICT_FSTREAM_HPP

#include <cassert>
#include <fstream>
#include <cstring>
#include <string>

/**
 * This namespace defines wrappers for std::ifstream, std::ofstream, and
 * std::fstream objects. The wrappers perform the following steps:
 * - check the open modes make sense
 * - check that the call to open() is successful
 * - (for input streams) check that the opened file is peek-able
 * - turn on the badbit in the exception mask
 */
namespace strict_fstream
{
/// Overload of error-reporting function, to enable use with VS (1)
/// and POSIX signature found in MUSL on Alpine (2)
/// Ref 1: http://stackoverflow.com/a/901316/717706
/// Ref 2: http://stackoverflow.com/a/41956165
static inline char* strerror_r_compat(int result, char* buffer, int err)
{
    if (result)
    {
        sprintf(buffer, "Unknown error: %d", err);
    }
    return buffer;
}

static inline char* strerror_r_compat(char* result, char*, int)
{
    return result;
}

static std::string strerror()
{
    std::string buff(80, '\0');
#ifdef _WIN32
    if (strerror_s(&buff[0], buff.size(), errno) != 0)
    {
        buff = "Unknown error";
    }
#else
    auto p = strerror_r_compat(strerror_r(errno, &buff[0], buff.size()), &buff[0], errno);
    std::string tmp(p, std::strlen(p));
    std::swap(buff, tmp);
#endif
    buff.resize(buff.find('\0'));
    return buff;
}

/// Exception class thrown by failed operations.
class Exception
    : public std::exception
{
public:
    Exception(const std::string& msg) : _msg(msg) {}
    const char * what() const noexcept { return _msg.c_str(); }
private:
    std::string _msg;
}; // class Exception

namespace detail
{

struct static_method_holder
{
    static std::string mode_to_string(std::ios_base::openmode mode)
    {
        static const int n_modes = 6;
        static const std::ios_base::openmode mode_val_v[n_modes] =
            {
                std::ios_base::in,
                std::ios_base::out,
                std::ios_base::app,
                std::ios_base::ate,
                std::ios_base::trunc,
                std::ios_base::binary
            };

        static const char * mode_name_v[n_modes] =
            {
                "in",
                "out",
                "app",
                "ate",
                "trunc",
                "binary"
            };
        std::string res;
        for (int i = 0; i < n_modes; ++i)
        {
            if (mode & mode_val_v[i])
            {
                res += (! res.empty()? "|" : "");
                res += mode_name_v[i];
            }
        }
        if (res.empty()) res = "none";
        return res;
    }
    static void check_mode(const std::string& filename, std::ios_base::openmode mode)
    {
        if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out");
        }
        else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out");
        }
        else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
        {
            throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app");
        }
     }
    static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
    {
        if (s_p->fail())
        {
            throw Exception(std::string("strict_fstream: open('")
                            + filename + "'," + mode_to_string(mode) + "): open failed: "
                            + strerror());
        }
    }
    static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
    {
        bool peek_failed = true;
        try
        {
            is_p->peek();
            peek_failed = is_p->fail();
        }
        catch (std::ios_base::failure e) {}
        if (peek_failed)
        {
            throw Exception(std::string("strict_fstream: open('")
                            + filename + "'," + mode_to_string(mode) + "): peek failed: "
                            + strerror());
        }
        is_p->clear();
    }
}; // struct static_method_holder

} // namespace detail

class ifstream
    : public std::ifstream
{
public:
    ifstream() = default;
    ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        mode |= std::ios_base::in;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::ifstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
        detail::static_method_holder::check_peek(this, filename, mode);
    }
}; // class ifstream

class ofstream
    : public std::ofstream
{
public:
    ofstream() = default;
    ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
    {
        mode |= std::ios_base::out;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::ofstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
    }
}; // class ofstream

class fstream
    : public std::fstream
{
public:
    fstream() = default;
    fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        open(filename, mode);
    }
    void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
    {
        if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
        exceptions(std::ios_base::badbit);
        detail::static_method_holder::check_mode(filename, mode);
        std::fstream::open(filename, mode);
        detail::static_method_holder::check_open(this, filename, mode);
        detail::static_method_holder::check_peek(this, filename, mode);
    }
}; // class fstream

} // namespace strict_fstream

#endif
<|endoftext|>


<commit_before>// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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 "subprocess.h"

#include "test.h"

#ifndef _WIN32
// SetWithLots need setrlimit.
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#endif

namespace {

#ifdef _WIN32
const char* kSimpleCommand = "cmd /c dir \\";
#else
const char* kSimpleCommand = "ls /";
#endif

struct SubprocessTest : public testing::Test {
  SubprocessSet subprocs_;
};

}  // anonymous namespace

// Run a command that fails and emits to stderr.
TEST_F(SubprocessTest, BadCommandStderr) {
  Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    // Pretend we discovered that stderr was ready for writing.
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitFailure, subproc->Finish());
  EXPECT_NE("", subproc->GetOutput());
}

// Run a command that does not exist
TEST_F(SubprocessTest, NoSuchCommand) {
  Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    // Pretend we discovered that stderr was ready for writing.
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitFailure, subproc->Finish());
  EXPECT_NE("", subproc->GetOutput());
#ifdef _WIN32
  ASSERT_EQ("CreateProcess failed: The system cannot find the file "
            "specified.\n", subproc->GetOutput());
#endif
}

#ifndef _WIN32

TEST_F(SubprocessTest, InterruptChild) {
  Subprocess* subproc = subprocs_.Add("kill -INT $$");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitInterrupted, subproc->Finish());
}

TEST_F(SubprocessTest, InterruptParent) {
  Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    bool interrupted = subprocs_.DoWork();
    if (interrupted)
      return;
  }

  ADD_FAILURE() << "We should have been interrupted";
}

#endif

TEST_F(SubprocessTest, SetWithSingle) {
  Subprocess* subproc = subprocs_.Add(kSimpleCommand);
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    subprocs_.DoWork();
  }
  ASSERT_EQ(ExitSuccess, subproc->Finish());
  ASSERT_NE("", subproc->GetOutput());

  ASSERT_EQ(1u, subprocs_.finished_.size());
}

TEST_F(SubprocessTest, SetWithMulti) {
  Subprocess* processes[3];
  const char* kCommands[3] = {
    kSimpleCommand,
#ifdef _WIN32
    "cmd /c echo hi",
    "cmd /c time /t",
#else
    "whoami",
    "pwd",
#endif
  };

  for (int i = 0; i < 3; ++i) {
    processes[i] = subprocs_.Add(kCommands[i]);
    ASSERT_NE((Subprocess *) 0, processes[i]);
  }

  ASSERT_EQ(3u, subprocs_.running_.size());
  for (int i = 0; i < 3; ++i) {
    ASSERT_FALSE(processes[i]->Done());
    ASSERT_EQ("", processes[i]->GetOutput());
  }

  while (!processes[0]->Done() || !processes[1]->Done() ||
         !processes[2]->Done()) {
    ASSERT_GT(subprocs_.running_.size(), 0u);
    subprocs_.DoWork();
  }

  ASSERT_EQ(0u, subprocs_.running_.size());
  ASSERT_EQ(3u, subprocs_.finished_.size());

  for (int i = 0; i < 3; ++i) {
    ASSERT_EQ(ExitSuccess, processes[i]->Finish());
    ASSERT_NE("", processes[i]->GetOutput());
    delete processes[i];
  }
}

// OS X's process limit is less than 1025 by default
// (|sysctl kern.maxprocperuid| is 709 on 10.7 and 10.8 and less prior to that).
#if !defined(__APPLE__)
TEST_F(SubprocessTest, SetWithLots) {
  // Arbitrary big number; needs to be over 1024 to confirm we're no longer
  // hostage to pselect.
  const size_t kNumProcs = 1025;

  // Make sure [ulimit -n] isn't going to stop us from working.
  rlimit rlim;
  ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
  ASSERT_GT(rlim.rlim_cur, kNumProcs)
      << "Raise [ulimit -n] well above " << kNumProcs
      << " to make this test go";

  vector<Subprocess*> procs;
  for (size_t i = 0; i < kNumProcs; ++i) {
    Subprocess* subproc = subprocs_.Add("/bin/echo");
    ASSERT_NE((Subprocess *) 0, subproc);
    procs.push_back(subproc);
  }
  while (!subprocs_.running_.empty())
    subprocs_.DoWork();
  for (size_t i = 0; i < procs.size(); ++i) {
    ASSERT_EQ(ExitSuccess, procs[i]->Finish());
    ASSERT_NE("", procs[i]->GetOutput());
  }
  ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
}
#endif  // linux || __OpenBSD__ || __Bitrig__

// TODO: this test could work on Windows, just not sure how to simply
// read stdin.
#ifndef _WIN32
// Verify that a command that attempts to read stdin correctly thinks
// that stdin is closed.
TEST_F(SubprocessTest, ReadStdin) {
  Subprocess* subproc = subprocs_.Add("cat -");
  while (!subproc->Done()) {
    subprocs_.DoWork();
  }
  ASSERT_EQ(ExitSuccess, subproc->Finish());
  ASSERT_EQ(1u, subprocs_.finished_.size());
}
#endif  // _WIN32
<commit_msg>Exclude Windows as well<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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 "subprocess.h"

#include "test.h"

#ifndef _WIN32
// SetWithLots need setrlimit.
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#endif

namespace {

#ifdef _WIN32
const char* kSimpleCommand = "cmd /c dir \\";
#else
const char* kSimpleCommand = "ls /";
#endif

struct SubprocessTest : public testing::Test {
  SubprocessSet subprocs_;
};

}  // anonymous namespace

// Run a command that fails and emits to stderr.
TEST_F(SubprocessTest, BadCommandStderr) {
  Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    // Pretend we discovered that stderr was ready for writing.
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitFailure, subproc->Finish());
  EXPECT_NE("", subproc->GetOutput());
}

// Run a command that does not exist
TEST_F(SubprocessTest, NoSuchCommand) {
  Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    // Pretend we discovered that stderr was ready for writing.
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitFailure, subproc->Finish());
  EXPECT_NE("", subproc->GetOutput());
#ifdef _WIN32
  ASSERT_EQ("CreateProcess failed: The system cannot find the file "
            "specified.\n", subproc->GetOutput());
#endif
}

#ifndef _WIN32

TEST_F(SubprocessTest, InterruptChild) {
  Subprocess* subproc = subprocs_.Add("kill -INT $$");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    subprocs_.DoWork();
  }

  EXPECT_EQ(ExitInterrupted, subproc->Finish());
}

TEST_F(SubprocessTest, InterruptParent) {
  Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1");
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    bool interrupted = subprocs_.DoWork();
    if (interrupted)
      return;
  }

  ADD_FAILURE() << "We should have been interrupted";
}

#endif

TEST_F(SubprocessTest, SetWithSingle) {
  Subprocess* subproc = subprocs_.Add(kSimpleCommand);
  ASSERT_NE((Subprocess *) 0, subproc);

  while (!subproc->Done()) {
    subprocs_.DoWork();
  }
  ASSERT_EQ(ExitSuccess, subproc->Finish());
  ASSERT_NE("", subproc->GetOutput());

  ASSERT_EQ(1u, subprocs_.finished_.size());
}

TEST_F(SubprocessTest, SetWithMulti) {
  Subprocess* processes[3];
  const char* kCommands[3] = {
    kSimpleCommand,
#ifdef _WIN32
    "cmd /c echo hi",
    "cmd /c time /t",
#else
    "whoami",
    "pwd",
#endif
  };

  for (int i = 0; i < 3; ++i) {
    processes[i] = subprocs_.Add(kCommands[i]);
    ASSERT_NE((Subprocess *) 0, processes[i]);
  }

  ASSERT_EQ(3u, subprocs_.running_.size());
  for (int i = 0; i < 3; ++i) {
    ASSERT_FALSE(processes[i]->Done());
    ASSERT_EQ("", processes[i]->GetOutput());
  }

  while (!processes[0]->Done() || !processes[1]->Done() ||
         !processes[2]->Done()) {
    ASSERT_GT(subprocs_.running_.size(), 0u);
    subprocs_.DoWork();
  }

  ASSERT_EQ(0u, subprocs_.running_.size());
  ASSERT_EQ(3u, subprocs_.finished_.size());

  for (int i = 0; i < 3; ++i) {
    ASSERT_EQ(ExitSuccess, processes[i]->Finish());
    ASSERT_NE("", processes[i]->GetOutput());
    delete processes[i];
  }
}

// OS X's process limit is less than 1025 by default
// (|sysctl kern.maxprocperuid| is 709 on 10.7 and 10.8 and less prior to that).
#if !defined(__APPLE__) && !defined(_WIN32)
TEST_F(SubprocessTest, SetWithLots) {
  // Arbitrary big number; needs to be over 1024 to confirm we're no longer
  // hostage to pselect.
  const size_t kNumProcs = 1025;

  // Make sure [ulimit -n] isn't going to stop us from working.
  rlimit rlim;
  ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
  ASSERT_GT(rlim.rlim_cur, kNumProcs)
      << "Raise [ulimit -n] well above " << kNumProcs
      << " to make this test go";

  vector<Subprocess*> procs;
  for (size_t i = 0; i < kNumProcs; ++i) {
    Subprocess* subproc = subprocs_.Add("/bin/echo");
    ASSERT_NE((Subprocess *) 0, subproc);
    procs.push_back(subproc);
  }
  while (!subprocs_.running_.empty())
    subprocs_.DoWork();
  for (size_t i = 0; i < procs.size(); ++i) {
    ASSERT_EQ(ExitSuccess, procs[i]->Finish());
    ASSERT_NE("", procs[i]->GetOutput());
  }
  ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
}
#endif  // !__APPLE__ && !_WIN32

// TODO: this test could work on Windows, just not sure how to simply
// read stdin.
#ifndef _WIN32
// Verify that a command that attempts to read stdin correctly thinks
// that stdin is closed.
TEST_F(SubprocessTest, ReadStdin) {
  Subprocess* subproc = subprocs_.Add("cat -");
  while (!subproc->Done()) {
    subprocs_.DoWork();
  }
  ASSERT_EQ(ExitSuccess, subproc->Finish());
  ASSERT_EQ(1u, subprocs_.finished_.size());
}
#endif  // _WIN32
<|endoftext|>