<commit_before>/*
  Q Light Controller Plus
  fixturebrowser.cpp

  Copyright (c) Massimo Callegari

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

#include <QQmlEngine>
#include <QQuickItem>
#include <QQmlContext>

#include "fixturebrowser.h"
#include "qlcfixturemode.h"
#include "qlcfixturedef.h"
#include "treemodelitem.h"
#include "treemodel.h"
#include "doc.h"

FixtureBrowser::FixtureBrowser(QQuickView *view, Doc *doc, QObject *parent)
    : QObject(parent)
    , m_doc(doc)
    , m_view(view)
    , m_manufacturerIndex(0)
    , m_selectedManufacturer(QString())
    , m_selectedModel(QString())
    , m_fixtureName(QString())
    , m_selectedMode(QString())
    , m_modeChannelsCount(1)
    , m_definition(NULL)
    , m_mode(NULL)
    , m_searchFilter(QString())
{
    Q_ASSERT(m_doc != NULL);
    Q_ASSERT(m_view != NULL);

    m_view->rootContext()->setContextProperty("fixtureBrowser", this);

    m_searchTree = new TreeModel(this);
    QQmlEngine::setObjectOwnership(m_searchTree, QQmlEngine::CppOwnership);
    m_searchTree->enableSorting(true);
}

FixtureBrowser::~FixtureBrowser()
{
    m_view->rootContext()->setContextProperty("fixtureBrowser", NULL);
}

QStringList FixtureBrowser::manufacturers()
{
    QStringList mfList = m_doc->fixtureDefCache()->manufacturers();
    mfList.sort();
    m_manufacturerIndex = mfList.indexOf("Generic");
    emit manufacturerIndexChanged(m_manufacturerIndex);
    return mfList;
}

QString FixtureBrowser::selectedManufacturer() const
{
    return m_selectedManufacturer;
}

void FixtureBrowser::setSelectedManufacturer(QString selectedManufacturer)
{
    if (m_selectedManufacturer == selectedManufacturer)
        return;

    m_selectedManufacturer = selectedManufacturer;
    emit selectedManufacturerChanged(selectedManufacturer);
    emit modelsListChanged();
}

QStringList FixtureBrowser::modelsList()
{
    qDebug() << "[FixtureBrowser] Models list for" << m_selectedManufacturer;
    QStringList fxList = m_doc->fixtureDefCache()->models(m_selectedManufacturer);
    if (m_selectedManufacturer == "Generic")
    {
        fxList << "Generic Dimmer";
        fxList << "Generic RGB Panel";
    }

    fxList.sort();
    return fxList;
}

QString FixtureBrowser::selectedModel() const
{
    return m_selectedModel;
}

void FixtureBrowser::setSelectedModel(QString selectedModel)
{
    if (m_selectedModel == selectedModel)
        return;

    m_selectedModel = selectedModel;
    setFixtureName(m_selectedModel);
    m_selectedMode = QString();
    emit selectedModelChanged(selectedModel);
    emit modesListChanged();
    emit modeChannelsCountChanged();
    emit modeChannelListChanged();
}

QString FixtureBrowser::fixtureName() const
{
    return m_fixtureName;
}

void FixtureBrowser::setFixtureName(QString fixtureName)
{
    if (m_fixtureName == fixtureName)
        return;

    m_fixtureName = fixtureName;
    emit fixtureNameChanged(fixtureName);
}

QStringList FixtureBrowser::modesList()
{
    qDebug() << "[FixtureBrowser] Modes list for" << m_selectedManufacturer << m_selectedModel;

    QStringList modesList;

    m_definition = m_doc->fixtureDefCache()->fixtureDef(m_selectedManufacturer, m_selectedModel);

    if (m_definition != NULL)
    {
        QList<QLCFixtureMode *> fxModesList = m_definition->modes();
        foreach(QLCFixtureMode *mode, fxModesList)
        {
            modesList.append(mode->name());
            if (m_selectedMode.isEmpty())
            {
                m_selectedMode = mode->name();
                m_modeChannelsCount = mode->channels().count();
            }
        }
    }
    return modesList;
}

QString FixtureBrowser::selectedMode() const
{
    return m_selectedMode;
}

void FixtureBrowser::setSelectedMode(QString selectedMode)
{
    qDebug() << "[FixtureBrowser] Select mode for" << m_selectedManufacturer << m_selectedModel << selectedMode;

    if (m_selectedMode == selectedMode)
        return;

    m_selectedMode = selectedMode;

    if (m_definition != NULL)
    {
        m_mode = m_definition->mode(m_selectedMode);
        if (m_mode)
            m_modeChannelsCount = m_mode->channels().count();
    }
    emit selectedModeChanged(selectedMode);
    emit modeChannelsCountChanged();
    emit modeChannelListChanged();
}

int FixtureBrowser::modeChannelsCount()
{
    if (m_definition != NULL)
    {
        m_mode = m_definition->mode(m_selectedMode);

        if (m_mode != NULL)
            return m_mode->channels().count();
    }
    return m_modeChannelsCount;
}

void FixtureBrowser::setModeChannelsCount(int modeChannelsCount)
{
    if (m_modeChannelsCount == modeChannelsCount)
        return;

    m_modeChannelsCount = modeChannelsCount;
    emit modeChannelsCountChanged();
}

QVariant FixtureBrowser::modeChannelList() const
{
    QVariantList channelList;

    if (m_mode != NULL)
    {
        int i = 1;
        for (QLCChannel *channel : m_mode->channels()) // C++11
        {
            QVariantMap chMap;
            chMap.insert("mIcon", channel->getIconNameFromGroup(channel->group(), true));
            chMap.insert("mLabel", QString("%1: %2").arg(i++).arg(channel->name()));
            channelList.append(chMap);
        }
    }

    return QVariant::fromValue(channelList);
}

int FixtureBrowser::manufacturerIndex() const
{
    return m_manufacturerIndex;
}

void FixtureBrowser::setManufacturerIndex(int index)
{
    if (m_manufacturerIndex == index)
        return;

    m_manufacturerIndex = index;
    emit manufacturerIndexChanged(index);
}

int FixtureBrowser::availableChannel(quint32 uniIdx, int channels, int quantity, int gap, int requested)
{
    qDebug() << "[FixtureBrowser] uniIdx:" << uniIdx << ", channels:" << channels << ", requested:" << requested;
    bool isAvailable = true;
    quint32 uniFilter = uniIdx == Universe::invalid() ? 0 : uniIdx;
    quint32 absAddress = (requested & 0x01FF) | (uniFilter << 9);
    for (int n = 0; n < quantity; n++)
    {
        for (int i = 0; i < channels; i++)
        {
            if(m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId())
            {
                isAvailable = false;
                break;
            }
        }
        absAddress += channels + gap;
    }
    if (isAvailable == true)
    {
        qDebug() << "[FixtureBrowser] Requested channel is available:" << requested;
        return requested;
    }
    else
    {
        qDebug() << "[FixtureBrowser] Requested channel" << requested << "not available in universe" << uniFilter;
        int validAddr = 0;
        int freeCounter = 0;
        absAddress = uniFilter << 9;
        for (int i = 0; i < 512; i++)
        {
            if(m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId())
            {
                freeCounter = 0;
                validAddr = i + 1;
            }
            else
                freeCounter++;

            if (freeCounter == (channels * quantity) + (gap * quantity))
            {
                qDebug() << "[FixtureBrowser] Returning available address:" << validAddr;
                return validAddr;
            }
        }
    }

    return -1;
}

int FixtureBrowser::availableChannel(quint32 fixtureID, int requested)
{
    qDebug() << "[FixtureBrowser] fxID:" << fixtureID << ", requested:" << requested;
    bool isAvailable = true;

    Fixture *fixture = m_doc->fixture(fixtureID);
    if (fixture == NULL)
        return -1;

    quint32 channels = fixture->channels();
    quint32 absAddress = (requested & 0x01FF) | (fixture->universe() << 9);

    for (quint32 i = 0; i < channels; i++)
    {
        quint32 fxIDOnAddr = m_doc->fixtureForAddress(absAddress + i);
        if(fxIDOnAddr != Fixture::invalidId() && fxIDOnAddr != fixtureID)
        {
            isAvailable = false;
            break;
        }
    }

    if (isAvailable == true)
    {
        qDebug() << "[FixtureBrowser] Requested channel is available:" << requested;
        return requested;
    }

    return -1;
}

QString FixtureBrowser::searchFilter() const
{
    return m_searchFilter;
}

void FixtureBrowser::setSearchFilter(QString searchFilter)
{
    if (m_searchFilter == searchFilter)
        return;

    if (m_searchFilter.length() >= SEARCH_MIN_CHARS && searchFilter.length() < SEARCH_MIN_CHARS)
    {
        m_selectedManufacturer = "";
        emit selectedManufacturerChanged(m_selectedManufacturer);
    }

    m_searchFilter = searchFilter;

    if (searchFilter.length() >= SEARCH_MIN_CHARS)
        updateSearchTree();
    else
    {
        m_searchTree->clear();
        emit searchListChanged();
    }

    emit searchFilterChanged(searchFilter);
}

QVariant FixtureBrowser::searchTreeModel() const
{
    return QVariant::fromValue(m_searchTree);
}

void FixtureBrowser::updateSearchTree()
{
    m_searchTree->clear();

    QStringList mfList = m_doc->fixtureDefCache()->manufacturers();
    mfList.sort();

    for(QString manufacturer : mfList) // C++11
    {
        QStringList modelsList = m_doc->fixtureDefCache()->models(manufacturer);
        modelsList.sort();

        for(QString model : modelsList)
        {
            if (manufacturer.toLower().contains(m_searchFilter) ||
                model.toLower().contains(m_searchFilter))
            {
                QVariantList params;
                TreeModelItem *item = m_searchTree->addItem(model, params, manufacturer);
                item->setFlag(TreeModel::Expanded, true);
            }
        }
    }
    emit searchListChanged();
}

<commit_msg>qmlui: reset channel count on fixture model change<commit_after>/*
  Q Light Controller Plus
  fixturebrowser.cpp

  Copyright (c) Massimo Callegari

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

#include <QQmlEngine>
#include <QQuickItem>
#include <QQmlContext>

#include "fixturebrowser.h"
#include "qlcfixturemode.h"
#include "qlcfixturedef.h"
#include "treemodelitem.h"
#include "treemodel.h"
#include "doc.h"

FixtureBrowser::FixtureBrowser(QQuickView *view, Doc *doc, QObject *parent)
    : QObject(parent)
    , m_doc(doc)
    , m_view(view)
    , m_manufacturerIndex(0)
    , m_selectedManufacturer(QString())
    , m_selectedModel(QString())
    , m_fixtureName(QString())
    , m_selectedMode(QString())
    , m_modeChannelsCount(1)
    , m_definition(NULL)
    , m_mode(NULL)
    , m_searchFilter(QString())
{
    Q_ASSERT(m_doc != NULL);
    Q_ASSERT(m_view != NULL);

    m_view->rootContext()->setContextProperty("fixtureBrowser", this);

    m_searchTree = new TreeModel(this);
    QQmlEngine::setObjectOwnership(m_searchTree, QQmlEngine::CppOwnership);
    m_searchTree->enableSorting(true);
}

FixtureBrowser::~FixtureBrowser()
{
    m_view->rootContext()->setContextProperty("fixtureBrowser", NULL);
}

QStringList FixtureBrowser::manufacturers()
{
    QStringList mfList = m_doc->fixtureDefCache()->manufacturers();
    mfList.sort();
    m_manufacturerIndex = mfList.indexOf("Generic");
    emit manufacturerIndexChanged(m_manufacturerIndex);
    return mfList;
}

QString FixtureBrowser::selectedManufacturer() const
{
    return m_selectedManufacturer;
}

void FixtureBrowser::setSelectedManufacturer(QString selectedManufacturer)
{
    if (m_selectedManufacturer == selectedManufacturer)
        return;

    m_selectedManufacturer = selectedManufacturer;
    emit selectedManufacturerChanged(selectedManufacturer);
    emit modelsListChanged();
}

QStringList FixtureBrowser::modelsList()
{
    qDebug() << "[FixtureBrowser] Models list for" << m_selectedManufacturer;
    QStringList fxList = m_doc->fixtureDefCache()->models(m_selectedManufacturer);
    if (m_selectedManufacturer == "Generic")
    {
        fxList << "Generic Dimmer";
        fxList << "Generic RGB Panel";
    }

    fxList.sort();
    return fxList;
}

QString FixtureBrowser::selectedModel() const
{
    return m_selectedModel;
}

void FixtureBrowser::setSelectedModel(QString selectedModel)
{
    if (m_selectedModel == selectedModel)
        return;

    m_selectedModel = selectedModel;
    setFixtureName(m_selectedModel);
    m_selectedMode = QString();
    m_modeChannelsCount = 1;
    emit selectedModelChanged(selectedModel);
    emit modesListChanged();
    emit modeChannelsCountChanged();
    emit modeChannelListChanged();
}

QString FixtureBrowser::fixtureName() const
{
    return m_fixtureName;
}

void FixtureBrowser::setFixtureName(QString fixtureName)
{
    if (m_fixtureName == fixtureName)
        return;

    m_fixtureName = fixtureName;
    emit fixtureNameChanged(fixtureName);
}

QStringList FixtureBrowser::modesList()
{
    qDebug() << "[FixtureBrowser] Modes list for" << m_selectedManufacturer << m_selectedModel;

    QStringList modesList;

    m_definition = m_doc->fixtureDefCache()->fixtureDef(m_selectedManufacturer, m_selectedModel);

    if (m_definition != NULL)
    {
        QList<QLCFixtureMode *> fxModesList = m_definition->modes();
        foreach(QLCFixtureMode *mode, fxModesList)
        {
            modesList.append(mode->name());
            if (m_selectedMode.isEmpty())
            {
                m_selectedMode = mode->name();
                m_modeChannelsCount = mode->channels().count();
            }
        }
    }
    return modesList;
}

QString FixtureBrowser::selectedMode() const
{
    return m_selectedMode;
}

void FixtureBrowser::setSelectedMode(QString selectedMode)
{
    qDebug() << "[FixtureBrowser] Select mode for" << m_selectedManufacturer << m_selectedModel << selectedMode;

    if (m_selectedMode == selectedMode)
        return;

    m_selectedMode = selectedMode;

    if (m_definition != NULL)
    {
        m_mode = m_definition->mode(m_selectedMode);
        if (m_mode)
            m_modeChannelsCount = m_mode->channels().count();
    }
    emit selectedModeChanged(selectedMode);
    emit modeChannelsCountChanged();
    emit modeChannelListChanged();
}

int FixtureBrowser::modeChannelsCount()
{
    if (m_definition != NULL)
    {
        m_mode = m_definition->mode(m_selectedMode);

        if (m_mode != NULL)
            return m_mode->channels().count();
    }
    return m_modeChannelsCount;
}

void FixtureBrowser::setModeChannelsCount(int modeChannelsCount)
{
    if (m_modeChannelsCount == modeChannelsCount)
        return;

    m_modeChannelsCount = modeChannelsCount;
    emit modeChannelsCountChanged();
}

QVariant FixtureBrowser::modeChannelList() const
{
    QVariantList channelList;

    if (m_mode != NULL)
    {
        int i = 1;
        for (QLCChannel *channel : m_mode->channels()) // C++11
        {
            QVariantMap chMap;
            chMap.insert("mIcon", channel->getIconNameFromGroup(channel->group(), true));
            chMap.insert("mLabel", QString("%1: %2").arg(i++).arg(channel->name()));
            channelList.append(chMap);
        }
    }

    return QVariant::fromValue(channelList);
}

int FixtureBrowser::manufacturerIndex() const
{
    return m_manufacturerIndex;
}

void FixtureBrowser::setManufacturerIndex(int index)
{
    if (m_manufacturerIndex == index)
        return;

    m_manufacturerIndex = index;
    emit manufacturerIndexChanged(index);
}

int FixtureBrowser::availableChannel(quint32 uniIdx, int channels, int quantity, int gap, int requested)
{
    qDebug() << "[FixtureBrowser] uniIdx:" << uniIdx << ", channels:" << channels << ", requested:" << requested;
    bool isAvailable = true;
    quint32 uniFilter = uniIdx == Universe::invalid() ? 0 : uniIdx;
    quint32 absAddress = (requested & 0x01FF) | (uniFilter << 9);
    for (int n = 0; n < quantity; n++)
    {
        for (int i = 0; i < channels; i++)
        {
            if(m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId())
            {
                isAvailable = false;
                break;
            }
        }
        absAddress += channels + gap;
    }
    if (isAvailable == true)
    {
        qDebug() << "[FixtureBrowser] Requested channel is available:" << requested;
        return requested;
    }
    else
    {
        qDebug() << "[FixtureBrowser] Requested channel" << requested << "not available in universe" << uniFilter;
        int validAddr = 0;
        int freeCounter = 0;
        absAddress = uniFilter << 9;
        for (int i = 0; i < 512; i++)
        {
            if(m_doc->fixtureForAddress(absAddress + i) != Fixture::invalidId())
            {
                freeCounter = 0;
                validAddr = i + 1;
            }
            else
                freeCounter++;

            if (freeCounter == (channels * quantity) + (gap * quantity))
            {
                qDebug() << "[FixtureBrowser] Returning available address:" << validAddr;
                return validAddr;
            }
        }
    }

    return -1;
}

int FixtureBrowser::availableChannel(quint32 fixtureID, int requested)
{
    qDebug() << "[FixtureBrowser] fxID:" << fixtureID << ", requested:" << requested;
    bool isAvailable = true;

    Fixture *fixture = m_doc->fixture(fixtureID);
    if (fixture == NULL)
        return -1;

    quint32 channels = fixture->channels();
    quint32 absAddress = (requested & 0x01FF) | (fixture->universe() << 9);

    for (quint32 i = 0; i < channels; i++)
    {
        quint32 fxIDOnAddr = m_doc->fixtureForAddress(absAddress + i);
        if(fxIDOnAddr != Fixture::invalidId() && fxIDOnAddr != fixtureID)
        {
            isAvailable = false;
            break;
        }
    }

    if (isAvailable == true)
    {
        qDebug() << "[FixtureBrowser] Requested channel is available:" << requested;
        return requested;
    }

    return -1;
}

QString FixtureBrowser::searchFilter() const
{
    return m_searchFilter;
}

void FixtureBrowser::setSearchFilter(QString searchFilter)
{
    if (m_searchFilter == searchFilter)
        return;

    if (m_searchFilter.length() >= SEARCH_MIN_CHARS && searchFilter.length() < SEARCH_MIN_CHARS)
    {
        m_selectedManufacturer = "";
        emit selectedManufacturerChanged(m_selectedManufacturer);
    }

    m_searchFilter = searchFilter;

    if (searchFilter.length() >= SEARCH_MIN_CHARS)
        updateSearchTree();
    else
    {
        m_searchTree->clear();
        emit searchListChanged();
    }

    emit searchFilterChanged(searchFilter);
}

QVariant FixtureBrowser::searchTreeModel() const
{
    return QVariant::fromValue(m_searchTree);
}

void FixtureBrowser::updateSearchTree()
{
    m_searchTree->clear();

    QStringList mfList = m_doc->fixtureDefCache()->manufacturers();
    mfList.sort();

    for(QString manufacturer : mfList) // C++11
    {
        QStringList modelsList = m_doc->fixtureDefCache()->models(manufacturer);
        modelsList.sort();

        for(QString model : modelsList)
        {
            if (manufacturer.toLower().contains(m_searchFilter) ||
                model.toLower().contains(m_searchFilter))
            {
                QVariantList params;
                TreeModelItem *item = m_searchTree->addItem(model, params, manufacturer);
                item->setFlag(TreeModel::Expanded, true);
            }
        }
    }
    emit searchListChanged();
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: thread.hxx,v $
 * $Revision: 1.11 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _THREAD_HXX_
#define _THREAD_HXX_

#ifdef __cplusplus

#include <osl/time.h>

#include <osl/diagnose.h>
#include <osl/thread.h>
#include <rtl/alloc.h>

namespace osl
{
/** threadFunc is the function which is executed by the threads
    created by the osl::Thread class. The function's signature
    matches the one of oslWorkerFunction which is declared in
    osl/thread.h .
*/
extern "C" inline void SAL_CALL threadFunc( void* param);

class Thread
{
    Thread( const Thread& );
    Thread& operator= ( const Thread& );
public:
    // these are here to force memory de/allocation to sal lib.
    inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW (())
        { return ::rtl_allocateMemory( nSize ); }
    inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW (())
        { ::rtl_freeMemory( pMem ); }
    inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW (())
        { return pMem; }
    inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW (())
        {}

    Thread(): m_hThread(0){}

    virtual  ~Thread()
    {
        osl_destroyThread( m_hThread);
    }

    sal_Bool SAL_CALL create()
    {
        OSL_ASSERT(m_hThread == 0); // only one running thread per instance
           if (m_hThread)
            return sal_False;

        m_hThread = osl_createSuspendedThread( threadFunc, (void*)this);
        if ( m_hThread )
            osl_resumeThread(m_hThread);

        return m_hThread != 0;
    }

    sal_Bool SAL_CALL createSuspended()
    {
        OSL_ASSERT(m_hThread == 0); // only one running thread per instance
        if( m_hThread)
            return sal_False;
        m_hThread= osl_createSuspendedThread( threadFunc,
                                             (void*)this);
        return m_hThread != 0;
    }

    virtual void SAL_CALL suspend()
    {
        if( m_hThread )
            osl_suspendThread(m_hThread);
    }

    virtual void SAL_CALL resume()
    {
        if( m_hThread )
            osl_resumeThread(m_hThread);
    }

    virtual void SAL_CALL terminate()
    {
        if( m_hThread )
            osl_terminateThread(m_hThread);
    }

    virtual void SAL_CALL join()
    {
        osl_joinWithThread(m_hThread);
    }

    sal_Bool SAL_CALL isRunning()
    {
        return osl_isThreadRunning(m_hThread);
    }

    void SAL_CALL setPriority( oslThreadPriority Priority)
    {
        if( m_hThread )
            osl_setThreadPriority(m_hThread, Priority);
    }

    oslThreadPriority SAL_CALL getPriority()
    {
        return m_hThread ? osl_getThreadPriority(m_hThread) : osl_Thread_PriorityUnknown;
    }

    oslThreadIdentifier SAL_CALL getIdentifier() const
    {
        return osl_getThreadIdentifier(m_hThread);
    }

    static oslThreadIdentifier SAL_CALL getCurrentIdentifier()
    {
        return osl_getThreadIdentifier(0);
    }

    static void SAL_CALL wait(const TimeValue& Delay)
    {
        osl_waitThread(&Delay);
    }

    static void SAL_CALL yield()
    {
        osl_yieldThread();
    }

    virtual sal_Bool SAL_CALL schedule()
    {
        return m_hThread ? osl_scheduleThread(m_hThread) : sal_False;
    }

    SAL_CALL operator oslThread() const
    {
        return m_hThread;
    }

protected:

    /** The thread functions calls the protected functions
        run and onTerminated.
    */
    friend void SAL_CALL threadFunc( void* param);

    virtual void SAL_CALL run() = 0;

    virtual void SAL_CALL onTerminated()
    {
    }

private:
    oslThread m_hThread;
};

extern "C" inline void SAL_CALL threadFunc( void* param)
{
        Thread* pObj= (Thread*)param;
        pObj->run();
        pObj->onTerminated();
}

class ThreadData
{
    ThreadData( const ThreadData& );
    ThreadData& operator= (const ThreadData& );
public:
     /// Create a thread specific local data key
    ThreadData( oslThreadKeyCallbackFunction pCallback= 0 )
    {
        m_hKey = osl_createThreadKey( pCallback );
    }

    /// Destroy a thread specific local data key
    ~ThreadData()
    {
           osl_destroyThreadKey(m_hKey);
    }

    /** Set the data associated with the data key.
        @returns True if operation was successfull
    */
    sal_Bool SAL_CALL setData(void *pData)
    {
           return (osl_setThreadKeyData(m_hKey, pData));
    }

    /** Get the data associated with the data key.
        @returns The data asscoitaed with the data key or
        NULL if no data was set
    */
    void* SAL_CALL getData()
    {
           return osl_getThreadKeyData(m_hKey);
    }

    operator oslThreadKey() const
    {
        return m_hKey;
    }

private:
    oslThreadKey m_hKey;
};

} // end namespace osl
#endif
#endif
<commit_msg>#i105639# made two functions const (patch by cmc)<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: thread.hxx,v $
 * $Revision: 1.11 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _THREAD_HXX_
#define _THREAD_HXX_

#ifdef __cplusplus

#include <osl/time.h>

#include <osl/diagnose.h>
#include <osl/thread.h>
#include <rtl/alloc.h>

namespace osl
{
/** threadFunc is the function which is executed by the threads
    created by the osl::Thread class. The function's signature
    matches the one of oslWorkerFunction which is declared in
    osl/thread.h .
*/
extern "C" inline void SAL_CALL threadFunc( void* param);

class Thread
{
    Thread( const Thread& );
    Thread& operator= ( const Thread& );
public:
    // these are here to force memory de/allocation to sal lib.
    inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW (())
        { return ::rtl_allocateMemory( nSize ); }
    inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW (())
        { ::rtl_freeMemory( pMem ); }
    inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW (())
        { return pMem; }
    inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW (())
        {}

    Thread(): m_hThread(0){}

    virtual  ~Thread()
    {
        osl_destroyThread( m_hThread);
    }

    sal_Bool SAL_CALL create()
    {
        OSL_ASSERT(m_hThread == 0); // only one running thread per instance
           if (m_hThread)
            return sal_False;

        m_hThread = osl_createSuspendedThread( threadFunc, (void*)this);
        if ( m_hThread )
            osl_resumeThread(m_hThread);

        return m_hThread != 0;
    }

    sal_Bool SAL_CALL createSuspended()
    {
        OSL_ASSERT(m_hThread == 0); // only one running thread per instance
        if( m_hThread)
            return sal_False;
        m_hThread= osl_createSuspendedThread( threadFunc,
                                             (void*)this);
        return m_hThread != 0;
    }

    virtual void SAL_CALL suspend()
    {
        if( m_hThread )
            osl_suspendThread(m_hThread);
    }

    virtual void SAL_CALL resume()
    {
        if( m_hThread )
            osl_resumeThread(m_hThread);
    }

    virtual void SAL_CALL terminate()
    {
        if( m_hThread )
            osl_terminateThread(m_hThread);
    }

    virtual void SAL_CALL join()
    {
        osl_joinWithThread(m_hThread);
    }

    sal_Bool SAL_CALL isRunning() const
    {
        return osl_isThreadRunning(m_hThread);
    }

    void SAL_CALL setPriority( oslThreadPriority Priority)
    {
        if( m_hThread )
            osl_setThreadPriority(m_hThread, Priority);
    }

    oslThreadPriority SAL_CALL getPriority() const
    {
        return m_hThread ? osl_getThreadPriority(m_hThread) : osl_Thread_PriorityUnknown;
    }

    oslThreadIdentifier SAL_CALL getIdentifier() const
    {
        return osl_getThreadIdentifier(m_hThread);
    }

    static oslThreadIdentifier SAL_CALL getCurrentIdentifier()
    {
        return osl_getThreadIdentifier(0);
    }

    static void SAL_CALL wait(const TimeValue& Delay)
    {
        osl_waitThread(&Delay);
    }

    static void SAL_CALL yield()
    {
        osl_yieldThread();
    }

    virtual sal_Bool SAL_CALL schedule()
    {
        return m_hThread ? osl_scheduleThread(m_hThread) : sal_False;
    }

    SAL_CALL operator oslThread() const
    {
        return m_hThread;
    }

protected:

    /** The thread functions calls the protected functions
        run and onTerminated.
    */
    friend void SAL_CALL threadFunc( void* param);

    virtual void SAL_CALL run() = 0;

    virtual void SAL_CALL onTerminated()
    {
    }

private:
    oslThread m_hThread;
};

extern "C" inline void SAL_CALL threadFunc( void* param)
{
        Thread* pObj= (Thread*)param;
        pObj->run();
        pObj->onTerminated();
}

class ThreadData
{
    ThreadData( const ThreadData& );
    ThreadData& operator= (const ThreadData& );
public:
     /// Create a thread specific local data key
    ThreadData( oslThreadKeyCallbackFunction pCallback= 0 )
    {
        m_hKey = osl_createThreadKey( pCallback );
    }

    /// Destroy a thread specific local data key
    ~ThreadData()
    {
           osl_destroyThreadKey(m_hKey);
    }

    /** Set the data associated with the data key.
        @returns True if operation was successfull
    */
    sal_Bool SAL_CALL setData(void *pData)
    {
           return (osl_setThreadKeyData(m_hKey, pData));
    }

    /** Get the data associated with the data key.
        @returns The data asscoitaed with the data key or
        NULL if no data was set
    */
    void* SAL_CALL getData()
    {
           return osl_getThreadKeyData(m_hKey);
    }

    operator oslThreadKey() const
    {
        return m_hKey;
    }

private:
    oslThreadKey m_hKey;
};

} // end namespace osl
#endif
#endif
<|endoftext|>


<commit_before>/*
 * PosixChildProcess.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ChildProcess.hpp"

#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#include <boost/bind.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>
#include <core/system/ProcessArgs.hpp>

#include <core/PerformanceTimer.hpp>

#include "ChildProcess.hpp"

namespace core {
namespace system {

namespace {

// pipe handle indexes
const int READ = 0;
const int WRITE = 1;
const std::size_t READ_ERR = -1;

int resolveExitStatus(int status)
{
   return WIFEXITED(status) ? WEXITSTATUS(status) : status;
}

void setPipeNonBlocking(int pipeFd)
{
   int flags = ::fcntl(pipeFd, F_GETFL);
   if ( (flags != -1) && !(flags & O_NONBLOCK) )
     ::fcntl(pipeFd, F_SETFL, flags | O_NONBLOCK);
}

void closePipe(int pipeFd, const ErrorLocation& location)
{
   safePosixCall<int>(boost::bind(::close, pipeFd), location);
}

void closePipe(int* pipeDescriptors, const ErrorLocation& location)
{
   closePipe(pipeDescriptors[READ], location);
   closePipe(pipeDescriptors[WRITE], location);
}

Error readPipe(int pipeFd, std::string* pOutput, bool *pEOF = NULL)
{
   // default to not eof
   if (pEOF)
      *pEOF = false;

   // setup and read into buffer
   const std::size_t kBufferSize = 512;
   char buffer[kBufferSize];
   std::size_t bytesRead = posixCall<std::size_t>(
                     boost::bind(::read, pipeFd, buffer, kBufferSize));
   while (true)
   {
      // check for error
      if (bytesRead == READ_ERR)
      {
         if (errno == EAGAIN) // carve-out for O_NONBLOCK pipes
            return Success();
         else
            return systemError(errno, ERROR_LOCATION);
      }

      // check for eof
      else if (bytesRead == 0)
      {
         if (pEOF)
            *pEOF = true;

         return Success();
      }

      // append to output
      pOutput->append(buffer, bytesRead);

      // read more bytes
      bytesRead = posixCall<std::size_t>(
                        boost::bind(::read, pipeFd, buffer, kBufferSize));
   }

   // keep compiler happy
   return Success();
}

} // anonymous namespace

struct ChildProcess::Impl
{
   Impl() : pid(-1), fdStdin(-1), fdStdout(-1), fdStderr(-1) {}
   pid_t pid;
   int fdStdin;
   int fdStdout;
   int fdStderr;

   void closeAll(const ErrorLocation& location)
   {
      pid = -1;
      closeFD(&fdStdin, location);
      closeFD(&fdStdout, location);
      closeFD(&fdStderr, location);
   }

   void closeFD(int* pFD, const ErrorLocation& location)
   {
      if (*pFD >= 0)
      {
         closePipe(*pFD, location);
         *pFD = -1;
      }
   }
};

ChildProcess::ChildProcess()
  : pImpl_(new Impl())
{
}

void ChildProcess::init(const std::string& exe,
                        const std::vector<std::string>& args,
                        const ProcessOptions& options)
{
   exe_ = exe;
   args_ = args;
   options_ = options;
}

void ChildProcess::init(const std::string& command,
                        const ProcessOptions& options)
{
   std::vector<std::string> args;
   args.push_back("-c");
   args.push_back(command);
   init("/bin/sh", args, options);
}

ChildProcess::~ChildProcess()
{
}

Error ChildProcess::writeToStdin(const std::string& input, bool eof)
{
   std::size_t written;
   Error error = posixCall<std::size_t>(boost::bind(::write,
                                                   pImpl_->fdStdin,
                                                   input.c_str(),
                                                   input.length()),
                                        ERROR_LOCATION,
                                        &written);
   if (error)
      return error;

   // close if requested
   if (eof)
      pImpl_->closeFD(&pImpl_->fdStdin, ERROR_LOCATION);

   // check for correct bytes written
   if (written != static_cast<std::size_t>(input.length()))
       return systemError(boost::system::errc::io_error, ERROR_LOCATION);

   // return success
   return Success();
}

Error ChildProcess::terminate()
{
   // only send signal if the process is open
   if (pImpl_->pid == -1)
      return systemError(ESRCH, ERROR_LOCATION);

   // determine target pid (kill just this pid or pid + children)
   pid_t pid = pImpl_->pid;
   if (options_.terminateChildren)
      pid = -pid;

   // send signal
   if (::kill(pid, SIGTERM) == -1)
      return systemError(errno, ERROR_LOCATION);
   else
      return Success();
}

Error ChildProcess::run()
{
   // standard input
   int fdInput[2];
   Error error = posixCall<int>(boost::bind(::pipe, fdInput), ERROR_LOCATION);
   if (error)
      return error;

   // standard output
   int fdOutput[2];
   error = posixCall<int>(boost::bind(::pipe, fdOutput), ERROR_LOCATION);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      return error;
   }

   // standard error
   int fdError[2];
   error = posixCall<int>(boost::bind(::pipe, fdError), ERROR_LOCATION);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      closePipe(fdOutput, ERROR_LOCATION);
      return error;
   }

   // fork
   pid_t pid;
   error = posixCall<pid_t>(::fork, ERROR_LOCATION, &pid);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      closePipe(fdOutput, ERROR_LOCATION);
      closePipe(fdError, ERROR_LOCATION);
      return error;
   }

   // child
   else if (pid == 0)
   {
      // NOTE: within the child we want to make sure in all cases that
      // we call ::execv to execute the program. as a result if any
      // errors occur while we are setting up for the ::execv we log
      // and continue rather than calling ::exit (we do this to avoid
      // strange error conditions related to global c++ objects being
      // torn down in a non-standard sequence).

      // check for an onAfterFork function
       if (options_.onAfterFork)
          options_.onAfterFork();

      // If options.detachSession is requested then separate
      if (options_.detachSession)
      {
         if (::setsid() == -1)
         {
            LOG_ERROR(systemError(errno, ERROR_LOCATION));
            // intentionally fail forward (see note above)
         }
      }
      else if (options_.terminateChildren)
      {
         // No need to call ::setpgid(0,0) if ::setsid() was already called

         // if options.terminateChildren is requested then obtain a new process
         // group (using our own process id). this enables terminate to
         // specify -pid to kill which will kill this process and all of its
         // children. note that another side-effect is that this process
         // will not automatically die with its parent, so the parent
         // may want to kill all children from the processSupervisor on exit
         if (::setpgid(0,0) == -1)
         {
            LOG_ERROR(systemError(errno, ERROR_LOCATION));
            // intentionally fail forward (see note above)
         }
      }

      // clear the child signal mask
      error = core::system::clearSignalMask();
      if (error)
      {
         LOG_ERROR(error);
         // intentionally fail forward (see note above)
      }

      // close unused pipes -- intentionally fail forward (see note above)
      closePipe(fdInput[WRITE], ERROR_LOCATION);
      closePipe(fdOutput[READ], ERROR_LOCATION);
      closePipe(fdError[READ], ERROR_LOCATION);

      // wire standard streams (intentionally fail forward)
      safePosixCall<int>(boost::bind(::dup2, fdInput[READ], STDIN_FILENO),
                         ERROR_LOCATION);
      safePosixCall<int>(boost::bind(::dup2, fdOutput[WRITE], STDOUT_FILENO),
                         ERROR_LOCATION);
      safePosixCall<int>(boost::bind(::dup2, fdError[WRITE], STDERR_FILENO),
                         ERROR_LOCATION);

      // close all open file descriptors other than std streams
      error = core::system::closeNonStdFileDescriptors();
      if (error)
      {
         LOG_ERROR(error);
         // intentionally fail forward (see note above)
      }

      // build args (on heap so they stay around after exec)
      // create set of args to pass (needs to include the cmd)
      std::vector<std::string> args;
      args.push_back(exe_);
      args.insert(args.end(), args_.begin(), args_.end());
      using core::system::ProcessArgs;
      ProcessArgs* pProcessArgs = new ProcessArgs(args);

      if (options_.environment)
      {
         // build env (on heap, see comment above)
         std::vector<std::string> env;
         const Options& options = options_.environment.get();
         for (Options::const_iterator
                  it = options.begin(); it != options.end(); ++it)
         {
            env.push_back(it->first + "=" + it->second);
         }
         ProcessArgs* pEnvironment = new ProcessArgs(env);

         // execute
         ::execve(exe_.c_str(), pProcessArgs->args(), pEnvironment->args());
      }
      else
      {
         // execute
         ::execv(exe_.c_str(), pProcessArgs->args()) ;
      }

      // in the normal case control should never return from execv (it starts
      // anew at main of the process pointed to by path). therefore, if we get
      // here then there was an error
      LOG_ERROR(systemError(errno, ERROR_LOCATION)) ;
      ::exit(EXIT_FAILURE) ;
   }

   // parent
   else
   {
      // close unused pipes
      closePipe(fdInput[READ], ERROR_LOCATION);
      closePipe(fdOutput[WRITE], ERROR_LOCATION);
      closePipe(fdError[WRITE], ERROR_LOCATION);

      // record pid and pipe handles
      pImpl_->pid = pid;
      pImpl_->fdStdin = fdInput[WRITE];
      pImpl_->fdStdout = fdOutput[READ];
      pImpl_->fdStderr = fdError[READ];

      return Success();
   }

   // keep compiler happy
   return Success();
}

Error SyncChildProcess::readStdOut(std::string* pOutput)
{
   return readPipe(pImpl_->fdStdout, pOutput);
}

Error SyncChildProcess::readStdErr(std::string* pOutput)
{
   return readPipe(pImpl_->fdStderr, pOutput);
}

Error SyncChildProcess::waitForExit(int* pExitStatus)
{
   // blocking wait for exit
   int status;
   pid_t result = posixCall<pid_t>(
      boost::bind(::waitpid, pImpl_->pid, &status, 0));

   // always close all of the pipes
   pImpl_->closeAll(ERROR_LOCATION);

   // check result
   if (result == -1)
   {
      *pExitStatus = -1;

      if (errno == ECHILD) // carve out for child already reaped
         return Success();
      else
         return systemError(errno, ERROR_LOCATION);
   }
   else
   {
      *pExitStatus = resolveExitStatus(status);
      return Success();
   }
}

struct AsyncChildProcess::AsyncImpl
{
   AsyncImpl()
      : calledOnStarted_(false),
        finishedStdout_(false),
        finishedStderr_(false),
        exited_(false)
   {
   }

   bool calledOnStarted_;
   bool finishedStdout_;
   bool finishedStderr_;
   bool exited_;
};

AsyncChildProcess::AsyncChildProcess(const std::string& exe,
                                     const std::vector<std::string>& args,
                                     const ProcessOptions& options)
   : ChildProcess(), pAsyncImpl_(new AsyncImpl())
{
   init(exe, args, options);
}

AsyncChildProcess::AsyncChildProcess(const std::string& command,
                                     const ProcessOptions& options)
      : ChildProcess(), pAsyncImpl_(new AsyncImpl())
{
   init(command, options);
}

AsyncChildProcess::~AsyncChildProcess()
{
}

void AsyncChildProcess::poll()
{
   // call onStarted if we haven't yet
   if (!(pAsyncImpl_->calledOnStarted_))
   {
      // make sure the output pipes are setup for async reading
      setPipeNonBlocking(pImpl_->fdStdout);
      setPipeNonBlocking(pImpl_->fdStderr);

      if (callbacks_.onStarted)
         callbacks_.onStarted(*this);
      pAsyncImpl_->calledOnStarted_ = true;
   }

   // call onContinue
   if (callbacks_.onContinue)
   {
      if (!callbacks_.onContinue(*this))
      {
         // terminate the proces
         Error error = terminate();
         if (error)
            LOG_ERROR(error);
      }
   }

   // check stdout and fire event if we got output
   if (!pAsyncImpl_->finishedStdout_)
   {
      bool eof;
      std::string out;
      Error error = readPipe(pImpl_->fdStdout, &out, &eof);
      if (error)
      {
         reportError(error);
      }
      else
      {
         if (!out.empty() && callbacks_.onStdout)
            callbacks_.onStdout(*this, out);

         if (eof)
           pAsyncImpl_->finishedStdout_ = true;
      }
   }

   // check stderr and fire event if we got output
   if (!pAsyncImpl_->finishedStderr_)
   {
      bool eof;
      std::string err;
      Error error = readPipe(pImpl_->fdStderr, &err, &eof);

      if (error)
      {
         reportError(error);
      }
      else
      {
         if (!err.empty() && callbacks_.onStderr)
            callbacks_.onStderr(*this, err);

         if (eof)
           pAsyncImpl_->finishedStderr_ = true;
      }
   }

   // Check for exited. Note that this method specifies WNOHANG
   // so we don't block forever waiting for a process the exit. We may
   // not be able to reap the child due to an error (typically ECHILD,
   // which occurs if the child was reaped by a global handler) in which
   // case we'll allow the exit sequence to proceed and simply pass -1 as
   // the exit status.
   int status;
   pid_t result = posixCall<pid_t>(
            boost::bind(::waitpid, pImpl_->pid, &status, WNOHANG));

   // either a normal exit or an error while waiting
   if (result != 0)
   {
      // close all of our pipes
      pImpl_->closeAll(ERROR_LOCATION);

      // fire exit event
      if (callbacks_.onExit)
      {
         // resolve exit status
         if (result > 0)
            status = resolveExitStatus(status);
         else
            status = -1;

         // call onExit
         callbacks_.onExit(status);
      }

      // set exited_ flag so that our exited function always
      // returns the right value
      pAsyncImpl_->exited_ = true;

      // if this is an error that isn't ECHILD then log it (we never
      // expect this to occur as the only documented error codes are
      // EINTR and ECHILD, and EINTR is handled internally by posixCall)
      if (result == -1 && errno != ECHILD)
         LOG_ERROR(systemError(errno, ERROR_LOCATION));
   }
}

bool AsyncChildProcess::exited()
{
   return pAsyncImpl_->exited_;
}

} // namespace system
} // namespace core

<commit_msg>don't log EPERM errors when killing an entire process group<commit_after>/*
 * PosixChildProcess.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ChildProcess.hpp"

#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#include <boost/bind.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>
#include <core/system/ProcessArgs.hpp>

#include <core/PerformanceTimer.hpp>

#include "ChildProcess.hpp"

namespace core {
namespace system {

namespace {

// pipe handle indexes
const int READ = 0;
const int WRITE = 1;
const std::size_t READ_ERR = -1;

int resolveExitStatus(int status)
{
   return WIFEXITED(status) ? WEXITSTATUS(status) : status;
}

void setPipeNonBlocking(int pipeFd)
{
   int flags = ::fcntl(pipeFd, F_GETFL);
   if ( (flags != -1) && !(flags & O_NONBLOCK) )
     ::fcntl(pipeFd, F_SETFL, flags | O_NONBLOCK);
}

void closePipe(int pipeFd, const ErrorLocation& location)
{
   safePosixCall<int>(boost::bind(::close, pipeFd), location);
}

void closePipe(int* pipeDescriptors, const ErrorLocation& location)
{
   closePipe(pipeDescriptors[READ], location);
   closePipe(pipeDescriptors[WRITE], location);
}

Error readPipe(int pipeFd, std::string* pOutput, bool *pEOF = NULL)
{
   // default to not eof
   if (pEOF)
      *pEOF = false;

   // setup and read into buffer
   const std::size_t kBufferSize = 512;
   char buffer[kBufferSize];
   std::size_t bytesRead = posixCall<std::size_t>(
                     boost::bind(::read, pipeFd, buffer, kBufferSize));
   while (true)
   {
      // check for error
      if (bytesRead == READ_ERR)
      {
         if (errno == EAGAIN) // carve-out for O_NONBLOCK pipes
            return Success();
         else
            return systemError(errno, ERROR_LOCATION);
      }

      // check for eof
      else if (bytesRead == 0)
      {
         if (pEOF)
            *pEOF = true;

         return Success();
      }

      // append to output
      pOutput->append(buffer, bytesRead);

      // read more bytes
      bytesRead = posixCall<std::size_t>(
                        boost::bind(::read, pipeFd, buffer, kBufferSize));
   }

   // keep compiler happy
   return Success();
}

} // anonymous namespace

struct ChildProcess::Impl
{
   Impl() : pid(-1), fdStdin(-1), fdStdout(-1), fdStderr(-1) {}
   pid_t pid;
   int fdStdin;
   int fdStdout;
   int fdStderr;

   void closeAll(const ErrorLocation& location)
   {
      pid = -1;
      closeFD(&fdStdin, location);
      closeFD(&fdStdout, location);
      closeFD(&fdStderr, location);
   }

   void closeFD(int* pFD, const ErrorLocation& location)
   {
      if (*pFD >= 0)
      {
         closePipe(*pFD, location);
         *pFD = -1;
      }
   }
};

ChildProcess::ChildProcess()
  : pImpl_(new Impl())
{
}

void ChildProcess::init(const std::string& exe,
                        const std::vector<std::string>& args,
                        const ProcessOptions& options)
{
   exe_ = exe;
   args_ = args;
   options_ = options;
}

void ChildProcess::init(const std::string& command,
                        const ProcessOptions& options)
{
   std::vector<std::string> args;
   args.push_back("-c");
   args.push_back(command);
   init("/bin/sh", args, options);
}

ChildProcess::~ChildProcess()
{
}

Error ChildProcess::writeToStdin(const std::string& input, bool eof)
{
   std::size_t written;
   Error error = posixCall<std::size_t>(boost::bind(::write,
                                                   pImpl_->fdStdin,
                                                   input.c_str(),
                                                   input.length()),
                                        ERROR_LOCATION,
                                        &written);
   if (error)
      return error;

   // close if requested
   if (eof)
      pImpl_->closeFD(&pImpl_->fdStdin, ERROR_LOCATION);

   // check for correct bytes written
   if (written != static_cast<std::size_t>(input.length()))
       return systemError(boost::system::errc::io_error, ERROR_LOCATION);

   // return success
   return Success();
}

Error ChildProcess::terminate()
{
   // only send signal if the process is open
   if (pImpl_->pid == -1)
      return systemError(ESRCH, ERROR_LOCATION);

   // determine target pid (kill just this pid or pid + children)
   pid_t pid = pImpl_->pid;
   if (options_.detachSession || options_.terminateChildren)
      pid = -pid;

   // send signal
   if (::kill(pid, SIGTERM) == -1)
   {
      // when killing an entire process group EPERM can be returned if even
      // a single one of the subprocesses couldn't be killed. in this case
      // the signal is still delivered and other subprocesses may have been
      // killed so we don't log an error
      if (pid < 0 && errno == EPERM)
         return Success();
      else
         return systemError(errno, ERROR_LOCATION);
   }
   else
      return Success();
}

Error ChildProcess::run()
{
   // standard input
   int fdInput[2];
   Error error = posixCall<int>(boost::bind(::pipe, fdInput), ERROR_LOCATION);
   if (error)
      return error;

   // standard output
   int fdOutput[2];
   error = posixCall<int>(boost::bind(::pipe, fdOutput), ERROR_LOCATION);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      return error;
   }

   // standard error
   int fdError[2];
   error = posixCall<int>(boost::bind(::pipe, fdError), ERROR_LOCATION);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      closePipe(fdOutput, ERROR_LOCATION);
      return error;
   }

   // fork
   pid_t pid;
   error = posixCall<pid_t>(::fork, ERROR_LOCATION, &pid);
   if (error)
   {
      closePipe(fdInput, ERROR_LOCATION);
      closePipe(fdOutput, ERROR_LOCATION);
      closePipe(fdError, ERROR_LOCATION);
      return error;
   }

   // child
   else if (pid == 0)
   {
      // NOTE: within the child we want to make sure in all cases that
      // we call ::execv to execute the program. as a result if any
      // errors occur while we are setting up for the ::execv we log
      // and continue rather than calling ::exit (we do this to avoid
      // strange error conditions related to global c++ objects being
      // torn down in a non-standard sequence).

      // check for an onAfterFork function
       if (options_.onAfterFork)
          options_.onAfterFork();

      // If options.detachSession is requested then separate
      if (options_.detachSession)
      {
         if (::setsid() == -1)
         {
            LOG_ERROR(systemError(errno, ERROR_LOCATION));
            // intentionally fail forward (see note above)
         }
      }
      else if (options_.terminateChildren)
      {
         // No need to call ::setpgid(0,0) if ::setsid() was already called

         // if options.terminateChildren is requested then obtain a new process
         // group (using our own process id). this enables terminate to
         // specify -pid to kill which will kill this process and all of its
         // children. note that another side-effect is that this process
         // will not automatically die with its parent, so the parent
         // may want to kill all children from the processSupervisor on exit
         if (::setpgid(0,0) == -1)
         {
            LOG_ERROR(systemError(errno, ERROR_LOCATION));
            // intentionally fail forward (see note above)
         }
      }

      // clear the child signal mask
      error = core::system::clearSignalMask();
      if (error)
      {
         LOG_ERROR(error);
         // intentionally fail forward (see note above)
      }

      // close unused pipes -- intentionally fail forward (see note above)
      closePipe(fdInput[WRITE], ERROR_LOCATION);
      closePipe(fdOutput[READ], ERROR_LOCATION);
      closePipe(fdError[READ], ERROR_LOCATION);

      // wire standard streams (intentionally fail forward)
      safePosixCall<int>(boost::bind(::dup2, fdInput[READ], STDIN_FILENO),
                         ERROR_LOCATION);
      safePosixCall<int>(boost::bind(::dup2, fdOutput[WRITE], STDOUT_FILENO),
                         ERROR_LOCATION);
      safePosixCall<int>(boost::bind(::dup2, fdError[WRITE], STDERR_FILENO),
                         ERROR_LOCATION);

      // close all open file descriptors other than std streams
      error = core::system::closeNonStdFileDescriptors();
      if (error)
      {
         LOG_ERROR(error);
         // intentionally fail forward (see note above)
      }

      // build args (on heap so they stay around after exec)
      // create set of args to pass (needs to include the cmd)
      std::vector<std::string> args;
      args.push_back(exe_);
      args.insert(args.end(), args_.begin(), args_.end());
      using core::system::ProcessArgs;
      ProcessArgs* pProcessArgs = new ProcessArgs(args);

      if (options_.environment)
      {
         // build env (on heap, see comment above)
         std::vector<std::string> env;
         const Options& options = options_.environment.get();
         for (Options::const_iterator
                  it = options.begin(); it != options.end(); ++it)
         {
            env.push_back(it->first + "=" + it->second);
         }
         ProcessArgs* pEnvironment = new ProcessArgs(env);

         // execute
         ::execve(exe_.c_str(), pProcessArgs->args(), pEnvironment->args());
      }
      else
      {
         // execute
         ::execv(exe_.c_str(), pProcessArgs->args()) ;
      }

      // in the normal case control should never return from execv (it starts
      // anew at main of the process pointed to by path). therefore, if we get
      // here then there was an error
      LOG_ERROR(systemError(errno, ERROR_LOCATION)) ;
      ::exit(EXIT_FAILURE) ;
   }

   // parent
   else
   {
      // close unused pipes
      closePipe(fdInput[READ], ERROR_LOCATION);
      closePipe(fdOutput[WRITE], ERROR_LOCATION);
      closePipe(fdError[WRITE], ERROR_LOCATION);

      // record pid and pipe handles
      pImpl_->pid = pid;
      pImpl_->fdStdin = fdInput[WRITE];
      pImpl_->fdStdout = fdOutput[READ];
      pImpl_->fdStderr = fdError[READ];

      return Success();
   }

   // keep compiler happy
   return Success();
}

Error SyncChildProcess::readStdOut(std::string* pOutput)
{
   return readPipe(pImpl_->fdStdout, pOutput);
}

Error SyncChildProcess::readStdErr(std::string* pOutput)
{
   return readPipe(pImpl_->fdStderr, pOutput);
}

Error SyncChildProcess::waitForExit(int* pExitStatus)
{
   // blocking wait for exit
   int status;
   pid_t result = posixCall<pid_t>(
      boost::bind(::waitpid, pImpl_->pid, &status, 0));

   // always close all of the pipes
   pImpl_->closeAll(ERROR_LOCATION);

   // check result
   if (result == -1)
   {
      *pExitStatus = -1;

      if (errno == ECHILD) // carve out for child already reaped
         return Success();
      else
         return systemError(errno, ERROR_LOCATION);
   }
   else
   {
      *pExitStatus = resolveExitStatus(status);
      return Success();
   }
}

struct AsyncChildProcess::AsyncImpl
{
   AsyncImpl()
      : calledOnStarted_(false),
        finishedStdout_(false),
        finishedStderr_(false),
        exited_(false)
   {
   }

   bool calledOnStarted_;
   bool finishedStdout_;
   bool finishedStderr_;
   bool exited_;
};

AsyncChildProcess::AsyncChildProcess(const std::string& exe,
                                     const std::vector<std::string>& args,
                                     const ProcessOptions& options)
   : ChildProcess(), pAsyncImpl_(new AsyncImpl())
{
   init(exe, args, options);
}

AsyncChildProcess::AsyncChildProcess(const std::string& command,
                                     const ProcessOptions& options)
      : ChildProcess(), pAsyncImpl_(new AsyncImpl())
{
   init(command, options);
}

AsyncChildProcess::~AsyncChildProcess()
{
}

void AsyncChildProcess::poll()
{
   // call onStarted if we haven't yet
   if (!(pAsyncImpl_->calledOnStarted_))
   {
      // make sure the output pipes are setup for async reading
      setPipeNonBlocking(pImpl_->fdStdout);
      setPipeNonBlocking(pImpl_->fdStderr);

      if (callbacks_.onStarted)
         callbacks_.onStarted(*this);
      pAsyncImpl_->calledOnStarted_ = true;
   }

   // call onContinue
   if (callbacks_.onContinue)
   {
      if (!callbacks_.onContinue(*this))
      {
         // terminate the proces
         Error error = terminate();
         if (error)
            LOG_ERROR(error);
      }
   }

   // check stdout and fire event if we got output
   if (!pAsyncImpl_->finishedStdout_)
   {
      bool eof;
      std::string out;
      Error error = readPipe(pImpl_->fdStdout, &out, &eof);
      if (error)
      {
         reportError(error);
      }
      else
      {
         if (!out.empty() && callbacks_.onStdout)
            callbacks_.onStdout(*this, out);

         if (eof)
           pAsyncImpl_->finishedStdout_ = true;
      }
   }

   // check stderr and fire event if we got output
   if (!pAsyncImpl_->finishedStderr_)
   {
      bool eof;
      std::string err;
      Error error = readPipe(pImpl_->fdStderr, &err, &eof);

      if (error)
      {
         reportError(error);
      }
      else
      {
         if (!err.empty() && callbacks_.onStderr)
            callbacks_.onStderr(*this, err);

         if (eof)
           pAsyncImpl_->finishedStderr_ = true;
      }
   }

   // Check for exited. Note that this method specifies WNOHANG
   // so we don't block forever waiting for a process the exit. We may
   // not be able to reap the child due to an error (typically ECHILD,
   // which occurs if the child was reaped by a global handler) in which
   // case we'll allow the exit sequence to proceed and simply pass -1 as
   // the exit status.
   int status;
   pid_t result = posixCall<pid_t>(
            boost::bind(::waitpid, pImpl_->pid, &status, WNOHANG));

   // either a normal exit or an error while waiting
   if (result != 0)
   {
      // close all of our pipes
      pImpl_->closeAll(ERROR_LOCATION);

      // fire exit event
      if (callbacks_.onExit)
      {
         // resolve exit status
         if (result > 0)
            status = resolveExitStatus(status);
         else
            status = -1;

         // call onExit
         callbacks_.onExit(status);
      }

      // set exited_ flag so that our exited function always
      // returns the right value
      pAsyncImpl_->exited_ = true;

      // if this is an error that isn't ECHILD then log it (we never
      // expect this to occur as the only documented error codes are
      // EINTR and ECHILD, and EINTR is handled internally by posixCall)
      if (result == -1 && errno != ECHILD)
         LOG_ERROR(systemError(errno, ERROR_LOCATION));
   }
}

bool AsyncChildProcess::exited()
{
   return pAsyncImpl_->exited_;
}

} // namespace system
} // namespace core

<|endoftext|>


<commit_before>#include <string>

#include "logprintf.h"
#include "wrapper.h"
#include "internal/jump.h"

#include "plugin/amx/amx.h"
#include "plugin/plugincommon.h"

extern void *pAMXFunctions;

static uint32_t amx_Register_addr;
static unsigned char amx_Register_code[5];

static uint32_t amx_FindPublic_addr;
static unsigned char amx_FindPublic_code[5];

static uint32_t amx_Exec_addr;
static unsigned char amx_Exec_code[5];

static AMX *pGamemode = 0;
static std::string lastPublicName;

namespace samp {

Wrapper::Wrapper() {}

Wrapper *Wrapper::GetInstance() {
    static Wrapper w;
    return &w;
}

static int my_amx_Register(AMX *amx, AMX_NATIVE_INFO *nativelist, size_t number) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_Register_addr), ::amx_Register_code, 5);
    int error = amx_Register(amx, nativelist, number);

    // Store natives in our global container
    for (size_t i = 0; nativelist[i].name != 0 && (i < number || number == -1); ++i) {
        Wrapper::GetInstance()->SetNative(nativelist[i].name, nativelist[i].func);
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_Register_addr), my_amx_Register, ::amx_Register_code);

    return error;
}

static int my_amx_FindPublic(AMX *amx, const char *name, int *index) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_FindPublic_addr), ::amx_FindPublic_code, 5);
    int error = amx_FindPublic(amx, name, index);

    if (amx == ::pGamemode) {
        if (error != AMX_ERR_NONE) {
            // The requested public doesn't exist but we say it does
            // to let the server subsequently execute it.
            *index = -1337;
        }
        ::lastPublicName.assign(name);
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_FindPublic_addr), my_amx_FindPublic, ::amx_FindPublic_code);

    return error;
}

static int my_amx_Exec(AMX *amx, cell *retval, int index) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_Exec_addr), ::amx_Exec_code, 5);

    samp::Wrapper::GetInstance()->CallPublic(::pGamemode, ::lastPublicName);

    int error = AMX_ERR_NONE;
    if (index != -1337) {
        // It's calling a fake public (see my_amx_FindPublic for details).
        if (index == AMX_EXEC_MAIN) {
            // main() is being called => this script is the gamemode
            ::pGamemode = amx;
        }
        error = amx_Exec(amx, retval, index);
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_Exec_addr), my_amx_Exec, ::amx_Exec_code);

    return error;
}

void Wrapper::Initialize(void **ppPluginData) {
    ::pAMXFunctions = ppPluginData[PLUGIN_DATA_AMX_EXPORTS];
    ::logprintf = (logprintf_t)ppPluginData[PLUGIN_DATA_LOGPRINTF];

    // Hook amx_Register
    ::amx_Register_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_Register]);
    SetJump(reinterpret_cast<void*>(::amx_Register_addr), my_amx_Register, ::amx_Register_code);

    // Hook amx_FindPublic
    ::amx_FindPublic_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_FindPublic]);
    SetJump(reinterpret_cast<void*>(::amx_FindPublic_addr), my_amx_FindPublic, ::amx_FindPublic_code);

    // Hook amx_Exec
    ::amx_Exec_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_Exec]);
    SetJump(reinterpret_cast<void*>(::amx_Exec_addr), my_amx_Exec, ::amx_Exec_code);
}

void Wrapper::SetNative(const std::string &name, AMX_NATIVE native) {
    if (!name.empty()) {
        natives_[name] = native;
    }
}

AMX_NATIVE Wrapper::GetNative(const std::string &name) const {
    auto it = natives_.find(name);
    if (it != natives_.end()) {
        return it->second;
    }
    return 0;
}

void Wrapper::SetPublicHandler(const std::string &name, PublicHandler handler) {
    publicHandlers_[name] = handler;
}

cell Wrapper::CallPublic(AMX *amx, const std::string &name) const {
    auto it = publicHandlers_.find(name);
    if (it != publicHandlers_.end()) {
        return it->second(amx);
    }
    return 0;
}

} // namespace samp<commit_msg>added initialization of callback handlers<commit_after>#include <string>

#include "logprintf.h"
#include "wrapper.h"

#include "internal/callbacks.h"
#include "internal/jump.h"

#include "plugin/amx/amx.h"
#include "plugin/plugincommon.h"

extern void *pAMXFunctions;

static uint32_t amx_Register_addr;
static unsigned char amx_Register_code[5];

static int my_amx_Register(AMX *amx, AMX_NATIVE_INFO *nativelist, size_t number) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_Register_addr), ::amx_Register_code, 5);
    int error = amx_Register(amx, nativelist, number);

    // Store natives in our global container
    for (size_t i = 0; nativelist[i].name != 0 && (i < number || number == -1); ++i) {
        samp::Wrapper::GetInstance()->SetNative(nativelist[i].name, nativelist[i].func);
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_Register_addr), ::my_amx_Register, ::amx_Register_code);

    return error;
}

static uint32_t amx_FindPublic_addr;
static unsigned char amx_FindPublic_code[5];

static AMX *pGamemode = 0;
static std::string lastPublicName;

static int my_amx_FindPublic(AMX *amx, const char *name, int *index) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_FindPublic_addr), ::amx_FindPublic_code, 5);
    int error = amx_FindPublic(amx, name, index);

    if (amx == ::pGamemode && ::pGamemode != 0) {
        if (error != AMX_ERR_NONE) {
            // The requested public doesn't exist but we say it does
            // to let the server subsequently execute it.
            *index = -1337;
            error = AMX_ERR_NONE;
        }
        ::lastPublicName = name;
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_FindPublic_addr), ::my_amx_FindPublic, ::amx_FindPublic_code);

    return error;
}

static uint32_t amx_Exec_addr;
static unsigned char amx_Exec_code[5];

static int my_amx_Exec(AMX *amx, cell *retval, int index) {
    // Restore the original code so we can call the function
    memcpy(reinterpret_cast<void*>(::amx_Exec_addr), ::amx_Exec_code, 5);

    if (index == AMX_EXEC_MAIN) {
        // main() is being called => this is the gamemode
        ::pGamemode = amx;
    }

    int error = AMX_ERR_NONE;

    if (amx == ::pGamemode && ::pGamemode != 0) {
        if (index != AMX_EXEC_MAIN && index != AMX_EXEC_CONT) {
            samp::Wrapper::GetInstance()->CallPublic(::pGamemode, ::lastPublicName);
        }
        if (index != -1337) {
            // It's calling a fake public (see my_amx_FindPublic for details).
            error = amx_Exec(amx, retval, index);
        }
    } else {
        error = amx_Exec(amx, retval, index);
    }

    // Set the jump again to catch further calls
    SetJump(reinterpret_cast<void*>(::amx_Exec_addr), ::my_amx_Exec, ::amx_Exec_code);

    return error;
}

static void InitializeCallbacks() {
    using samp::Wrapper;
    using namespace samp::internal::callbacks;

    Wrapper::GetInstance()->SetPublicHandler("OnGameModeInit", OnGameModeInit);
    Wrapper::GetInstance()->SetPublicHandler("OnGameModeExit", OnGameModeExit);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerConnect", OnPlayerConnect);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerDisconnect", OnPlayerDisconnect);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerSpawn", OnPlayerSpawn);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerDeath", OnPlayerDeath);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleSpawn", OnVehicleSpawn);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleDeath", OnVehicleDeath);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerText", OnPlayerText);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerCommandText", OnPlayerCommandText);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerRequestClass", OnPlayerRequestClass);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerEnterVehicle", OnPlayerEnterVehicle);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerExitVehicle", OnPlayerExitVehicle);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerStateChange", OnPlayerStateChange);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerEnterCheckpoint", OnPlayerEnterCheckpoint);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerLeaveCheckpoint", OnPlayerLeaveCheckpoint);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerEnterRaceCheckpoint", OnPlayerEnterRaceCheckpoint);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerLeaveRaceCheckpoint", OnPlayerLeaveRaceCheckpoint);
    Wrapper::GetInstance()->SetPublicHandler("OnRconCommand", OnRconCommand);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerRequestSpawn", OnPlayerRequestSpawn);
    Wrapper::GetInstance()->SetPublicHandler("OnObjectMoved", OnObjectMoved);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerObjectMoved", OnPlayerObjectMoved);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerPickUpPickup", OnPlayerPickUpPickup);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleMod", OnVehicleMod);
    Wrapper::GetInstance()->SetPublicHandler("OnEnterExitModShop", OnEnterExitModShop);
    Wrapper::GetInstance()->SetPublicHandler("OnVehiclePaintjob", OnVehiclePaintjob);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleRespray", OnVehicleRespray);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleDamageStatusUpdate", OnVehicleDamageStatusUpdate);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerSelectedMenuRow", OnPlayerSelectedMenuRow);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerExitedMenu", OnPlayerExitedMenu);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerInteriorChange", OnPlayerInteriorChange);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerKeyStateChange", OnPlayerKeyStateChange);
    Wrapper::GetInstance()->SetPublicHandler("OnRconLoginAttempt", OnRconLoginAttempt);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerUpdate", OnPlayerUpdate);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerStreamIn", OnPlayerStreamIn);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerStreamOut", OnPlayerStreamOut);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleStreamIn", OnVehicleStreamIn);
    Wrapper::GetInstance()->SetPublicHandler("OnVehicleStreamOut", OnVehicleStreamOut);
    Wrapper::GetInstance()->SetPublicHandler("OnDialogResponse", OnDialogResponse);
    Wrapper::GetInstance()->SetPublicHandler("OnPlayerClickPlayer", OnPlayerClickPlayer);
}

namespace samp {

Wrapper::Wrapper() {}

Wrapper *Wrapper::GetInstance() {
    static Wrapper w;
    return &w;
}

void Wrapper::Initialize(void **ppPluginData) {
    // Very important things
    ::pAMXFunctions = ppPluginData[PLUGIN_DATA_AMX_EXPORTS];
    ::logprintf = (logprintf_t)ppPluginData[PLUGIN_DATA_LOGPRINTF];

    // Hook amx_Register
    ::amx_Register_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_Register]);
    SetJump(reinterpret_cast<void*>(::amx_Register_addr), ::my_amx_Register, ::amx_Register_code);

    // Hook amx_FindPublic
    ::amx_FindPublic_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_FindPublic]);
    SetJump(reinterpret_cast<void*>(::amx_FindPublic_addr), ::my_amx_FindPublic, ::amx_FindPublic_code);

    // Hook amx_Exec
    ::amx_Exec_addr = reinterpret_cast<uint32_t>((static_cast<void**>(pAMXFunctions))[PLUGIN_AMX_EXPORT_Exec]);
    SetJump(reinterpret_cast<void*>(::amx_Exec_addr), ::my_amx_Exec, ::amx_Exec_code);

    // Set handlers for all known SA:MP callbacks
    InitializeCallbacks();
}

void Wrapper::SetNative(const std::string &name, AMX_NATIVE native) {
    if (!name.empty()) {
        natives_[name] = native;
    }
}

AMX_NATIVE Wrapper::GetNative(const std::string &name) const {
    auto it = natives_.find(name);
    if (it != natives_.end()) {
        return it->second;
    }
    return 0;
}

void Wrapper::SetPublicHandler(const std::string &name, PublicHandler handler) {
    publicHandlers_[name] = handler;
}

cell Wrapper::CallPublic(AMX *amx, const std::string &name) const {
    auto it = publicHandlers_.find(name);
    if (it != publicHandlers_.end()) {
        return it->second(amx);
    }
    return 0;
}

} // namespace samp<|endoftext|>


<commit_before>/******************************************************************************//**
 * @copyright (c) RDO-Team, 2011
 * @file      sequence.cpp
 * @authors    ,
 * @date      13.03.2011
 * @brief
 * @indent    4T
 *********************************************************************************/

// **************************************************************************** PCH
#include "rdo_lib/rdo_runtime/pch.h"
// *********************************************************************** INCLUDES
// *********************************************************************** SYNOPSIS
#include "rdo_lib/rdo_runtime/calc/sequence.h"
#include "rdo_lib/rdo_runtime/rdo_runtime.h"
// ********************************************************************************

OPEN_RDO_RUNTIME_NAMESPACE

// ********************************************************************************
// ******************** RDOCalcSeqInit
// ********************************************************************************
REF(RDOValue) RDOCalcSeqInit::doCalc(CREF(LPRDORuntime) pRuntime)
{
	m_gen->setSeed(m_base);
	return m_value;
}

RDOCalcSeqInit::~RDOCalcSeqInit()
{
//	delete m_gen;
}

// ********************************************************************************
// ******************** RDOCalcSeqNextUniform
// ********************************************************************************
RDOValue RDOCalcSeqNextUniform::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	if (pRuntime->getFuncArgument(0).getDouble() > pRuntime->getFuncArgument(1).getDouble())
	{
		pRuntime->error(rdo::format(_T("   uniform    :  ,  "), this));
	}
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble(), pRuntime->getFuncArgument(1).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextNormal
// ********************************************************************************
RDOValue RDOCalcSeqNextNormal::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble(), pRuntime->getFuncArgument(1).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextExponential
// ********************************************************************************
RDOValue RDOCalcSeqNextExponential::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextTriangular
// ********************************************************************************
RDOValue RDOCalcSeqNextTriangular::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	if (pRuntime->getFuncArgument(0).getDouble() > pRuntime->getFuncArgument(1).getDouble())
	{
		pRuntime->error(rdo::format(_T("       :  ,    ,  "), this));
	}

	if (pRuntime->getFuncArgument(1).getDouble() > pRuntime->getFuncArgument(2).getDouble())
	{
		pRuntime->error(rdo::format(_T("       :  ,    ,  "), this));
	}
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble(), pRuntime->getFuncArgument(1).getDouble(), pRuntime->getFuncArgument(2).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextByHist
// ********************************************************************************
RDOValue RDOCalcSeqNextByHist::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next();
}

CLOSE_RDO_RUNTIME_NAMESPACE
<commit_msg> - удален повторный расчёт параметров  - исправлено формирование runtime-error<commit_after>/******************************************************************************//**
 * @copyright (c) RDO-Team, 2011
 * @file      sequence.cpp
 * @authors    ,
 * @date      13.03.2011
 * @brief
 * @indent    4T
 *********************************************************************************/

// **************************************************************************** PCH
#include "rdo_lib/rdo_runtime/pch.h"
// *********************************************************************** INCLUDES
// *********************************************************************** SYNOPSIS
#include "rdo_lib/rdo_runtime/calc/sequence.h"
#include "rdo_lib/rdo_runtime/rdo_runtime.h"
// ********************************************************************************

OPEN_RDO_RUNTIME_NAMESPACE

// ********************************************************************************
// ******************** RDOCalcSeqInit
// ********************************************************************************
REF(RDOValue) RDOCalcSeqInit::doCalc(CREF(LPRDORuntime) pRuntime)
{
	m_gen->setSeed(m_base);
	return m_value;
}

RDOCalcSeqInit::~RDOCalcSeqInit()
{
//	delete m_gen;
}

// ********************************************************************************
// ******************** RDOCalcSeqNextUniform
// ********************************************************************************
RDOValue RDOCalcSeqNextUniform::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	RDOValue from = pRuntime->getFuncArgument(0);
	RDOValue to   = pRuntime->getFuncArgument(1);
	if (from > to)
	{
		pRuntime->error(
			rdo::format(_T("   uniform         ,  : %s..%s")
				, from.getAsString().c_str()
				, to  .getAsString().c_str())
			, this
		);
	}
	return m_gen->next(from.getDouble(), to.getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextNormal
// ********************************************************************************
RDOValue RDOCalcSeqNextNormal::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble(), pRuntime->getFuncArgument(1).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextExponential
// ********************************************************************************
RDOValue RDOCalcSeqNextExponential::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextTriangular
// ********************************************************************************
RDOValue RDOCalcSeqNextTriangular::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	if (pRuntime->getFuncArgument(0).getDouble() > pRuntime->getFuncArgument(1).getDouble())
	{
		pRuntime->error(rdo::format(_T("       :  ,    ,  "), this));
	}

	if (pRuntime->getFuncArgument(1).getDouble() > pRuntime->getFuncArgument(2).getDouble())
	{
		pRuntime->error(rdo::format(_T("       :  ,    ,  "), this));
	}
	return m_gen->next(pRuntime->getFuncArgument(0).getDouble(), pRuntime->getFuncArgument(1).getDouble(), pRuntime->getFuncArgument(2).getDouble());
}

// ********************************************************************************
// ******************** RDOCalcSeqNextByHist
// ********************************************************************************
RDOValue RDOCalcSeqNextByHist::getNextValue(CREF(LPRDORuntime) pRuntime)
{
	return m_gen->next();
}

CLOSE_RDO_RUNTIME_NAMESPACE
<|endoftext|>