<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pathsettings.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 00:33:03 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_
#define __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

#ifndef __FRAMEWORK_THREADHELP_THREADHELPBASE_HXX_
#include <threadhelp/threadhelpbase.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_GENERIC_HXX_
#include <macros/generic.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XINTERFACE_HXX_
#include <macros/xinterface.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XTYPEPROVIDER_HXX_
#include <macros/xtypeprovider.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XSERVICEINFO_HXX_
#include <macros/xserviceinfo.hxx>
#endif

#ifndef __FRAMEWORK_STDTYPES_H_
#include <stdtypes.h>
#endif

#ifndef __FRAMEWORK_PROPERTIES_H_
#include <properties.h>
#endif

#ifndef __FRAMEWORK_STDTYPES_H_
#include <stdtypes.h>
#endif

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________

#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XTYPEPROVIDER_HPP_
#include <com/sun/star/lang/XTypeProvider.hpp>
#endif

#ifndef _COM_SUN_STAR_UTIL_XSTRINGSUBSTITUTION_HPP_
#include <com/sun/star/util/XStringSubstitution.hpp>
#endif

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

#ifndef _CPPUHELPER_PROPSHLP_HXX
#include <cppuhelper/propshlp.hxx>
#endif

#ifndef _CPPUHELPER_INTERFACECONTAINER_HXX_
#include <cppuhelper/interfacecontainer.hxx>
#endif

#ifndef _CPPUHELPER_WEAK_HXX_
#include <cppuhelper/weak.hxx>
#endif

#ifndef _UTL_CONFIGITEM_HXX_
#include <unotools/configitem.hxx>
#endif

namespace framework
{

struct PathInfo
{
    ::rtl::OUString sPath       ;
    ::rtl::OUString sValue      ;
    sal_Bool        bReadOnly   ;
    sal_Bool        bMultiPath  ;
};

/** implements the data container for the path settings service.
    It can be used to read/write the right config items; (re-)substitute
    her path values; check her readonly states and to provide an easy and fast
    access by using an ID instead of a name. */
class PathSettingsCfg : protected ThreadHelpBase
                      , public    utl::ConfigItem
{
    // ______________________________________
    // const

    protected:

        enum EPropHandle
        {
            E_ADDIN         = PATHSETTINGS_PROPHANDLE_ADDIN         ,
            E_AUTOCORRECT   = PATHSETTINGS_PROPHANDLE_AUTOCORRECT   ,
            E_AUTOTEXT      = PATHSETTINGS_PROPHANDLE_AUTOTEXT      ,
            E_BACKUP        = PATHSETTINGS_PROPHANDLE_BACKUP        ,
            E_BASIC         = PATHSETTINGS_PROPHANDLE_BASIC         ,
            E_BITMAP        = PATHSETTINGS_PROPHANDLE_BITMAP        ,
            E_CONFIG        = PATHSETTINGS_PROPHANDLE_CONFIG        ,
            E_DICTIONARY    = PATHSETTINGS_PROPHANDLE_DICTIONARY    ,
            E_FAVORITE      = PATHSETTINGS_PROPHANDLE_FAVORITE      ,
            E_FILTER        = PATHSETTINGS_PROPHANDLE_FILTER        ,
            E_GALLERY       = PATHSETTINGS_PROPHANDLE_GALLERY       ,
            E_GRAPHIC       = PATHSETTINGS_PROPHANDLE_GRAPHIC       ,
            E_HELP          = PATHSETTINGS_PROPHANDLE_HELP          ,
            E_LINGUISTIC    = PATHSETTINGS_PROPHANDLE_LINGUISTIC    ,
            E_MODULE        = PATHSETTINGS_PROPHANDLE_MODULE        ,
            E_PALETTE       = PATHSETTINGS_PROPHANDLE_PALETTE       ,
            E_PLUGIN        = PATHSETTINGS_PROPHANDLE_PLUGIN        ,
            E_STORAGE       = PATHSETTINGS_PROPHANDLE_STORAGE       ,
            E_TEMP          = PATHSETTINGS_PROPHANDLE_TEMP          ,
            E_TEMPLATE      = PATHSETTINGS_PROPHANDLE_TEMPLATE      ,
            E_UICONFIG      = PATHSETTINGS_PROPHANDLE_UICONFIG      ,
            E_USERCONFIG    = PATHSETTINGS_PROPHANDLE_USERCONFIG    ,
            E_USERDICTIONARY= PATHSETTINGS_PROPHANDLE_USERDICTIONARY,
            E_WORK          = PATHSETTINGS_PROPHANDLE_WORK
        };

        static const ::rtl::OUString PropNames[];
        static const css::beans::Property Properties[];

    // ______________________________________
    // member

    private:

        /** list of all supported path variables.
            Correspond to the defined lists of names/handles of static class PathSettingsPropHelp.
            see file "properties.h" for further informations. */
        PathInfo m_lPathes[PATHSETTINGS_PROPCOUNT];

        /** is used to map path names faster to her corresponding ID. */
        NameToHandleHash m_lIDMap;

        /** helper needed to (re-)substitute all internal save path values. */
        css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;

    // ______________________________________
    // interface

    public:

        /** initialize this instance and read all needed config values immediatly.
            The given uno manager is used to create own needed helper services. */
        PathSettingsCfg( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR );

        /** destroy this instance and free all used memory.
            Further it write all changed items back to the configguration, if it was not already done. */
        virtual ~PathSettingsCfg();

        /** is called from the ConfigManager before application ends or from the
            PropertyChangeListener if the sub tree broadcasts changes.
            We have to update the specified config items and actualize our cache. */
        virtual void Notify( const com::sun::star::uno::Sequence< rtl::OUString >& lPropertyNames );

        /** returns the path value for the given path handle.
            It returns an empty string for invalid calls. */
        ::rtl::OUString getPath( EPropHandle nID ) const;

        /** set the new path value for the given path handle.
            Invalid calls will be ignored and do nothing. */
        void setPath(       EPropHandle      nID    ,
                      const ::rtl::OUString& sValue );

        /** returns the readonly state for theiven path handle.
            It returns false as default and answer for invalid calls! */
        sal_Bool isReadOnly( EPropHandle nID ) const;

        /** some of our pathes are multi pathes.
            They contain a list of values instead of one string only.
            This method returns true, if the given handle match to a
            multi path (they are well known and fix!); false if not. */
        sal_Bool isMultiPath( EPropHandle nID ) const;

        /** returns a descriptor for all path properties.
            It contains against some fix informations some dynamic ones too (e.g. readonly states).
            But note: This method can return valid results only, if all neccessary data was readed
            before from the configuration! */
        const css::uno::Sequence< css::beans::Property > getPropertyDescriptor() const;

        /** map given path name to it's corresponding ID.
            But the out parameter is set to a valid enum value only in case
            mapping was successfully. If method returns false - rID will be undefined!
            Using of such "invalid" ID can produce crashes ... */
        sal_Bool mapName2Handle( const ::rtl::OUString& sName ,
                                       EPropHandle&     rID   ) const;

        /** it checks, if the given path value seams to be a valid URL or system path.
            To do it right it must be called with an information about the path type (single/multi path)! */
        sal_Bool isValidValue( const ::rtl::OUString& sValue     ,
                                     sal_Bool         bMultiPath ) const;

        /** it prepares the given path for saving.
            Doing so the path value must be checked and substituted.
            Further this method must know the path type (single/multi path) */
        sal_Bool checkAndSubstituteValue( ::rtl::OUString& sValue     ,
                                          sal_Bool         bMultiPath ) const;

    // ______________________________________
    // helper

    private:

        /** read the given list of path entries from the configuration and update
            all internal structures. And additional parameter "bSearchID" can be used
            to optimize this method and disable mapping of path names to her corresponding ID.
            If it's set to "FALSE" ... the method require that the array position of a path name
            entry inside "lNames" is equal to it's property handle! */
        void impl_read( const css::uno::Sequence< ::rtl::OUString >& lNames    ,
                              sal_Bool                               bSearchID );
};

class PathSettings : public  css::lang::XTypeProvider             ,
                     public  css::lang::XServiceInfo              ,
                     // base classes
                     // Order is neccessary for right initialization!
                     private PathSettingsCfg                      ,
                     public  ::cppu::OBroadcastHelper             ,
                     public  ::cppu::OPropertySetHelper           , // => XPropertySet / XFastPropertySet / XMultiPropertySet
                     public  ::cppu::OWeakObject                    // => XWeak, XInterface
{
    // ___________________________________________
    // member

    private:

        /** reference to factory, which has create this instance. */
        css::uno::Reference< css::lang::XMultiServiceFactory > m_xSMGR;

    // ___________________________________________
    // interface

    public:

        /** initialize a new instance of this class.
            Attention: It's neccessary for right function of this class, that the order of base
            classes is the right one. Because we transfer information from one base to another
            during this ctor runs! */
        PathSettings( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR );

        /** free all used ressources ... if it was not already done. */
        virtual ~PathSettings();

        /** declaration of XInterface, XTypeProvider, XServiceInfo */
        DECLARE_XINTERFACE
        DECLARE_XTYPEPROVIDER
        DECLARE_XSERVICEINFO

    // ___________________________________________
    // helper

    private:

        //  OPropertySetHelper
        virtual sal_Bool                                            SAL_CALL convertFastPropertyValue        (       css::uno::Any&  aConvertedValue ,
                                                                                css::uno::Any&  aOldValue       ,
                                                                                sal_Int32       nHandle         ,
                                                                                const css::uno::Any&  aValue          ) throw(css::lang::IllegalArgumentException);
        virtual void                                                SAL_CALL setFastPropertyValue_NoBroadcast(       sal_Int32       nHandle         ,
                                                                                const css::uno::Any&  aValue          ) throw(css::uno::Exception);
        virtual void                                                SAL_CALL getFastPropertyValue            (       css::uno::Any&  aValue          ,
                                                                                sal_Int32       nHandle         ) const;
        virtual ::cppu::IPropertyArrayHelper&                       SAL_CALL getInfoHelper                   (                                       );
        virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo              (                                       ) throw(::com::sun::star::uno::RuntimeException);

};

} // namespace framework

#endif // __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_
<commit_msg>INTEGRATION: CWS warnings01 (1.3.32); FILE MERGED 2005/11/16 15:20:09 pl 1.3.32.2: #i55991# removed warnings 2005/10/28 14:48:17 cd 1.3.32.1: #i55991# Warning free code changes for gcc<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pathsettings.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 11:00:54 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_
#define __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

#ifndef __FRAMEWORK_THREADHELP_THREADHELPBASE_HXX_
#include <threadhelp/threadhelpbase.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_GENERIC_HXX_
#include <macros/generic.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XINTERFACE_HXX_
#include <macros/xinterface.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XTYPEPROVIDER_HXX_
#include <macros/xtypeprovider.hxx>
#endif

#ifndef __FRAMEWORK_MACROS_XSERVICEINFO_HXX_
#include <macros/xserviceinfo.hxx>
#endif

#ifndef __FRAMEWORK_STDTYPES_H_
#include <stdtypes.h>
#endif

#ifndef __FRAMEWORK_PROPERTIES_H_
#include <properties.h>
#endif

#ifndef __FRAMEWORK_STDTYPES_H_
#include <stdtypes.h>
#endif

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________

#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif

#ifndef _COM_SUN_STAR_LANG_XTYPEPROVIDER_HPP_
#include <com/sun/star/lang/XTypeProvider.hpp>
#endif

#ifndef _COM_SUN_STAR_UTIL_XSTRINGSUBSTITUTION_HPP_
#include <com/sun/star/util/XStringSubstitution.hpp>
#endif

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

#ifndef _CPPUHELPER_PROPSHLP_HXX
#include <cppuhelper/propshlp.hxx>
#endif

#ifndef _CPPUHELPER_INTERFACECONTAINER_HXX_
#include <cppuhelper/interfacecontainer.hxx>
#endif

#ifndef _CPPUHELPER_WEAK_HXX_
#include <cppuhelper/weak.hxx>
#endif

#ifndef _UTL_CONFIGITEM_HXX_
#include <unotools/configitem.hxx>
#endif

namespace framework
{

struct PathInfo
{
    ::rtl::OUString sPath       ;
    ::rtl::OUString sValue      ;
    sal_Bool        bReadOnly   ;
    sal_Bool        bMultiPath  ;
};

/** implements the data container for the path settings service.
    It can be used to read/write the right config items; (re-)substitute
    her path values; check her readonly states and to provide an easy and fast
    access by using an ID instead of a name. */
class PathSettingsCfg : protected ThreadHelpBase
                      , public    utl::ConfigItem
{
    // ______________________________________
    // const

    protected:

        enum EPropHandle
        {
            E_ADDIN         = PATHSETTINGS_PROPHANDLE_ADDIN         ,
            E_AUTOCORRECT   = PATHSETTINGS_PROPHANDLE_AUTOCORRECT   ,
            E_AUTOTEXT      = PATHSETTINGS_PROPHANDLE_AUTOTEXT      ,
            E_BACKUP        = PATHSETTINGS_PROPHANDLE_BACKUP        ,
            E_BASIC         = PATHSETTINGS_PROPHANDLE_BASIC         ,
            E_BITMAP        = PATHSETTINGS_PROPHANDLE_BITMAP        ,
            E_CONFIG        = PATHSETTINGS_PROPHANDLE_CONFIG        ,
            E_DICTIONARY    = PATHSETTINGS_PROPHANDLE_DICTIONARY    ,
            E_FAVORITE      = PATHSETTINGS_PROPHANDLE_FAVORITE      ,
            E_FILTER        = PATHSETTINGS_PROPHANDLE_FILTER        ,
            E_GALLERY       = PATHSETTINGS_PROPHANDLE_GALLERY       ,
            E_GRAPHIC       = PATHSETTINGS_PROPHANDLE_GRAPHIC       ,
            E_HELP          = PATHSETTINGS_PROPHANDLE_HELP          ,
            E_LINGUISTIC    = PATHSETTINGS_PROPHANDLE_LINGUISTIC    ,
            E_MODULE        = PATHSETTINGS_PROPHANDLE_MODULE        ,
            E_PALETTE       = PATHSETTINGS_PROPHANDLE_PALETTE       ,
            E_PLUGIN        = PATHSETTINGS_PROPHANDLE_PLUGIN        ,
            E_STORAGE       = PATHSETTINGS_PROPHANDLE_STORAGE       ,
            E_TEMP          = PATHSETTINGS_PROPHANDLE_TEMP          ,
            E_TEMPLATE      = PATHSETTINGS_PROPHANDLE_TEMPLATE      ,
            E_UICONFIG      = PATHSETTINGS_PROPHANDLE_UICONFIG      ,
            E_USERCONFIG    = PATHSETTINGS_PROPHANDLE_USERCONFIG    ,
            E_USERDICTIONARY= PATHSETTINGS_PROPHANDLE_USERDICTIONARY,
            E_WORK          = PATHSETTINGS_PROPHANDLE_WORK
        };

        static const ::rtl::OUString PropNames[];
        static const css::beans::Property Properties[];

    // ______________________________________
    // member

    private:

        /** list of all supported path variables.
            Correspond to the defined lists of names/handles of static class PathSettingsPropHelp.
            see file "properties.h" for further informations. */
        PathInfo m_lPathes[PATHSETTINGS_PROPCOUNT];

        /** is used to map path names faster to her corresponding ID. */
        NameToHandleHash m_lIDMap;

        /** helper needed to (re-)substitute all internal save path values. */
        css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;

    // ______________________________________
    // interface

    public:

        /** initialize this instance and read all needed config values immediatly.
            The given uno manager is used to create own needed helper services. */
        PathSettingsCfg( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR );

        /** destroy this instance and free all used memory.
            Further it write all changed items back to the configguration, if it was not already done. */
        virtual ~PathSettingsCfg();

        /** is called from the ConfigManager before application ends or from the
            PropertyChangeListener if the sub tree broadcasts changes.
            We have to update the specified config items and actualize our cache. */
        virtual void Notify( const com::sun::star::uno::Sequence< rtl::OUString >& lPropertyNames );

        /** returns the path value for the given path handle.
            It returns an empty string for invalid calls. */
        ::rtl::OUString getPath( EPropHandle nID ) const;

        /** set the new path value for the given path handle.
            Invalid calls will be ignored and do nothing. */
        void setPath(       EPropHandle      nID    ,
                      const ::rtl::OUString& sValue );

        /** returns the readonly state for theiven path handle.
            It returns false as default and answer for invalid calls! */
        sal_Bool isReadOnly( EPropHandle nID ) const;

        /** some of our pathes are multi pathes.
            They contain a list of values instead of one string only.
            This method returns true, if the given handle match to a
            multi path (they are well known and fix!); false if not. */
        sal_Bool isMultiPath( EPropHandle nID ) const;

        /** returns a descriptor for all path properties.
            It contains against some fix informations some dynamic ones too (e.g. readonly states).
            But note: This method can return valid results only, if all neccessary data was readed
            before from the configuration! */
        const css::uno::Sequence< css::beans::Property > getPropertyDescriptor() const;

        /** map given path name to it's corresponding ID.
            But the out parameter is set to a valid enum value only in case
            mapping was successfully. If method returns false - rID will be undefined!
            Using of such "invalid" ID can produce crashes ... */
        sal_Bool mapName2Handle( const ::rtl::OUString& sName ,
                                       EPropHandle&     rID   ) const;

        /** it checks, if the given path value seams to be a valid URL or system path.
            To do it right it must be called with an information about the path type (single/multi path)! */
        sal_Bool isValidValue( const ::rtl::OUString& sValue     ,
                                     sal_Bool         bMultiPath ) const;

        /** it prepares the given path for saving.
            Doing so the path value must be checked and substituted.
            Further this method must know the path type (single/multi path) */
        sal_Bool checkAndSubstituteValue( ::rtl::OUString& sValue     ,
                                          sal_Bool         bMultiPath ) const;

    // ______________________________________
    // helper

    private:

        /** read the given list of path entries from the configuration and update
            all internal structures. And additional parameter "bSearchID" can be used
            to optimize this method and disable mapping of path names to her corresponding ID.
            If it's set to "FALSE" ... the method require that the array position of a path name
            entry inside "lNames" is equal to it's property handle! */
        void impl_read( const css::uno::Sequence< ::rtl::OUString >& lNames    ,
                              sal_Bool                               bSearchID );
};

class PathSettings : public  css::lang::XTypeProvider             ,
                     public  css::lang::XServiceInfo              ,
                     // base classes
                     // Order is neccessary for right initialization!
                     private PathSettingsCfg                      ,
                     public  ::cppu::OBroadcastHelper             ,
                     public  ::cppu::OPropertySetHelper           , // => XPropertySet / XFastPropertySet / XMultiPropertySet
                     public  ::cppu::OWeakObject                    // => XWeak, XInterface
{
    // ___________________________________________
    // member

    private:

        /** reference to factory, which has create this instance. */
        css::uno::Reference< css::lang::XMultiServiceFactory > m_xSMGR;

    // ___________________________________________
    // interface

    public:

        /** initialize a new instance of this class.
            Attention: It's neccessary for right function of this class, that the order of base
            classes is the right one. Because we transfer information from one base to another
            during this ctor runs! */
        PathSettings( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR );

        /** free all used ressources ... if it was not already done. */
        virtual ~PathSettings();

        /** declaration of XInterface, XTypeProvider, XServiceInfo */
        FWK_DECLARE_XINTERFACE
        FWK_DECLARE_XTYPEPROVIDER
        DECLARE_XSERVICEINFO

    // ___________________________________________
    // helper

    private:

        //  OPropertySetHelper
        virtual sal_Bool                                            SAL_CALL convertFastPropertyValue        (       css::uno::Any&  aConvertedValue ,
                                                                                css::uno::Any&  aOldValue       ,
                                                                                sal_Int32       nHandle         ,
                                                                                const css::uno::Any&  aValue          ) throw(css::lang::IllegalArgumentException);
        virtual void                                                SAL_CALL setFastPropertyValue_NoBroadcast(       sal_Int32       nHandle         ,
                                                                                const css::uno::Any&  aValue          ) throw(css::uno::Exception);
        using cppu::OPropertySetHelper::getFastPropertyValue;
        virtual void                                                SAL_CALL getFastPropertyValue            (       css::uno::Any&  aValue          ,
                                                                                sal_Int32       nHandle         ) const;
        virtual ::cppu::IPropertyArrayHelper&                       SAL_CALL getInfoHelper                   (                                       );
        virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo              (                                       ) throw(::com::sun::star::uno::RuntimeException);

};

} // namespace framework

#endif // __FRAMEWORK_SERVICES_PATHSETTINGS_HXX_
<|endoftext|>


<commit_before>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        peersQueue.push(std::move(entry));
    } else {
        handlePeer(peer);
    }
    int fd = peer->fd();
    {
        Guard guard(toWriteLock);
        toWrite.emplace(fd, std::deque<WriteEntry>{});
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it->first);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            {
                Guard guard(toWriteLock);
                auto it = toWrite.find(fd);
                if (it == std::end(toWrite)) {
                    throw std::runtime_error("Assertion Error: could not find write data");
                }
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            // Try to drain the queue
            asyncWriteImpl(fd);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer] = {0};

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

#ifdef PISTACHE_USE_SSL
        if (peer->ssl() != NULL) {
            bytes = SSL_read((SSL *)peer->ssl(), buffer + totalBytes,
                Const::MaxBuffer - totalBytes);
        } else {
#endif /* PISTACHE_USE_SSL */
            bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
#ifdef PISTACHE_USE_SSL
        }
#endif /* PISTACHE_USE_SSL */

        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            handler_->onInput(buffer, bytes, peer);
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

#ifdef PISTACHE_USE_SSL
    if (peer->ssl() != NULL) {
        SSL_free((SSL *)peer->ssl());
        peer->associateSSL(NULL);
    }
#endif /* PISTACHE_USE_SSL */

    peers.erase(it->first);

    {
        // Clean up buffers
        Guard guard(toWriteLock);
        auto & wq = toWrite[fd];
        while (wq.size() > 0) {
            wq.pop_front();
        }
        toWrite.erase(fd);
    }

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd)
{
    bool stop = false;
    while (!stop) {
        Guard guard(toWriteLock);

        auto it = toWrite.find(fd);

        // cleanup will have been handled by handlePeerDisconnection
        if (it == std::end(toWrite)) { return; }
        auto & wq = it->second;
        if (wq.size() == 0) {
            break;
        }

        auto & entry = wq.front();
        int flags    = entry.flags;
        BufferHolder &buffer = entry.buffer;
        Async::Deferred<ssize_t> deferred = std::move(entry.deferred);

        auto cleanUp = [&]() {
            wq.pop_front();
            if (wq.size() == 0) {
                toWrite.erase(fd);
                reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);
                stop = true;
            }
        };

        size_t totalWritten = buffer.offset();
        for (;;) {
            ssize_t bytesWritten = 0;
            auto len = buffer.size() - totalWritten;

            if (buffer.isRaw()) {
                auto raw = buffer.raw();
                auto ptr = raw.data().c_str() + totalWritten;

#ifdef PISTACHE_USE_SSL
                auto it = peers.find(fd);

                if (it == std::end(peers))
                    throw std::runtime_error("No peer found for fd: " + std::to_string(fd));

                if (it->second->ssl() != NULL) {
                    bytesWritten = SSL_write((SSL *)it->second->ssl(), ptr, len);
                } else {
#endif /* PISTACHE_USE_SSL */
                    bytesWritten = ::send(fd, ptr, len, flags);
#ifdef PISTACHE_USE_SSL
                }
#endif /* PISTACHE_USE_SSL */
            } else {
                auto file = buffer.fd();
                off_t offset = totalWritten;
                bytesWritten = ::sendfile(fd, file, &offset, len);
            }
            if (bytesWritten < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {

                    auto bufferHolder = buffer.detach(totalWritten);

                    // pop_front kills buffer - so we cannot continue loop or use buffer after this point
                    wq.pop_front();
                    wq.push_front(WriteEntry(std::move(deferred), bufferHolder, flags));
                    reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
                }
                else {
                    cleanUp();
                    deferred.reject(Pistache::Error::system("Could not write data"));
                }
                break;
            }
            else {
                totalWritten += bytesWritten;
                if (totalWritten >= buffer.size()) {
                    if (buffer.isFile()) {
                        // done with the file buffer, nothing else knows whether to
                        // close it with the way the code is written.
                        ::close(buffer.fd());
                    }

                    cleanUp();

                    // Cast to match the type of defered template
                    // to avoid a BadType exception
                    deferred.resolve(static_cast<ssize_t>(totalWritten));
                    break;
                }
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        timersQueue.push(std::move(entry));
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto write = writesQueue.popSafe();
        if (!write) break;

        auto fd = write->peerFd;
        if (!isPeerFd(fd)) continue;

        {
            Guard guard(toWriteLock);
            toWrite[fd].push_back(std::move(*write));
        }

        reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto timer = timersQueue.popSafe();
        if (!timer) break;

        armTimerMsImpl(std::move(*timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto data = peersQueue.popSafe();
        if (!data) break;

        handlePeer(data->peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<commit_msg>Add error handling in cases of writing to an invalid file descriptor<commit_after>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        peersQueue.push(std::move(entry));
    } else {
        handlePeer(peer);
    }
    int fd = peer->fd();
    {
        Guard guard(toWriteLock);
        toWrite.emplace(fd, std::deque<WriteEntry>{});
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it->first);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            {
                Guard guard(toWriteLock);
                auto it = toWrite.find(fd);
                if (it == std::end(toWrite)) {
                    throw std::runtime_error("Assertion Error: could not find write data");
                }
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            // Try to drain the queue
            asyncWriteImpl(fd);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer] = {0};

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

#ifdef PISTACHE_USE_SSL
        if (peer->ssl() != NULL) {
            bytes = SSL_read((SSL *)peer->ssl(), buffer + totalBytes,
                Const::MaxBuffer - totalBytes);
        } else {
#endif /* PISTACHE_USE_SSL */
            bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
#ifdef PISTACHE_USE_SSL
        }
#endif /* PISTACHE_USE_SSL */

        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            handler_->onInput(buffer, bytes, peer);
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

#ifdef PISTACHE_USE_SSL
    if (peer->ssl() != NULL) {
        SSL_free((SSL *)peer->ssl());
        peer->associateSSL(NULL);
    }
#endif /* PISTACHE_USE_SSL */

    peers.erase(it->first);

    {
        // Clean up buffers
        Guard guard(toWriteLock);
        auto & wq = toWrite[fd];
        while (wq.size() > 0) {
            wq.pop_front();
        }
        toWrite.erase(fd);
    }

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd)
{
    bool stop = false;
    while (!stop) {
        Guard guard(toWriteLock);

        auto it = toWrite.find(fd);

        // cleanup will have been handled by handlePeerDisconnection
        if (it == std::end(toWrite)) { return; }
        auto & wq = it->second;
        if (wq.size() == 0) {
            break;
        }

        auto & entry = wq.front();
        int flags    = entry.flags;
        BufferHolder &buffer = entry.buffer;
        Async::Deferred<ssize_t> deferred = std::move(entry.deferred);

        auto cleanUp = [&]() {
            wq.pop_front();
            if (wq.size() == 0) {
                toWrite.erase(fd);
                reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);
                stop = true;
            }
        };

        size_t totalWritten = buffer.offset();
        for (;;) {
            ssize_t bytesWritten = 0;
            auto len = buffer.size() - totalWritten;

            if (buffer.isRaw()) {
                auto raw = buffer.raw();
                auto ptr = raw.data().c_str() + totalWritten;

#ifdef PISTACHE_USE_SSL
                auto it = peers.find(fd);

                if (it == std::end(peers))
                    throw std::runtime_error("No peer found for fd: " + std::to_string(fd));

                if (it->second->ssl() != NULL) {
                    bytesWritten = SSL_write((SSL *)it->second->ssl(), ptr, len);
                } else {
#endif /* PISTACHE_USE_SSL */
                    bytesWritten = ::send(fd, ptr, len, flags);
#ifdef PISTACHE_USE_SSL
                }
#endif /* PISTACHE_USE_SSL */
            } else {
                auto file = buffer.fd();
                off_t offset = totalWritten;
                bytesWritten = ::sendfile(fd, file, &offset, len);
            }
            if (bytesWritten < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {

                    auto bufferHolder = buffer.detach(totalWritten);

                    // pop_front kills buffer - so we cannot continue loop or use buffer after this point
                    wq.pop_front();
                    wq.push_front(WriteEntry(std::move(deferred), bufferHolder, flags));
                    reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
                }
                else if (errno == EBADF) {
                    wq.pop_front();
                    toWrite.erase(fd);
                    stop = true;
                }
                else {
                    cleanUp();
                    deferred.reject(Pistache::Error::system("Could not write data"));
                }
                break;
            }
            else {
                totalWritten += bytesWritten;
                if (totalWritten >= buffer.size()) {
                    if (buffer.isFile()) {
                        // done with the file buffer, nothing else knows whether to
                        // close it with the way the code is written.
                        ::close(buffer.fd());
                    }

                    cleanUp();

                    // Cast to match the type of defered template
                    // to avoid a BadType exception
                    deferred.resolve(static_cast<ssize_t>(totalWritten));
                    break;
                }
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        timersQueue.push(std::move(entry));
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto write = writesQueue.popSafe();
        if (!write) break;

        auto fd = write->peerFd;
        if (!isPeerFd(fd)) continue;

        {
            Guard guard(toWriteLock);
            toWrite[fd].push_back(std::move(*write));
        }

        reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto timer = timersQueue.popSafe();
        if (!timer) break;

        armTimerMsImpl(std::move(*timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto data = peersQueue.popSafe();
        if (!data) break;

        handlePeer(data->peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<|endoftext|>


<commit_before>/*
Copyright (C) 2011 by Ivan Safrin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "PolyPhysicsScene.h"
#include "btBulletCollisionCommon.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "BulletDynamics/Character/btKinematicCharacterController.h"

#include "PolyCollisionScene.h"
#include "PolyCoreServices.h"
#include "PolyVector3.h"
#include "PolyPhysicsSceneEntity.h"
#include "PolyCore.h"

using namespace Polycode;

PhysicsSceneEvent::PhysicsSceneEvent() : Event () {
	eventType = "PhysicsSceneEvent";
}

PhysicsScene::PhysicsScene(int maxSubSteps, bool virtualScene) : CollisionScene(virtualScene, true), physicsWorld(NULL), solver(NULL), broadphase(NULL) {
	this->maxSubSteps = maxSubSteps;
	initPhysicsScene();
}

PhysicsScene::~PhysicsScene() {
	// Physics children and physicsWorld will be deleted by ~CollisionScene.
	delete solver;
	delete broadphase;
}

void worldTickCallback(btDynamicsWorld *world, btScalar timeStep) {
	PhysicsScene *physicsScene = (PhysicsScene*)world->getWorldUserInfo();
	physicsScene->processWorldCollisions();
}

void PhysicsScene::initPhysicsScene() {
	collisionConfiguration = new btDefaultCollisionConfiguration();

	dispatcher = new btCollisionDispatcher(collisionConfiguration);

	solver = new btSequentialImpulseConstraintSolver();

	btVector3 worldMin(-100,-100,-100);
	btVector3 worldMax(100,100,100);
	axisSweep = new btAxisSweep3(worldMin,worldMax);

	broadphase = new btDbvtBroadphase();

	physicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);

//	physicsWorld->getSolverInfo().m_solverMode |= SOLVER_RANDMIZE_ORDER;
	physicsWorld->setGravity(btVector3(0,-10,0));

	axisSweep->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());

	world = physicsWorld;

	physicsWorld->setInternalTickCallback(worldTickCallback, this);
}

void PhysicsScene::setGravity(Vector3 gravity) {
	physicsWorld->setGravity(btVector3(gravity.x, gravity.y, gravity.z));
}

PhysicsSceneEntity *PhysicsScene::getPhysicsEntityByCollisionObject(btCollisionObject *object) {
	for(int i=0; i < physicsChildren.size(); i++) {
		PhysicsSceneEntity *entity = physicsChildren[i];
		if(entity->rigidBody == object) {
			return entity;
		}
	}
	return  NULL;
}

void PhysicsScene::processWorldCollisions() {

	int numManifolds = physicsWorld->getDispatcher()->getNumManifolds();
	for (int i=0;i<numManifolds;i++)
	{
		btPersistentManifold* contactManifold =  world->getDispatcher()->getManifoldByIndexInternal(i);
		btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
		btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());

		int numContacts = contactManifold->getNumContacts();
		for (int j=0;j<numContacts;j++)
		{
			btManifoldPoint& pt = contactManifold->getContactPoint(j);
			if (pt.getDistance()<0.f)
			{
				const btVector3& ptA = pt.getPositionWorldOnA();
				const btVector3& ptB = pt.getPositionWorldOnB();
				const btVector3& normalOnB = pt.m_normalWorldOnB;
				const btScalar appliedImpulse = pt.m_appliedImpulse;

				PhysicsSceneEvent *event = new PhysicsSceneEvent();
				event->positionOnA = Vector3(ptA.x(), ptA.y(), ptA.z());
				event->positionOnB = Vector3(ptB.x(), ptB.y(), ptB.z());
				event->worldNormalOnB = Vector3(normalOnB.x(), normalOnB.y(), normalOnB.z());
				event->appliedImpulse = appliedImpulse;

				event->entityA = getPhysicsEntityByCollisionObject(obA);
				event->entityB = getPhysicsEntityByCollisionObject(obB);

				dispatchEvent(event, PhysicsSceneEvent::COLLISION_EVENT);
			}
		}
	}

}

void PhysicsScene::Update() {

	for(int i=0; i < physicsChildren.size(); i++) {
//		if(physicsChildren[i]->enabled)
			physicsChildren[i]->Update();
	}

	Number elapsed = CoreServices::getInstance()->getCore()->getElapsed();
	if(maxSubSteps > 0) {
		physicsWorld->stepSimulation(elapsed, maxSubSteps);
	} else {
		physicsWorld->stepSimulation(elapsed);
	}
	CollisionScene::Update();

}

void PhysicsScene::setVelocity(SceneEntity *entity, Vector3 velocity) {
	PhysicsSceneEntity *physicsEntity = getPhysicsEntityBySceneEntity(entity);
	if(physicsEntity) {
		physicsEntity->setVelocity(velocity);
	}
}

void PhysicsScene::warpEntity(SceneEntity *entity, Vector3 position, bool resetRotation) {
	PhysicsSceneEntity *physicsEntity = getPhysicsEntityBySceneEntity(entity);
	if(physicsEntity) {
		physicsEntity->warpTo(position, resetRotation);
	}
}

PhysicsCharacter *PhysicsScene::addCharacterChild(SceneEntity *newEntity,Number mass, Number friction, Number stepSize, int group) {
	addEntity(newEntity);
	PhysicsCharacter *newPhysicsEntity = new PhysicsCharacter(newEntity, mass, friction, stepSize);

	physicsWorld->addCollisionObject(newPhysicsEntity->ghostObject,group, btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
	physicsWorld->addAction(newPhysicsEntity->character);

	physicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(newPhysicsEntity->ghostObject->getBroadphaseHandle(),physicsWorld->getDispatcher());

	newPhysicsEntity->character->reset ();

	newPhysicsEntity->character->setUseGhostSweepTest(false);

	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);
	return newPhysicsEntity;

}

void PhysicsScene::removeCharacterChild(PhysicsCharacter *character) {
	physicsWorld->removeAction(character->character);

	physicsWorld->removeCollisionObject(character->ghostObject);

		for(int i=0; i < physicsChildren.size(); i++) {
			if(physicsChildren[i] == character) {
				physicsChildren.erase(physicsChildren.begin()+i);
			}
		}
		for(int i=0; i < collisionChildren.size(); i++) {
			if(collisionChildren[i] == character) {
				collisionChildren.erase(collisionChildren.begin()+i);
			}
		}

}

PhysicsVehicle *PhysicsScene::addVehicleChild(SceneEntity *newEntity, Number mass, Number friction, int group) {
	addEntity(newEntity);

	btDefaultVehicleRaycaster *m_vehicleRayCaster = new btDefaultVehicleRaycaster(physicsWorld);

	PhysicsVehicle *newPhysicsEntity = new PhysicsVehicle(newEntity, mass, friction,m_vehicleRayCaster);
	physicsWorld->addRigidBody(newPhysicsEntity->rigidBody, group, btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);

	newPhysicsEntity->vehicle = new btRaycastVehicle(newPhysicsEntity->tuning,newPhysicsEntity->rigidBody,m_vehicleRayCaster);

	newPhysicsEntity->rigidBody->setActivationState(DISABLE_DEACTIVATION);

	int rightIndex = 0;
		int upIndex = 1;
		int forwardIndex = 2;

	physicsWorld->addVehicle(newPhysicsEntity->vehicle);

	newPhysicsEntity->vehicle->setCoordinateSystem(rightIndex,upIndex,forwardIndex);

	newPhysicsEntity->vehicle->resetSuspension();

	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);

	return newPhysicsEntity;
}

void PhysicsScene::removePhysicsChild(SceneEntity *entity) {
	PhysicsSceneEntity *ent = getPhysicsEntityBySceneEntity(entity);
	if(ent) {
		if(ent->getType() == PhysicsSceneEntity::CHARACTER_CONTROLLER) {
			removeCharacterChild((PhysicsCharacter *)ent);
		} else {

			if(ent->rigidBody)
				physicsWorld->removeRigidBody(ent->rigidBody);
			physicsWorld->removeCollisionObject(ent->collisionObject);
			for(int i=0; i < physicsChildren.size(); i++) {
				if(physicsChildren[i] == ent) {
					physicsChildren.erase(physicsChildren.begin()+i);
				}
			}
			for(int i=0; i < collisionChildren.size(); i++) {
				if(collisionChildren[i] == ent) {
					collisionChildren.erase(collisionChildren.begin()+i);
				}
			}
		}
	}
	delete ent;
	CollisionScene::removeEntity(entity);
}

void PhysicsScene::removeEntity(SceneEntity *entity) {
	PhysicsSceneEntity *ent = getPhysicsEntityBySceneEntity(entity);
	if(ent) {
		removePhysicsChild(entity);
	} else {
		CollisionScene::removeEntity(entity);
	}
}

PhysicsSceneEntity *PhysicsScene::getPhysicsEntityBySceneEntity(SceneEntity *entity) {
	PhysicsSceneEntity *retEntity = NULL;
	for(int i=0; i < physicsChildren.size(); i++) {
		if(physicsChildren[i]->getSceneEntity() == entity) {
			retEntity = physicsChildren[i];
		}
	}
	return retEntity;
}

PhysicsSceneEntity *PhysicsScene::trackPhysicsChild(SceneEntity *newEntity, int type, Number mass, Number friction, Number restitution, int group) {
	PhysicsSceneEntity *newPhysicsEntity = new PhysicsSceneEntity(newEntity, type, mass, friction,restitution);
	physicsWorld->addRigidBody(newPhysicsEntity->rigidBody, group,  btBroadphaseProxy::AllFilter); //btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
//	world->addCollisionObject(newPhysicsEntity->collisionObject, group);
	//newPhysicsEntity->rigidBody->setActivationState(ISLAND_SLEEPING);
	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);
	return newPhysicsEntity;
}

PhysicsSceneEntity *PhysicsScene::addPhysicsChild(SceneEntity *newEntity, int type, Number mass, Number friction, Number restitution, int group) {
	addEntity(newEntity);
	return trackPhysicsChild(newEntity, type, mass, friction, restitution, group);
}
<commit_msg>Fix that crash (delete children before world)<commit_after>/*
Copyright (C) 2011 by Ivan Safrin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "PolyPhysicsScene.h"
#include "btBulletCollisionCommon.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "BulletDynamics/Character/btKinematicCharacterController.h"

#include "PolyCollisionScene.h"
#include "PolyCoreServices.h"
#include "PolyVector3.h"
#include "PolyPhysicsSceneEntity.h"
#include "PolyCore.h"

using namespace Polycode;

PhysicsSceneEvent::PhysicsSceneEvent() : Event () {
	eventType = "PhysicsSceneEvent";
}

PhysicsScene::PhysicsScene(int maxSubSteps, bool virtualScene) : CollisionScene(virtualScene, true), physicsWorld(NULL), solver(NULL), broadphase(NULL) {
	this->maxSubSteps = maxSubSteps;
	initPhysicsScene();
}

PhysicsScene::~PhysicsScene() {
	for(int i=0; i < collisionChildren.size(); i++)
		delete collisionChildren[i];
	delete physicsWorld;
	delete solver;
	delete broadphase;

	// Prevent double free by ~CollisionScene
	collisionChildren.clear();
	world = NULL;
}

void worldTickCallback(btDynamicsWorld *world, btScalar timeStep) {
	PhysicsScene *physicsScene = (PhysicsScene*)world->getWorldUserInfo();
	physicsScene->processWorldCollisions();
}

void PhysicsScene::initPhysicsScene() {
	collisionConfiguration = new btDefaultCollisionConfiguration();

	dispatcher = new btCollisionDispatcher(collisionConfiguration);

	solver = new btSequentialImpulseConstraintSolver();

	btVector3 worldMin(-100,-100,-100);
	btVector3 worldMax(100,100,100);
	axisSweep = new btAxisSweep3(worldMin,worldMax);

	broadphase = new btDbvtBroadphase();

	physicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);

//	physicsWorld->getSolverInfo().m_solverMode |= SOLVER_RANDMIZE_ORDER;
	physicsWorld->setGravity(btVector3(0,-10,0));

	axisSweep->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());

	world = physicsWorld;

	physicsWorld->setInternalTickCallback(worldTickCallback, this);
}

void PhysicsScene::setGravity(Vector3 gravity) {
	physicsWorld->setGravity(btVector3(gravity.x, gravity.y, gravity.z));
}

PhysicsSceneEntity *PhysicsScene::getPhysicsEntityByCollisionObject(btCollisionObject *object) {
	for(int i=0; i < physicsChildren.size(); i++) {
		PhysicsSceneEntity *entity = physicsChildren[i];
		if(entity->rigidBody == object) {
			return entity;
		}
	}
	return  NULL;
}

void PhysicsScene::processWorldCollisions() {

	int numManifolds = physicsWorld->getDispatcher()->getNumManifolds();
	for (int i=0;i<numManifolds;i++)
	{
		btPersistentManifold* contactManifold =  world->getDispatcher()->getManifoldByIndexInternal(i);
		btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
		btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());

		int numContacts = contactManifold->getNumContacts();
		for (int j=0;j<numContacts;j++)
		{
			btManifoldPoint& pt = contactManifold->getContactPoint(j);
			if (pt.getDistance()<0.f)
			{
				const btVector3& ptA = pt.getPositionWorldOnA();
				const btVector3& ptB = pt.getPositionWorldOnB();
				const btVector3& normalOnB = pt.m_normalWorldOnB;
				const btScalar appliedImpulse = pt.m_appliedImpulse;

				PhysicsSceneEvent *event = new PhysicsSceneEvent();
				event->positionOnA = Vector3(ptA.x(), ptA.y(), ptA.z());
				event->positionOnB = Vector3(ptB.x(), ptB.y(), ptB.z());
				event->worldNormalOnB = Vector3(normalOnB.x(), normalOnB.y(), normalOnB.z());
				event->appliedImpulse = appliedImpulse;

				event->entityA = getPhysicsEntityByCollisionObject(obA);
				event->entityB = getPhysicsEntityByCollisionObject(obB);

				dispatchEvent(event, PhysicsSceneEvent::COLLISION_EVENT);
			}
		}
	}

}

void PhysicsScene::Update() {

	for(int i=0; i < physicsChildren.size(); i++) {
//		if(physicsChildren[i]->enabled)
			physicsChildren[i]->Update();
	}

	Number elapsed = CoreServices::getInstance()->getCore()->getElapsed();
	if(maxSubSteps > 0) {
		physicsWorld->stepSimulation(elapsed, maxSubSteps);
	} else {
		physicsWorld->stepSimulation(elapsed);
	}
	CollisionScene::Update();

}

void PhysicsScene::setVelocity(SceneEntity *entity, Vector3 velocity) {
	PhysicsSceneEntity *physicsEntity = getPhysicsEntityBySceneEntity(entity);
	if(physicsEntity) {
		physicsEntity->setVelocity(velocity);
	}
}

void PhysicsScene::warpEntity(SceneEntity *entity, Vector3 position, bool resetRotation) {
	PhysicsSceneEntity *physicsEntity = getPhysicsEntityBySceneEntity(entity);
	if(physicsEntity) {
		physicsEntity->warpTo(position, resetRotation);
	}
}

PhysicsCharacter *PhysicsScene::addCharacterChild(SceneEntity *newEntity,Number mass, Number friction, Number stepSize, int group) {
	addEntity(newEntity);
	PhysicsCharacter *newPhysicsEntity = new PhysicsCharacter(newEntity, mass, friction, stepSize);

	physicsWorld->addCollisionObject(newPhysicsEntity->ghostObject,group, btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
	physicsWorld->addAction(newPhysicsEntity->character);

	physicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(newPhysicsEntity->ghostObject->getBroadphaseHandle(),physicsWorld->getDispatcher());

	newPhysicsEntity->character->reset ();

	newPhysicsEntity->character->setUseGhostSweepTest(false);

	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);
	return newPhysicsEntity;

}

void PhysicsScene::removeCharacterChild(PhysicsCharacter *character) {
	physicsWorld->removeAction(character->character);

	physicsWorld->removeCollisionObject(character->ghostObject);

		for(int i=0; i < physicsChildren.size(); i++) {
			if(physicsChildren[i] == character) {
				physicsChildren.erase(physicsChildren.begin()+i);
			}
		}
		for(int i=0; i < collisionChildren.size(); i++) {
			if(collisionChildren[i] == character) {
				collisionChildren.erase(collisionChildren.begin()+i);
			}
		}

}

PhysicsVehicle *PhysicsScene::addVehicleChild(SceneEntity *newEntity, Number mass, Number friction, int group) {
	addEntity(newEntity);

	btDefaultVehicleRaycaster *m_vehicleRayCaster = new btDefaultVehicleRaycaster(physicsWorld);

	PhysicsVehicle *newPhysicsEntity = new PhysicsVehicle(newEntity, mass, friction,m_vehicleRayCaster);
	physicsWorld->addRigidBody(newPhysicsEntity->rigidBody, group, btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);

	newPhysicsEntity->vehicle = new btRaycastVehicle(newPhysicsEntity->tuning,newPhysicsEntity->rigidBody,m_vehicleRayCaster);

	newPhysicsEntity->rigidBody->setActivationState(DISABLE_DEACTIVATION);

	int rightIndex = 0;
		int upIndex = 1;
		int forwardIndex = 2;

	physicsWorld->addVehicle(newPhysicsEntity->vehicle);

	newPhysicsEntity->vehicle->setCoordinateSystem(rightIndex,upIndex,forwardIndex);

	newPhysicsEntity->vehicle->resetSuspension();

	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);

	return newPhysicsEntity;
}

void PhysicsScene::removePhysicsChild(SceneEntity *entity) {
	PhysicsSceneEntity *ent = getPhysicsEntityBySceneEntity(entity);
	if(ent) {
		if(ent->getType() == PhysicsSceneEntity::CHARACTER_CONTROLLER) {
			removeCharacterChild((PhysicsCharacter *)ent);
		} else {

			if(ent->rigidBody)
				physicsWorld->removeRigidBody(ent->rigidBody);
			physicsWorld->removeCollisionObject(ent->collisionObject);
			for(int i=0; i < physicsChildren.size(); i++) {
				if(physicsChildren[i] == ent) {
					physicsChildren.erase(physicsChildren.begin()+i);
				}
			}
			for(int i=0; i < collisionChildren.size(); i++) {
				if(collisionChildren[i] == ent) {
					collisionChildren.erase(collisionChildren.begin()+i);
				}
			}
		}
	}
	delete ent;
	CollisionScene::removeEntity(entity);
}

void PhysicsScene::removeEntity(SceneEntity *entity) {
	PhysicsSceneEntity *ent = getPhysicsEntityBySceneEntity(entity);
	if(ent) {
		removePhysicsChild(entity);
	} else {
		CollisionScene::removeEntity(entity);
	}
}

PhysicsSceneEntity *PhysicsScene::getPhysicsEntityBySceneEntity(SceneEntity *entity) {
	PhysicsSceneEntity *retEntity = NULL;
	for(int i=0; i < physicsChildren.size(); i++) {
		if(physicsChildren[i]->getSceneEntity() == entity) {
			retEntity = physicsChildren[i];
		}
	}
	return retEntity;
}

PhysicsSceneEntity *PhysicsScene::trackPhysicsChild(SceneEntity *newEntity, int type, Number mass, Number friction, Number restitution, int group) {
	PhysicsSceneEntity *newPhysicsEntity = new PhysicsSceneEntity(newEntity, type, mass, friction,restitution);
	physicsWorld->addRigidBody(newPhysicsEntity->rigidBody, group,  btBroadphaseProxy::AllFilter); //btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
//	world->addCollisionObject(newPhysicsEntity->collisionObject, group);
	//newPhysicsEntity->rigidBody->setActivationState(ISLAND_SLEEPING);
	physicsChildren.push_back(newPhysicsEntity);
	collisionChildren.push_back(newPhysicsEntity);
	return newPhysicsEntity;
}

PhysicsSceneEntity *PhysicsScene::addPhysicsChild(SceneEntity *newEntity, int type, Number mass, Number friction, Number restitution, int group) {
	addEntity(newEntity);
	return trackPhysicsChild(newEntity, type, mass, friction, restitution, group);
}
<|endoftext|>


<commit_before>#include <node.h>
#include <git2.h>
#include "./object_v8.h"
#include "./repository_v8.h"
#include <string.h>

using namespace v8;

#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif

Handle<Object> Error(int id){
    Isolate *iso = Isolate::GetCurrent();
    EscapableHandleScope scope(iso);

    ObjectV8 ret;
    ret.set("id", id);
    const git_error *err = giterr_last();
    if (err){
        ret.set("message", err->message);
        ret.set("klass", err->klass);
    }else{
        ret.set("message", "");
        ret.set("klass", 0);
    }

    Local<Object> o= ret.toJSObject();
    return scope.Escape(o);
}

struct InitCfg{
    int no_options;
    const char *dir;
    int bare;
    uint32_t shared;
    git_repository_init_options opts;

    InitCfg(const char*, int, const char*, const char*, const char*, const char*);
};

InitCfg::InitCfg(const char *d, int b, const char *s, const char *g, const char *t, const char *de)
    :no_options(1), dir(d), bare(b), shared(0)
{
    if (!strcmp(s, "false") || !strcmp(s, "umask"))
        shared = GIT_REPOSITORY_INIT_SHARED_UMASK;

    else if (!strcmp(s, "true") || !strcmp(s, "group"))
        shared =  GIT_REPOSITORY_INIT_SHARED_GROUP;

    else if (!strcmp(s, "all") || !strcmp(s, "world") || !strcmp(s, "everybody"))
        shared = GIT_REPOSITORY_INIT_SHARED_ALL;

    else if (s[0] == '0') {
        char *end = NULL;
        long val = strtol(s + 1, &end, 8);
        if (end == s + 1 || *end != 0) return; //invalid octal value for --shared
        shared = (uint32_t)val;
    }

    git_repository_init_init_options(&opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION);

    int hasG = strcmp(g, "");
    int hasT = strcmp(t, "");
    int hasD = strcmp(de, "");
    if (shared || hasG || hasT || hasD){
        no_options = 0;
        /**
         * Some command line options were specified, so we'll use the
         * extended init API to handle them
         */
        opts.flags = GIT_REPOSITORY_INIT_MKPATH;

        if (bare) opts.flags |= GIT_REPOSITORY_INIT_BARE;

        if (hasT) {
            opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
            opts.template_path = t;
        }

        if (hasG) {
            /**
             * If you specified a separate git directory, then initialize
             * the repository at that path and use the second path as the
             * working directory of the repository (with a git-link file)
             */
            opts.workdir_path = dir;
            dir = g;
        }

        if (hasD) opts.description = de;

        if (shared) opts.mode = shared;
    }
}

void Init(const FunctionCallbackInfo<Value> &args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 2:
        aDir = args[0]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[1]);
        break;
    case 3:
        aDir = args[0]->ToString();
        aOpts = args[1];
        cb = Local<Function>::Cast(args[2]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    String::Utf8Value shared(o.get("shared", ""));
    String::Utf8Value gitdir(o.get("gitdir", ""));
    String::Utf8Value templ(o.get("template", ""));
    String::Utf8Value desc(o.get("description", ""));

    InitCfg cfg(
        *dir,
        o.get("bare", 0),
        *shared,
        *gitdir,
        *templ,
        *desc
    );
    git_repository *repo = NULL;
    int error = 0;

    if (cfg.no_options){
        error = git_repository_init(&repo, cfg.dir, cfg.bare);
    }else{
        error = git_repository_init_ext(&repo, cfg.dir, &(cfg.opts));
    }

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

struct OpenCfg{
    int no_options;
    const char *dir;
    int bare;
    unsigned int flags;
    const char *ceiling;

    OpenCfg(const char*, int, int, const char*);
};

OpenCfg::OpenCfg(const char *d, int b, int s, const char *c)
    :no_options(1), dir(d), bare(b), flags(0), ceiling(c)
{
    if (!b || !s || !c) return;
    no_options = 0;

    if (!s) flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
    if (c) flags = GIT_REPOSITORY_OPEN_CROSS_FS;
}

void Open(const FunctionCallbackInfo<Value>& args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 2:
        aDir = args[0]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[1]);
        break;
    case 3:
        aDir = args[0]->ToString();
        aOpts = args[1];
        cb = Local<Function>::Cast(args[2]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    String::Utf8Value ceil(o.get("ceiling", ""));

    OpenCfg cfg(
        *dir,
        o.get("bare", 0),
        o.get("search", 0),
        *ceil
    );
    git_repository *repo = NULL;
    int error = 0;

    if (cfg.no_options){
        error = git_repository_open(&repo, cfg.dir);
    }else if (cfg.bare){
        error = git_repository_open_bare(&repo, cfg.dir);
    }else{
        error = git_repository_open_ext(&repo, cfg.dir, cfg.flags, cfg.ceiling);
    }

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

int cred_acquire_cb(git_cred **out,
        const char * UNUSED(url),
        const char * UNUSED(username_from_url),
        unsigned int allowed_types,
        void * UNUSED(payload))
{
    char username[128] = {0};
    char password[128] = {0};
printf("allowed_types: %d\n",allowed_types);
printf("GIT_CREDTYPE_USERPASS_PLAINTEXT: %d\n",allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT);
printf("GIT_CREDTYPE_SSH_KEY: %d\n",allowed_types & GIT_CREDTYPE_SSH_KEY);
printf("GIT_CREDTYPE_SSH_CUSTOM: %d\n",allowed_types & GIT_CREDTYPE_SSH_CUSTOM);
printf("GIT_CREDTYPE_DEFAULT: %d\n",allowed_types & GIT_CREDTYPE_DEFAULT);
printf("GIT_CREDTYPE_SSH_INTERACTIVE: %d\n",allowed_types & GIT_CREDTYPE_SSH_INTERACTIVE);
printf("GIT_CREDTYPE_USERNAME: %d\n",allowed_types & GIT_CREDTYPE_USERNAME);

    printf("Username: ");
    scanf("%s", username);

    /* Yup. Right there on your terminal. Careful where you copy/paste output. */
    printf("Password: ");
    scanf("%s", password);
//git_cred_ssh_custom_new
//git_cred_ssh_key_new
    return git_cred_userpass_plaintext_new(out, username, password);
}

typedef struct progress_data {
    git_transfer_progress fetch_progress;
    size_t completed_steps;
    size_t total_steps;
    const char *path;
} progress_data;

static void print_progress(const progress_data *pd)
{
    int network_percent = pd->fetch_progress.total_objects > 0 ?
        (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects :
        0;
    int index_percent = pd->fetch_progress.total_objects > 0 ?
        (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects :
        0;

    int checkout_percent = pd->total_steps > 0
        ? (100 * pd->completed_steps) / pd->total_steps
        : 0;
    int kbytes = pd->fetch_progress.received_bytes / 1024;

    if (pd->fetch_progress.total_objects &&
        pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
        printf("Resolving deltas %d/%d\r",
               pd->fetch_progress.indexed_deltas,
               pd->fetch_progress.total_deltas);
    } else {
        printf("net %3d%% (%4d kb, %5d/%5d)  /  idx %3d%% (%5d/%5d)  /  chk %3d%% (%4zu/%4zu) %s\n",
           network_percent, kbytes,
           pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
           index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
           checkout_percent,
           pd->completed_steps, pd->total_steps,
           pd->path);
    }
}

static int fetch_progress(const git_transfer_progress *stats, void *payload)
{
    progress_data *pd = (progress_data*)payload;
    pd->fetch_progress = *stats;
    print_progress(pd);
    return 0;
}
static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
{
    progress_data *pd = (progress_data*)payload;
    pd->completed_steps = cur;
    pd->total_steps = tot;
    pd->path = path;
    print_progress(pd);
}

struct CloneCfg{
    const char *url;
    const char *dir;
    progress_data pd;
    git_checkout_options checkout_opts;
    git_clone_options clone_opts;

    CloneCfg(const char*, const char*, int);
};

CloneCfg::CloneCfg(const char *u, const char *d, int f)
    :url(u), dir(d), pd({{0}})
{
    git_clone_init_options(&clone_opts, GIT_CLONE_OPTIONS_VERSION);
    git_checkout_init_options(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);

    checkout_opts.checkout_strategy = f ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
    checkout_opts.progress_cb = checkout_progress;
    checkout_opts.progress_payload = &pd;
    clone_opts.checkout_opts = checkout_opts;
    clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
    clone_opts.remote_callbacks.credentials = cred_acquire_cb;
    clone_opts.remote_callbacks.payload = &pd;
}

void Clone(const FunctionCallbackInfo<Value>& args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aUrl;
    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 3:
        aUrl = args[0]->ToString();
        aDir = args[1]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[2]);
        break;
    case 4:
        aUrl = args[0]->ToString();
        aDir = args[1]->ToString();
        aOpts = args[2];
        cb = Local<Function>::Cast(args[3]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    String::Utf8Value url(aUrl);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    CloneCfg cfg(
        *url,
        *dir,
        o.get("force", 0)
    );
    git_repository *repo = NULL;
    int error = git_clone(&repo, cfg.url, cfg.dir, &(cfg.clone_opts));

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

void setup(Handle<Object> exports){
    NODE_SET_METHOD(exports, "init", Init);
    NODE_SET_METHOD(exports, "open", Open);
    NODE_SET_METHOD(exports, "clone", Clone);
}

NODE_MODULE(gitdist, setup)
<commit_msg>working clone<commit_after>#include <node.h>
#include <git2.h>
#include "./object_v8.h"
#include "./repository_v8.h"
#include <string.h>

using namespace v8;

#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif

Handle<Object> Error(int id){
    Isolate *iso = Isolate::GetCurrent();
    EscapableHandleScope scope(iso);

    ObjectV8 ret;
    ret.set("id", id);
    const git_error *err = giterr_last();
    if (err){
        ret.set("message", err->message);
        ret.set("klass", err->klass);
    }else{
        ret.set("message", "");
        ret.set("klass", 0);
    }

    Local<Object> o= ret.toJSObject();
    return scope.Escape(o);
}

struct InitCfg{
    int no_options;
    const char *dir;
    int bare;
    uint32_t shared;
    git_repository_init_options opts;

    InitCfg(const char*, int, const char*, const char*, const char*, const char*);
};

InitCfg::InitCfg(const char *d, int b, const char *s, const char *g, const char *t, const char *de)
    :no_options(1), dir(d), bare(b), shared(0)
{
    if (!strcmp(s, "false") || !strcmp(s, "umask"))
        shared = GIT_REPOSITORY_INIT_SHARED_UMASK;

    else if (!strcmp(s, "true") || !strcmp(s, "group"))
        shared =  GIT_REPOSITORY_INIT_SHARED_GROUP;

    else if (!strcmp(s, "all") || !strcmp(s, "world") || !strcmp(s, "everybody"))
        shared = GIT_REPOSITORY_INIT_SHARED_ALL;

    else if (s[0] == '0') {
        char *end = NULL;
        long val = strtol(s + 1, &end, 8);
        if (end == s + 1 || *end != 0) return; //invalid octal value for --shared
        shared = (uint32_t)val;
    }

    git_repository_init_init_options(&opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION);

    int hasG = strcmp(g, "");
    int hasT = strcmp(t, "");
    int hasD = strcmp(de, "");
    if (shared || hasG || hasT || hasD){
        no_options = 0;
        /**
         * Some command line options were specified, so we'll use the
         * extended init API to handle them
         */
        opts.flags = GIT_REPOSITORY_INIT_MKPATH;

        if (bare) opts.flags |= GIT_REPOSITORY_INIT_BARE;

        if (hasT) {
            opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
            opts.template_path = t;
        }

        if (hasG) {
            /**
             * If you specified a separate git directory, then initialize
             * the repository at that path and use the second path as the
             * working directory of the repository (with a git-link file)
             */
            opts.workdir_path = dir;
            dir = g;
        }

        if (hasD) opts.description = de;

        if (shared) opts.mode = shared;
    }
}

void Init(const FunctionCallbackInfo<Value> &args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 2:
        aDir = args[0]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[1]);
        break;
    case 3:
        aDir = args[0]->ToString();
        aOpts = args[1];
        cb = Local<Function>::Cast(args[2]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    String::Utf8Value shared(o.get("shared", ""));
    String::Utf8Value gitdir(o.get("gitdir", ""));
    String::Utf8Value templ(o.get("template", ""));
    String::Utf8Value desc(o.get("description", ""));

    InitCfg cfg(
        *dir,
        o.get("bare", 0),
        *shared,
        *gitdir,
        *templ,
        *desc
    );
    git_repository *repo = NULL;
    int error = 0;

    if (cfg.no_options){
        error = git_repository_init(&repo, cfg.dir, cfg.bare);
    }else{
        error = git_repository_init_ext(&repo, cfg.dir, &(cfg.opts));
    }

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

struct OpenCfg{
    int no_options;
    const char *dir;
    int bare;
    unsigned int flags;
    const char *ceiling;

    OpenCfg(const char*, int, int, const char*);
};

OpenCfg::OpenCfg(const char *d, int b, int s, const char *c)
    :no_options(1), dir(d), bare(b), flags(0), ceiling(c)
{
    if (!b || !s || !c) return;
    no_options = 0;

    if (!s) flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
    if (c) flags = GIT_REPOSITORY_OPEN_CROSS_FS;
}

void Open(const FunctionCallbackInfo<Value>& args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 2:
        aDir = args[0]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[1]);
        break;
    case 3:
        aDir = args[0]->ToString();
        aOpts = args[1];
        cb = Local<Function>::Cast(args[2]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    String::Utf8Value ceil(o.get("ceiling", ""));

    OpenCfg cfg(
        *dir,
        o.get("bare", 0),
        o.get("search", 0),
        *ceil
    );
    git_repository *repo = NULL;
    int error = 0;

    if (cfg.no_options){
        error = git_repository_open(&repo, cfg.dir);
    }else if (cfg.bare){
        error = git_repository_open_bare(&repo, cfg.dir);
    }else{
        error = git_repository_open_ext(&repo, cfg.dir, cfg.flags, cfg.ceiling);
    }

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

int cred_acquire_cb(git_cred **out,
        const char * url,
        const char * username_from_url,
        unsigned int allowed_types,
        void * UNUSED(payload))
{
    if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT){
        char username[128] = {0};
        char password[128] = {0};
        printf("Username for %s: [%s]",url,username_from_url);
        scanf("%s", username);
        /* Yup. Right there on your terminal. Careful where you copy/paste output. */
        printf("Password: ");
        scanf("%s", password);
        return git_cred_userpass_plaintext_new(out, username, password);
    }else if (allowed_types & GIT_CREDTYPE_SSH_KEY){
        return git_cred_ssh_key_new(out, username_from_url, "/home/ubuntu/.ssh/id_rsa.pub", "/home/ubuntu/.ssh/id_rsa", "darren<3snow");
    }else if (allowed_types & GIT_CREDTYPE_SSH_CUSTOM){
        //return git_cred_ssh_custom_new();
    }else if (allowed_types & GIT_CREDTYPE_DEFAULT){
        //return git_cred_default_new();
    }else if (allowed_types & GIT_CREDTYPE_SSH_INTERACTIVE){
        //return git_cred_ssh_interactive_new();
    }else if (allowed_types & GIT_CREDTYPE_USERNAME){
        //return git_cred_username_new();
    }
}

typedef struct progress_data {
    git_transfer_progress fetch_progress;
    size_t completed_steps;
    size_t total_steps;
    const char *path;
} progress_data;

static void print_progress(const progress_data *pd)
{
    int network_percent = pd->fetch_progress.total_objects > 0 ?
        (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects :
        0;
    int index_percent = pd->fetch_progress.total_objects > 0 ?
        (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects :
        0;

    int checkout_percent = pd->total_steps > 0
        ? (100 * pd->completed_steps) / pd->total_steps
        : 0;
    int kbytes = pd->fetch_progress.received_bytes / 1024;

    if (pd->fetch_progress.total_objects &&
        pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
        printf("Resolving deltas %d/%d\r",
               pd->fetch_progress.indexed_deltas,
               pd->fetch_progress.total_deltas);
    } else {
        printf("net %3d%% (%4d kb, %5d/%5d)  /  idx %3d%% (%5d/%5d)  /  chk %3d%% (%4zu/%4zu) %s\n",
           network_percent, kbytes,
           pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
           index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
           checkout_percent,
           pd->completed_steps, pd->total_steps,
           pd->path);
    }
}

static int fetch_progress(const git_transfer_progress *stats, void *payload)
{
    progress_data *pd = (progress_data*)payload;
    pd->fetch_progress = *stats;
    print_progress(pd);
    return 0;
}
static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
{
    progress_data *pd = (progress_data*)payload;
    pd->completed_steps = cur;
    pd->total_steps = tot;
    pd->path = path;
    print_progress(pd);
}

struct CloneCfg{
    const char *url;
    const char *dir;
    progress_data pd;
    git_checkout_options checkout_opts;
    git_clone_options clone_opts;

    CloneCfg(const char*, const char*, int);
};

CloneCfg::CloneCfg(const char *u, const char *d, int f)
    :url(u), dir(d), pd({{0}})
{
    git_clone_init_options(&clone_opts, GIT_CLONE_OPTIONS_VERSION);
    git_checkout_init_options(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);

    checkout_opts.checkout_strategy = f ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
    checkout_opts.progress_cb = checkout_progress;
    checkout_opts.progress_payload = &pd;
    clone_opts.checkout_opts = checkout_opts;
    clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
    clone_opts.remote_callbacks.credentials = cred_acquire_cb;
    clone_opts.remote_callbacks.payload = &pd;
}

void Clone(const FunctionCallbackInfo<Value>& args){
    Isolate *iso = Isolate::GetCurrent();
    HandleScope scope(iso);

    git_libgit2_init();

    Local<String> aUrl;
    Local<String> aDir;
    Local<Value> aOpts;
    Local<Function> cb;

    switch(args.Length()){
    case 3:
        aUrl = args[0]->ToString();
        aDir = args[1]->ToString();
        aOpts = Undefined(iso);
        cb = Local<Function>::Cast(args[2]);
        break;
    case 4:
        aUrl = args[0]->ToString();
        aDir = args[1]->ToString();
        aOpts = args[2];
        cb = Local<Function>::Cast(args[3]);
        break;
    default:
        iso->ThrowException(Exception::TypeError(String::NewFromUtf8(iso, "Wrong number of arguments")));
        return;
    }
    String::Utf8Value dir(aDir);
    String::Utf8Value url(aUrl);
    ObjectV8 o(Local<Object>::Cast(aOpts));

    CloneCfg cfg(
        *url,
        *dir,
        o.get("force", 0)
    );
    git_repository *repo = NULL;
    int error = git_clone(&repo, cfg.url, cfg.dir, &(cfg.clone_opts));

    if (error){
        const unsigned argc = 1;
        Local<Value> argv[argc] = { Error(error) };
        cb->Call(iso->GetCurrentContext()->Global(), argc, argv);
        return;
    }

    const unsigned argc = 2;
    Local<Value> argv[argc] = { Null(iso), repo_create(repo) };
    cb->Call(iso->GetCurrentContext()->Global(), argc, argv);

    git_libgit2_shutdown();
}

void setup(Handle<Object> exports){
    NODE_SET_METHOD(exports, "init", Init);
    NODE_SET_METHOD(exports, "open", Open);
    NODE_SET_METHOD(exports, "clone", Clone);
}

NODE_MODULE(gitdist, setup)
<|endoftext|>