<commit_before>/*************************************************************************
 *
 *  $RCSfile: unload.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: hr $ $Date: 2002-04-22 15:25:55 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _RTL_UNLOAD_H_
#include <rtl/unload.h>
#endif

#ifndef _RTL_ALLOC_H_
#include <rtl/alloc.h>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#include <functional>
#include <hash_map>
#include <list>
#include <deque>

using osl::MutexGuard;

//----------------------------------------------------------------------------

template<typename T>
struct MyAllocator
{
    typedef std::size_t    size_type;
    typedef std::ptrdiff_t difference_type;

    typedef T * pointer;
    typedef const T * const_pointer;

    typedef T & reference;
    typedef const T & const_reference;

    typedef T value_type;

    template<typename U>
    struct rebind
    {
        typedef MyAllocator<U> other;
    };

    pointer address (reference value) const
    {
        return &value;
    }
    const_pointer address (const_reference value) const
    {
        return &value;
    }

    MyAllocator (void)
    {}

    template<typename U>
    MyAllocator (const MyAllocator<U> &)
    {}

    MyAllocator (const MyAllocator &)
    {}

    ~MyAllocator (void)
    {}

    size_type max_size() const
    {
        return size_type(-1)/sizeof(T);
    }

    pointer allocate (size_type n, void const * = 0)
    {
        n *= sizeof(T);
        return (pointer)rtl_allocateMemory(sal_uInt32(n));
    }
    void deallocate (pointer p, size_type n)
    {
        n *= sizeof(T);
        rtl_freeMemory(p);
    }

    void construct (pointer p, const_reference value)
    {
        new ((void*)p) T(value);
    }
    void destroy (pointer p)
    {
        p->~T();
    }
};

//----------------------------------------------------------------------------

template<typename T, typename U>
inline bool operator== (const MyAllocator<T> &, const MyAllocator<U> &)
{
    return true;
}

template<typename T, typename U>
inline bool operator!= (const MyAllocator<T> &, const MyAllocator<U> &)
{
    return false;
}

//----------------------------------------------------------------------------
// see stlport '_alloc.h' comments why old compilers require the hack below.
//----------------------------------------------------------------------------

#ifndef __STL_MEMBER_TEMPLATE_CLASSES
namespace _STL
{
    template<typename T, typename U>
    inline MyAllocator<U> & __stl_alloc_rebind (MyAllocator<T> & a, U const *)
    {
        return (MyAllocator<U>&)(a);
    }
}
#endif /* __STL_MEMBER_TEMPLATE_CLASSES */

//----------------------------------------------------------------------------

static void rtl_notifyUnloadingListeners();

static sal_Bool isEqualTimeValue ( const TimeValue* time1,  const TimeValue* time2)
{
    if( time1->Seconds == time2->Seconds &&
        time1->Nanosec == time2->Nanosec)
        return sal_True;
    else
        return sal_False;
}

static sal_Bool isGreaterTimeValue(  const TimeValue* time1,  const TimeValue* time2)
{
    sal_Bool retval= sal_False;
    if ( time1->Seconds > time2->Seconds)
        retval= sal_True;
    else if ( time1->Seconds == time2->Seconds)
    {
        if( time1->Nanosec > time2->Nanosec)
            retval= sal_True;
    }
    return retval;
}

static sal_Bool isGreaterEqualTimeValue( const TimeValue* time1, const TimeValue* time2)
{
    if( isEqualTimeValue( time1, time2) )
        return sal_True;
    else if( isGreaterTimeValue( time1, time2))
        return sal_True;
    else
        return sal_False;
}

static void addTimeValue( const TimeValue* value1, const TimeValue* value2, TimeValue* result)
{
    sal_uInt64 sum;
    result->Nanosec=0;
    result->Seconds=0;

    sum= value1->Nanosec + value2->Nanosec;
    if( sum >= 1000000000 )
    {
        result->Seconds=1;
        sum -= 1000000000;
    }
    result->Nanosec= (sal_uInt32)sum;
    result->Seconds += value1->Seconds + value2->Seconds;
}

static sal_Bool hasEnoughTimePassed( const TimeValue* unusedSince, const TimeValue* timespan)
{
    sal_Bool retval= sal_False;
    TimeValue currentTime;
    if( osl_getSystemTime( &currentTime))
    {
        TimeValue addedTime;
        addTimeValue( unusedSince, timespan, &addedTime);
        if( isGreaterEqualTimeValue( &currentTime, &addedTime))
            retval= sal_True;
    }

    return retval;
}

static osl::Mutex* getUnloadingMutex()
{
    static osl::Mutex * g_pMutex= NULL;
    if (!g_pMutex)
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if (!g_pMutex)
        {
            static osl::Mutex g_aMutex;
            g_pMutex= &g_aMutex;
        }
    }
    return g_pMutex;
}

extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    osl_incrementInterlockedCount( &pMod->counter);
}

extern "C" void rtl_moduleCount_release( rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    OSL_ENSURE( pMod->counter >0 , "library counter incorrect" );
    osl_decrementInterlockedCount( &pMod->counter);
    if( pMod->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());

        if( sal_False == osl_getSystemTime( &pMod->unusedSince) )
        {
            // set the time to 0 if we could not get the time
            pMod->unusedSince.Seconds= 0;
            pMod->unusedSince.Nanosec= 0;
        }
    }
}

struct hashModule
{
    size_t operator()( const oslModule& rkey) const
    {
        return (size_t)rkey;
    }
};

typedef std::hash_map<
    oslModule,
    std::pair<sal_uInt32, component_canUnloadFunc>,
    hashModule,
    std::equal_to<oslModule>,
    MyAllocator<oslModule>
> ModuleMap;

typedef ModuleMap::iterator Mod_IT;

static ModuleMap& getModuleMap()
{
    static ModuleMap * g_pMap= NULL;
    if (!g_pMap)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pMap)
        {
            static ModuleMap g_aModuleMap;
            g_pMap= &g_aModuleMap;
        }
    }
    return *g_pMap;
}

extern "C" sal_Bool rtl_moduleCount_canUnload( rtl_StandardModuleCount * that, TimeValue * libUnused)
{
    if (that->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());
        if (libUnused && (that->counter == 0))
        {
            rtl_copyMemory(libUnused, &that->unusedSince, sizeof(TimeValue));
        }
    }
    return (that->counter == 0);
}

extern "C" sal_Bool SAL_CALL rtl_registerModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    ModuleMap& moduleMap= getModuleMap();
    sal_Bool ret= sal_True;

    // If the module has been registered before, then find it and increment
    // its reference cout
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end())
    {
        //module already registered, increment ref count
        it->second.first++;
    }
    else
    {
        // Test if the module supports unloading (exports component_canUnload)
        rtl::OUString name(RTL_CONSTASCII_USTRINGPARAM( COMPONENT_CANUNLOAD));
        component_canUnloadFunc pFunc=
            (component_canUnloadFunc)osl_getSymbol( module, name.pData);
        if (pFunc)
        {
            //register module for the first time, set ref count to 1
            moduleMap[module]= std::make_pair((sal_uInt32)1, pFunc);
        }
        else
            ret= sal_False;
    }
    return ret;
}

extern "C" void SAL_CALL rtl_unregisterModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());

    ModuleMap& moduleMap= getModuleMap();
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end() )
    {
        // The module is registered, decrement ref count.
        it->second.first --;

        // If the refcount == 0 then remove the module from the map
        if( it->second.first == 0)
            moduleMap.erase( it);
    }
}

extern "C" void SAL_CALL rtl_unloadUnusedModules( TimeValue* libUnused)
{
    MutexGuard guard( getUnloadingMutex());

    typedef std::list< oslModule, MyAllocator<oslModule> > list_type;
    list_type unloadedModulesList;

    ModuleMap& moduleMap= getModuleMap();
    Mod_IT it_e= moduleMap.end();

    // notify all listeners
    rtl_notifyUnloadingListeners();

    // prepare default TimeValue if argumetn is NULL
    TimeValue nullTime={0,0};
    TimeValue* pLibUnused= libUnused? libUnused : &nullTime;

    Mod_IT it= moduleMap.begin();
    for (; it != it_e; ++it)
    {
        //can the module be unloaded?
        component_canUnloadFunc func= it->second.second;
        TimeValue unusedSince= {0, 0};

        if( func( &unusedSince) )
        {
            // module can be unloaded if it has not been used at least for the time
            // specified by the argument libUnused
            if( hasEnoughTimePassed( &unusedSince, pLibUnused))
            {
                // get the reference count and unload the module as many times
                sal_uInt32 refCount= it->second.first;

                for ( sal_uInt32 i=0; i < refCount; i++)
                    osl_unloadModule( it->first);

                // mark the module for later removal
                unloadedModulesList.push_front( it->first);
            }
        }
    }

    // remove all entries containing invalid (unloaded) modules
    list_type::const_iterator un_it= unloadedModulesList.begin();
    for (; un_it != unloadedModulesList.end(); ++un_it)
    {
        moduleMap.erase( *un_it);
    }
}

// ==============================================================================
// Unloading Listener Administration
//===============================================================================
struct hashListener
{
    size_t operator()( const sal_Int32& rkey) const
    {
        return (size_t)rkey;
    }
};

typedef std::hash_map<
    sal_Int32,
    std::pair<rtl_unloadingListenerFunc, void*>,
    hashListener,
    std::equal_to<sal_Int32>,
    MyAllocator<sal_Int32>
> ListenerMap;

typedef ListenerMap::iterator Lis_IT;

static ListenerMap& getListenerMap()
{
    static ListenerMap * g_pListeners= NULL;
    if (!g_pListeners)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pListeners)
        {
            static ListenerMap g_aListenerMap;
            g_pListeners= &g_aListenerMap;
        }
    }
    return *g_pListeners;
}

// This queue contains cookies which have been passed out by rtl_addUnloadingListener and
// which have been regainded by rtl_removeUnloadingListener. When rtl_addUnloadingListener
// is called then a cookie has to be returned. First we look into the set if there is one
// availabe. Otherwise a new cookie will be provided.
// not a new value is returned.

typedef std::deque<
    sal_Int32,
    MyAllocator<sal_Int32>
> queue_type;

static queue_type& getCookieQueue()
{
    static queue_type * g_pCookies= NULL;
    if (!g_pCookies)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pCookies)
        {
            static queue_type g_aCookieQueue;
            g_pCookies= &g_aCookieQueue;
        }
    }
    return *g_pCookies;
}

static sal_Int32 getCookie()
{
    static sal_Int32 cookieValue= 1;

    sal_Int32 retval;
    queue_type& regainedCookies= getCookieQueue();
    if( regainedCookies.empty() )
        retval= cookieValue++;
    else
    {
        retval= regainedCookies.front();
        regainedCookies.pop_front();
    }
    return retval;
}

static inline void recycleCookie( sal_Int32 i)
{
    getCookieQueue().push_back(i);
}

// calling the function twice with the same arguments will return tow different cookies.
// The listener will then notified twice.

extern "C"
sal_Int32 SAL_CALL rtl_addUnloadingListener( rtl_unloadingListenerFunc callback, void* _this)
{
    MutexGuard guard( getUnloadingMutex());

    sal_Int32 cookie= getCookie();
    ListenerMap& listenerMap= getListenerMap();
    listenerMap[ cookie]= std::make_pair( callback, _this);
    return cookie;
}

extern "C"
void SAL_CALL rtl_removeUnloadingListener( sal_Int32 cookie )
{
    MutexGuard guard( getUnloadingMutex());

    ListenerMap& listenerMap= getListenerMap();
    size_t removedElements= listenerMap.erase( cookie);
    if( removedElements )
        recycleCookie( cookie);
}

static void rtl_notifyUnloadingListeners()
{
    ListenerMap& listenerMap= getListenerMap();
    for( Lis_IT it= listenerMap.begin(); it != listenerMap.end(); ++it)
    {
        rtl_unloadingListenerFunc callbackFunc= it->second.first;
        callbackFunc( it->second.second);
    }
}
<commit_msg>INTEGRATION: CWS sal04 (1.5.124); FILE MERGED 2003/09/18 12:58:38 tra 1.5.124.1: #110486#now using the sal::Allocator for STL objects<commit_after>/*************************************************************************
 *
 *  $RCSfile: unload.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: hr $ $Date: 2003-09-29 14:41:49 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _RTL_UNLOAD_H_
#include <rtl/unload.h>
#endif

#ifndef _RTL_ALLOC_H_
#include <rtl/alloc.h>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#ifndef INCLUDED_SAL_INTERNAL_ALLOCATOR_HXX
#include "internal/allocator.hxx"
#endif

#include <functional>
#include <hash_map>
#include <list>
#include <deque>

using osl::MutexGuard;

//----------------------------------------------------------------------------

static void rtl_notifyUnloadingListeners();

static sal_Bool isEqualTimeValue ( const TimeValue* time1,  const TimeValue* time2)
{
    if( time1->Seconds == time2->Seconds &&
        time1->Nanosec == time2->Nanosec)
        return sal_True;
    else
        return sal_False;
}

static sal_Bool isGreaterTimeValue(  const TimeValue* time1,  const TimeValue* time2)
{
    sal_Bool retval= sal_False;
    if ( time1->Seconds > time2->Seconds)
        retval= sal_True;
    else if ( time1->Seconds == time2->Seconds)
    {
        if( time1->Nanosec > time2->Nanosec)
            retval= sal_True;
    }
    return retval;
}

static sal_Bool isGreaterEqualTimeValue( const TimeValue* time1, const TimeValue* time2)
{
    if( isEqualTimeValue( time1, time2) )
        return sal_True;
    else if( isGreaterTimeValue( time1, time2))
        return sal_True;
    else
        return sal_False;
}

static void addTimeValue( const TimeValue* value1, const TimeValue* value2, TimeValue* result)
{
    sal_uInt64 sum;
    result->Nanosec=0;
    result->Seconds=0;

    sum= value1->Nanosec + value2->Nanosec;
    if( sum >= 1000000000 )
    {
        result->Seconds=1;
        sum -= 1000000000;
    }
    result->Nanosec= (sal_uInt32)sum;
    result->Seconds += value1->Seconds + value2->Seconds;
}

static sal_Bool hasEnoughTimePassed( const TimeValue* unusedSince, const TimeValue* timespan)
{
    sal_Bool retval= sal_False;
    TimeValue currentTime;
    if( osl_getSystemTime( &currentTime))
    {
        TimeValue addedTime;
        addTimeValue( unusedSince, timespan, &addedTime);
        if( isGreaterEqualTimeValue( &currentTime, &addedTime))
            retval= sal_True;
    }

    return retval;
}

static osl::Mutex* getUnloadingMutex()
{
    static osl::Mutex * g_pMutex= NULL;
    if (!g_pMutex)
    {
        MutexGuard guard( osl::Mutex::getGlobalMutex() );
        if (!g_pMutex)
        {
            static osl::Mutex g_aMutex;
            g_pMutex= &g_aMutex;
        }
    }
    return g_pMutex;
}

extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    osl_incrementInterlockedCount( &pMod->counter);
}

extern "C" void rtl_moduleCount_release( rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    OSL_ENSURE( pMod->counter >0 , "library counter incorrect" );
    osl_decrementInterlockedCount( &pMod->counter);
    if( pMod->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());

        if( sal_False == osl_getSystemTime( &pMod->unusedSince) )
        {
            // set the time to 0 if we could not get the time
            pMod->unusedSince.Seconds= 0;
            pMod->unusedSince.Nanosec= 0;
        }
    }
}

struct hashModule
{
    size_t operator()( const oslModule& rkey) const
    {
        return (size_t)rkey;
    }
};

typedef std::hash_map<
    oslModule,
    std::pair<sal_uInt32, component_canUnloadFunc>,
    hashModule,
    std::equal_to<oslModule>,
    sal::Allocator<oslModule>
> ModuleMap;

typedef ModuleMap::iterator Mod_IT;

static ModuleMap& getModuleMap()
{
    static ModuleMap * g_pMap= NULL;
    if (!g_pMap)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pMap)
        {
            static ModuleMap g_aModuleMap;
            g_pMap= &g_aModuleMap;
        }
    }
    return *g_pMap;
}

extern "C" sal_Bool rtl_moduleCount_canUnload( rtl_StandardModuleCount * that, TimeValue * libUnused)
{
    if (that->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());
        if (libUnused && (that->counter == 0))
        {
            rtl_copyMemory(libUnused, &that->unusedSince, sizeof(TimeValue));
        }
    }
    return (that->counter == 0);
}

extern "C" sal_Bool SAL_CALL rtl_registerModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    ModuleMap& moduleMap= getModuleMap();
    sal_Bool ret= sal_True;

    // If the module has been registered before, then find it and increment
    // its reference cout
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end())
    {
        //module already registered, increment ref count
        it->second.first++;
    }
    else
    {
        // Test if the module supports unloading (exports component_canUnload)
        rtl::OUString name(RTL_CONSTASCII_USTRINGPARAM( COMPONENT_CANUNLOAD));
        component_canUnloadFunc pFunc=
            (component_canUnloadFunc)osl_getSymbol( module, name.pData);
        if (pFunc)
        {
            //register module for the first time, set ref count to 1
            moduleMap[module]= std::make_pair((sal_uInt32)1, pFunc);
        }
        else
            ret= sal_False;
    }
    return ret;
}

extern "C" void SAL_CALL rtl_unregisterModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());

    ModuleMap& moduleMap= getModuleMap();
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end() )
    {
        // The module is registered, decrement ref count.
        it->second.first --;

        // If the refcount == 0 then remove the module from the map
        if( it->second.first == 0)
            moduleMap.erase( it);
    }
}

extern "C" void SAL_CALL rtl_unloadUnusedModules( TimeValue* libUnused)
{
    MutexGuard guard( getUnloadingMutex());

    typedef std::list< oslModule, sal::Allocator<oslModule> > list_type;
    list_type unloadedModulesList;

    ModuleMap& moduleMap= getModuleMap();
    Mod_IT it_e= moduleMap.end();

    // notify all listeners
    rtl_notifyUnloadingListeners();

    // prepare default TimeValue if argumetn is NULL
    TimeValue nullTime={0,0};
    TimeValue* pLibUnused= libUnused? libUnused : &nullTime;

    Mod_IT it= moduleMap.begin();
    for (; it != it_e; ++it)
    {
        //can the module be unloaded?
        component_canUnloadFunc func= it->second.second;
        TimeValue unusedSince= {0, 0};

        if( func( &unusedSince) )
        {
            // module can be unloaded if it has not been used at least for the time
            // specified by the argument libUnused
            if( hasEnoughTimePassed( &unusedSince, pLibUnused))
            {
                // get the reference count and unload the module as many times
                sal_uInt32 refCount= it->second.first;

                for ( sal_uInt32 i=0; i < refCount; i++)
                    osl_unloadModule( it->first);

                // mark the module for later removal
                unloadedModulesList.push_front( it->first);
            }
        }
    }

    // remove all entries containing invalid (unloaded) modules
    list_type::const_iterator un_it= unloadedModulesList.begin();
    for (; un_it != unloadedModulesList.end(); ++un_it)
    {
        moduleMap.erase( *un_it);
    }
}

// ==============================================================================
// Unloading Listener Administration
//===============================================================================
struct hashListener
{
    size_t operator()( const sal_Int32& rkey) const
    {
        return (size_t)rkey;
    }
};

typedef std::hash_map<
    sal_Int32,
    std::pair<rtl_unloadingListenerFunc, void*>,
    hashListener,
    std::equal_to<sal_Int32>,
    sal::Allocator<sal_Int32>
> ListenerMap;

typedef ListenerMap::iterator Lis_IT;

static ListenerMap& getListenerMap()
{
    static ListenerMap * g_pListeners= NULL;
    if (!g_pListeners)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pListeners)
        {
            static ListenerMap g_aListenerMap;
            g_pListeners= &g_aListenerMap;
        }
    }
    return *g_pListeners;
}

// This queue contains cookies which have been passed out by rtl_addUnloadingListener and
// which have been regainded by rtl_removeUnloadingListener. When rtl_addUnloadingListener
// is called then a cookie has to be returned. First we look into the set if there is one
// availabe. Otherwise a new cookie will be provided.
// not a new value is returned.

typedef std::deque<
    sal_Int32,
    sal::Allocator<sal_Int32>
> queue_type;

static queue_type& getCookieQueue()
{
    static queue_type * g_pCookies= NULL;
    if (!g_pCookies)
    {
        MutexGuard guard( getUnloadingMutex() );
        if (!g_pCookies)
        {
            static queue_type g_aCookieQueue;
            g_pCookies= &g_aCookieQueue;
        }
    }
    return *g_pCookies;
}

static sal_Int32 getCookie()
{
    static sal_Int32 cookieValue= 1;

    sal_Int32 retval;
    queue_type& regainedCookies= getCookieQueue();
    if( regainedCookies.empty() )
        retval= cookieValue++;
    else
    {
        retval= regainedCookies.front();
        regainedCookies.pop_front();
    }
    return retval;
}

static inline void recycleCookie( sal_Int32 i)
{
    getCookieQueue().push_back(i);
}

// calling the function twice with the same arguments will return tow different cookies.
// The listener will then notified twice.

extern "C"
sal_Int32 SAL_CALL rtl_addUnloadingListener( rtl_unloadingListenerFunc callback, void* _this)
{
    MutexGuard guard( getUnloadingMutex());

    sal_Int32 cookie= getCookie();
    ListenerMap& listenerMap= getListenerMap();
    listenerMap[ cookie]= std::make_pair( callback, _this);
    return cookie;
}

extern "C"
void SAL_CALL rtl_removeUnloadingListener( sal_Int32 cookie )
{
    MutexGuard guard( getUnloadingMutex());

    ListenerMap& listenerMap= getListenerMap();
    size_t removedElements= listenerMap.erase( cookie);
    if( removedElements )
        recycleCookie( cookie);
}

static void rtl_notifyUnloadingListeners()
{
    ListenerMap& listenerMap= getListenerMap();
    for( Lis_IT it= listenerMap.begin(); it != listenerMap.end(); ++it)
    {
        rtl_unloadingListenerFunc callbackFunc= it->second.first;
        callbackFunc( it->second.second);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/portability/Time.h>
#include <folly/Likely.h>

#include <assert.h>

#include <chrono>

template <typename _Rep, typename _Period>
static void duration_to_ts(
    std::chrono::duration<_Rep, _Period> d,
    struct timespec* ts) {
  ts->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
  ts->tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
                    d % std::chrono::seconds(1))
                    .count();
}

#if !FOLLY_HAVE_CLOCK_GETTIME
#if __MACH__
#include <errno.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include <mach/mach_types.h>
#include <mach/task.h>
#include <mach/thread_act.h>
#include <mach/vm_map.h>

static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
  return std::chrono::seconds(t.seconds) +
      std::chrono::microseconds(t.microseconds);
}

static int clock_process_cputime(struct timespec* ts) {
  // Get CPU usage for live threads.
  task_thread_times_info thread_times_info;
  mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT;
  kern_return_t kern_result = task_info(
      mach_task_self(),
      TASK_THREAD_TIMES_INFO,
      (thread_info_t)&thread_times_info,
      &thread_times_info_count);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }

  // Get CPU usage for terminated threads.
  mach_task_basic_info task_basic_info;
  mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
  kern_result = task_info(
      mach_task_self(),
      MACH_TASK_BASIC_INFO,
      (thread_info_t)&task_basic_info,
      &task_basic_info_count);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }

  auto cputime = time_value_to_ns(thread_times_info.user_time) +
      time_value_to_ns(thread_times_info.system_time) +
      time_value_to_ns(task_basic_info.user_time) +
      time_value_to_ns(task_basic_info.system_time);
  duration_to_ts(cputime, ts);
  return 0;
}

static int clock_thread_cputime(struct timespec* ts) {
  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  thread_basic_info_data_t thread_info_data;
  thread_act_t thread = mach_thread_self();
  kern_return_t kern_result = thread_info(
      thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count);
  mach_port_deallocate(mach_task_self(), thread);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }
  auto cputime = time_value_to_ns(thread_info_data.system_time) +
      time_value_to_ns(thread_info_data.user_time);
  duration_to_ts(cputime, ts);
  return 0;
}

int clock_gettime(clockid_t clk_id, struct timespec* ts) {
  switch (clk_id) {
    case CLOCK_REALTIME: {
      auto now = std::chrono::system_clock::now().time_since_epoch();
      duration_to_ts(now, ts);
      return 0;
    }
    case CLOCK_MONOTONIC: {
      auto now = std::chrono::steady_clock::now().time_since_epoch();
      duration_to_ts(now, ts);
      return 0;
    }
    case CLOCK_PROCESS_CPUTIME_ID:
      return clock_process_cputime(ts);
    case CLOCK_THREAD_CPUTIME_ID:
      return clock_thread_cputime(ts);
    default:
      errno = EINVAL;
      return -1;
  }
}

int clock_getres(clockid_t clk_id, struct timespec* ts) {
  if (clk_id != CLOCK_MONOTONIC) {
    return -1;
  }

  static auto info = [] {
    static mach_timebase_info_data_t info;
    auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
    assert(result);
    return result;
  }();

  ts->tv_sec = 0;
  ts->tv_nsec = info->numer / info->denom;

  return 0;
}
#elif defined(_WIN32)
#include <errno.h>
#include <locale.h>
#include <stdint.h>
#include <stdlib.h>

#include <folly/portability/Windows.h>

using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;

static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) {
  ULARGE_INTEGER i;
  i.HighPart = ft.dwHighDateTime;
  i.LowPart = ft.dwLowDateTime;

  // FILETIMEs are in units of 100ns.
  return unsigned_nanos(i.QuadPart * 100);
};

static LARGE_INTEGER performanceFrequency() {
  static auto result = [] {
    LARGE_INTEGER freq;
    // On Windows XP or later, this will never fail.
    BOOL res = QueryPerformanceFrequency(&freq);
    assert(res);
    return freq;
  }();
  return result;
}

extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
  if (!res) {
    errno = EFAULT;
    return -1;
  }

  switch (clock_id) {
    case CLOCK_MONOTONIC: {
      LARGE_INTEGER freq = performanceFrequency();
      if (freq.QuadPart == -1) {
        errno = EINVAL;
        return -1;
      }

      static constexpr size_t kNsPerSec = 1000000000;

      res->tv_sec = 0;
      res->tv_nsec = (long)((kNsPerSec + (freq.QuadPart >> 1)) / freq.QuadPart);
      if (res->tv_nsec < 1) {
        res->tv_nsec = 1;
      }

      return 0;
    }

    case CLOCK_REALTIME:
    case CLOCK_PROCESS_CPUTIME_ID:
    case CLOCK_THREAD_CPUTIME_ID: {
      DWORD adj, timeIncrement;
      BOOL adjDisabled;
      if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
        errno = EINVAL;
        return -1;
      }

      res->tv_sec = 0;
      res->tv_nsec = timeIncrement * 100;
      return 0;
    }

    default:
      errno = EINVAL;
      return -1;
  }
}

extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
  if (!tp) {
    errno = EFAULT;
    return -1;
  }

  const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
    static constexpr unsigned_nanos one_sec(std::chrono::seconds(1));
    tp->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(t).count();
    tp->tv_nsec = (t % one_sec).count();
    return 0;
  };

  FILETIME createTime, exitTime, kernalTime, userTime;
  switch (clock_id) {
    case CLOCK_REALTIME: {
      auto now = std::chrono::system_clock::now().time_since_epoch();
      duration_to_ts(now, tp);
      return 0;
    }
    case CLOCK_MONOTONIC: {
      auto now = std::chrono::steady_clock::now().time_since_epoch();
      duration_to_ts(now, tp);
      return 0;
    }
    case CLOCK_PROCESS_CPUTIME_ID: {
      if (!GetProcessTimes(
              GetCurrentProcess(),
              &createTime,
              &exitTime,
              &kernalTime,
              &userTime)) {
        errno = EINVAL;
        return -1;
      }

      return unanosToTimespec(
          tp,
          filetimeToUnsignedNanos(kernalTime) +
              filetimeToUnsignedNanos(userTime));
    }
    case CLOCK_THREAD_CPUTIME_ID: {
      if (!GetThreadTimes(
              GetCurrentThread(),
              &createTime,
              &exitTime,
              &kernalTime,
              &userTime)) {
        errno = EINVAL;
        return -1;
      }

      return unanosToTimespec(
          tp,
          filetimeToUnsignedNanos(kernalTime) +
              filetimeToUnsignedNanos(userTime));
    }

    default:
      errno = EINVAL;
      return -1;
  }
}
#else
#error No clock_gettime(3) compatibility wrapper available for this platform.
#endif
#endif

#ifdef _WIN32
#include <iomanip>
#include <sstream>

#include <folly/portability/Windows.h>

extern "C" {
char* asctime_r(const tm* tm, char* buf) {
  char tmpBuf[64];
  if (asctime_s(tmpBuf, tm)) {
    return nullptr;
  }
  // Nothing we can do if the buff is to small :(
  return strcpy(buf, tmpBuf);
}

char* ctime_r(const time_t* t, char* buf) {
  char tmpBuf[64];
  if (ctime_s(tmpBuf, 64, t)) {
    return nullptr;
  }
  // Nothing we can do if the buff is to small :(
  return strcpy(buf, tmpBuf);
}

tm* gmtime_r(const time_t* t, tm* res) {
  if (!gmtime_s(res, t)) {
    return res;
  }
  return nullptr;
}

tm* localtime_r(const time_t* t, tm* o) {
  if (!localtime_s(o, t)) {
    return o;
  }
  return nullptr;
}

int nanosleep(const struct timespec* request, struct timespec* remain) {
  Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
  if (remain != nullptr) {
    remain->tv_nsec = 0;
    remain->tv_sec = 0;
  }
  return 0;
}

char* strptime(const char* __restrict s,
               const char* __restrict f,
               struct tm* __restrict tm) {
  // Isn't the C++ standard lib nice? std::get_time is defined such that its
  // format parameters are the exact same as strptime. Of course, we have to
  // create a string stream first, and imbue it with the current C locale, and
  // we also have to make sure we return the right things if it fails, or
  // if it succeeds, but this is still far simpler an implementation than any
  // of the versions in any of the C standard libraries.
  std::istringstream input(s);
  input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
  input >> std::get_time(tm, f);
  if (input.fail()) {
    return nullptr;
  }
  return const_cast<char*>(s + input.tellg());
}
}
#endif
<commit_msg>Return the correct resolution for clock_getres<commit_after>/*
 * Copyright 2016 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/portability/Time.h>
#include <folly/Likely.h>

#include <assert.h>

#include <chrono>

template <typename _Rep, typename _Period>
static void duration_to_ts(
    std::chrono::duration<_Rep, _Period> d,
    struct timespec* ts) {
  ts->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
  ts->tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
                    d % std::chrono::seconds(1))
                    .count();
}

#if !FOLLY_HAVE_CLOCK_GETTIME
#if __MACH__
#include <errno.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include <mach/mach_types.h>
#include <mach/task.h>
#include <mach/thread_act.h>
#include <mach/vm_map.h>

static std::chrono::nanoseconds time_value_to_ns(time_value_t t) {
  return std::chrono::seconds(t.seconds) +
      std::chrono::microseconds(t.microseconds);
}

static int clock_process_cputime(struct timespec* ts) {
  // Get CPU usage for live threads.
  task_thread_times_info thread_times_info;
  mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT;
  kern_return_t kern_result = task_info(
      mach_task_self(),
      TASK_THREAD_TIMES_INFO,
      (thread_info_t)&thread_times_info,
      &thread_times_info_count);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }

  // Get CPU usage for terminated threads.
  mach_task_basic_info task_basic_info;
  mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;
  kern_result = task_info(
      mach_task_self(),
      MACH_TASK_BASIC_INFO,
      (thread_info_t)&task_basic_info,
      &task_basic_info_count);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }

  auto cputime = time_value_to_ns(thread_times_info.user_time) +
      time_value_to_ns(thread_times_info.system_time) +
      time_value_to_ns(task_basic_info.user_time) +
      time_value_to_ns(task_basic_info.system_time);
  duration_to_ts(cputime, ts);
  return 0;
}

static int clock_thread_cputime(struct timespec* ts) {
  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  thread_basic_info_data_t thread_info_data;
  thread_act_t thread = mach_thread_self();
  kern_return_t kern_result = thread_info(
      thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count);
  mach_port_deallocate(mach_task_self(), thread);
  if (UNLIKELY(kern_result != KERN_SUCCESS)) {
    return -1;
  }
  auto cputime = time_value_to_ns(thread_info_data.system_time) +
      time_value_to_ns(thread_info_data.user_time);
  duration_to_ts(cputime, ts);
  return 0;
}

int clock_gettime(clockid_t clk_id, struct timespec* ts) {
  switch (clk_id) {
    case CLOCK_REALTIME: {
      auto now = std::chrono::system_clock::now().time_since_epoch();
      duration_to_ts(now, ts);
      return 0;
    }
    case CLOCK_MONOTONIC: {
      auto now = std::chrono::steady_clock::now().time_since_epoch();
      duration_to_ts(now, ts);
      return 0;
    }
    case CLOCK_PROCESS_CPUTIME_ID:
      return clock_process_cputime(ts);
    case CLOCK_THREAD_CPUTIME_ID:
      return clock_thread_cputime(ts);
    default:
      errno = EINVAL;
      return -1;
  }
}

int clock_getres(clockid_t clk_id, struct timespec* ts) {
  if (clk_id != CLOCK_MONOTONIC) {
    return -1;
  }

  static auto info = [] {
    static mach_timebase_info_data_t info;
    auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr;
    assert(result);
    return result;
  }();

  ts->tv_sec = 0;
  ts->tv_nsec = info->numer / info->denom;

  return 0;
}
#elif defined(_WIN32)
#include <errno.h>
#include <locale.h>
#include <stdint.h>
#include <stdlib.h>

#include <folly/portability/Windows.h>

using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;

static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) {
  ULARGE_INTEGER i;
  i.HighPart = ft.dwHighDateTime;
  i.LowPart = ft.dwLowDateTime;

  // FILETIMEs are in units of 100ns.
  return unsigned_nanos(i.QuadPart * 100);
};

static LARGE_INTEGER performanceFrequency() {
  static auto result = [] {
    LARGE_INTEGER freq;
    // On Windows XP or later, this will never fail.
    BOOL res = QueryPerformanceFrequency(&freq);
    assert(res);
    return freq;
  }();
  return result;
}

extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) {
  if (!res) {
    errno = EFAULT;
    return -1;
  }

  static constexpr size_t kNsPerSec = 1000000000;
  switch (clock_id) {
    case CLOCK_REALTIME: {
      constexpr auto perSec = double(std::chrono::system_clock::period::num) /
          std::chrono::system_clock::period::den;
      res->tv_sec = time_t(perSec);
      res->tv_nsec = time_t(perSec * kNsPerSec);
      return 0;
    }
    case CLOCK_MONOTONIC: {
      constexpr auto perSec = double(std::chrono::steady_clock::period::num) /
          std::chrono::steady_clock::period::den;
      res->tv_sec = time_t(perSec);
      res->tv_nsec = time_t(perSec * kNsPerSec);
      return 0;
    }
    case CLOCK_PROCESS_CPUTIME_ID:
    case CLOCK_THREAD_CPUTIME_ID: {
      DWORD adj, timeIncrement;
      BOOL adjDisabled;
      if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) {
        errno = EINVAL;
        return -1;
      }

      res->tv_sec = 0;
      res->tv_nsec = timeIncrement * 100;
      return 0;
    }

    default:
      errno = EINVAL;
      return -1;
  }
}

extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) {
  if (!tp) {
    errno = EFAULT;
    return -1;
  }

  const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int {
    static constexpr unsigned_nanos one_sec(std::chrono::seconds(1));
    tp->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(t).count();
    tp->tv_nsec = (t % one_sec).count();
    return 0;
  };

  FILETIME createTime, exitTime, kernalTime, userTime;
  switch (clock_id) {
    case CLOCK_REALTIME: {
      auto now = std::chrono::system_clock::now().time_since_epoch();
      duration_to_ts(now, tp);
      return 0;
    }
    case CLOCK_MONOTONIC: {
      auto now = std::chrono::steady_clock::now().time_since_epoch();
      duration_to_ts(now, tp);
      return 0;
    }
    case CLOCK_PROCESS_CPUTIME_ID: {
      if (!GetProcessTimes(
              GetCurrentProcess(),
              &createTime,
              &exitTime,
              &kernalTime,
              &userTime)) {
        errno = EINVAL;
        return -1;
      }

      return unanosToTimespec(
          tp,
          filetimeToUnsignedNanos(kernalTime) +
              filetimeToUnsignedNanos(userTime));
    }
    case CLOCK_THREAD_CPUTIME_ID: {
      if (!GetThreadTimes(
              GetCurrentThread(),
              &createTime,
              &exitTime,
              &kernalTime,
              &userTime)) {
        errno = EINVAL;
        return -1;
      }

      return unanosToTimespec(
          tp,
          filetimeToUnsignedNanos(kernalTime) +
              filetimeToUnsignedNanos(userTime));
    }

    default:
      errno = EINVAL;
      return -1;
  }
}
#else
#error No clock_gettime(3) compatibility wrapper available for this platform.
#endif
#endif

#ifdef _WIN32
#include <iomanip>
#include <sstream>

#include <folly/portability/Windows.h>

extern "C" {
char* asctime_r(const tm* tm, char* buf) {
  char tmpBuf[64];
  if (asctime_s(tmpBuf, tm)) {
    return nullptr;
  }
  // Nothing we can do if the buff is to small :(
  return strcpy(buf, tmpBuf);
}

char* ctime_r(const time_t* t, char* buf) {
  char tmpBuf[64];
  if (ctime_s(tmpBuf, 64, t)) {
    return nullptr;
  }
  // Nothing we can do if the buff is to small :(
  return strcpy(buf, tmpBuf);
}

tm* gmtime_r(const time_t* t, tm* res) {
  if (!gmtime_s(res, t)) {
    return res;
  }
  return nullptr;
}

tm* localtime_r(const time_t* t, tm* o) {
  if (!localtime_s(o, t)) {
    return o;
  }
  return nullptr;
}

int nanosleep(const struct timespec* request, struct timespec* remain) {
  Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000)));
  if (remain != nullptr) {
    remain->tv_nsec = 0;
    remain->tv_sec = 0;
  }
  return 0;
}

char* strptime(const char* __restrict s,
               const char* __restrict f,
               struct tm* __restrict tm) {
  // Isn't the C++ standard lib nice? std::get_time is defined such that its
  // format parameters are the exact same as strptime. Of course, we have to
  // create a string stream first, and imbue it with the current C locale, and
  // we also have to make sure we return the right things if it fails, or
  // if it succeeds, but this is still far simpler an implementation than any
  // of the versions in any of the C standard libraries.
  std::istringstream input(s);
  input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
  input >> std::get_time(tm, f);
  if (input.fail()) {
    return nullptr;
  }
  return const_cast<char*>(s + input.tellg());
}
}
#endif
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <memory>
#include "Reporter.h"
#include "Event.h"
#include "Handler.h"
#include "Message.h"

#define STATUS_SUCCESS						0x00000000
#define STATUS_LOGON_FAILURE				0xC000006D
#define STATUS_BAD_NETWORK_NAME				0xC00000CC
#define STATUS_ACCESS_DENIED				0xC0000022
#define STATUS_MORE_PROCESSING_REQUIRED		0xC0000016
#define STATUS_USER_SESSION_DELETED			0xC0000203
#define STATUS_INVALID_PARAMETER			0xC000000D
#define STATUS_FS_DRIVER_REQUIRED			0xC000019C
#define STATUS_NOT_SUPPORTED				0xC00000BB
#define STATUS_NETWORK_NAME_DELETED			0xC00000C9
#define STATUS_FILE_CLOSED					0xC0000128
#define STATUS_OBJECT_NAME_NOT_FOUND		0xC0000034
#define STATUS_NO_MORE_FILES				0x80000006
#define STATUS_BUFFER_OVERFLOW				0x80000005
#define STATUS_NO_MORE_FILES				0x80000006
#define STATUS_NOTIFY_ENUM_DIR				0x0000010C

#define SMBX_COMMAND(handlerType) \
	CommandPair( \
		[] (shared_ptr<SMB2_Header> header) -> shared_ptr<SMB2_Body> { return make_shared<handlerType##_Request>(header); }, \
		[] (shared_ptr<SMB2_Header> header, shared_ptr<SMB2_Body> request) -> shared_ptr<SMB2_Body> { return make_shared<handlerType##_Response>(header, request); } \
	)

namespace SMBx
{
	enum SMB2_COMMAND : uint16
	{
		SMB2_NEGOTIATE_PROTOCOL = 0,
		SMB2_SESSION_SETUP      = 1,
		SMB2_LOGOFF             = 2,
		SMB2_TREE_CONNECT       = 3,
		SMB2_TREE_DISCONNECT    = 4,
		SMB2_CREATE             = 5,
		SMB2_CLOSE              = 6,
		SMB2_FLUSH              = 7,
		SMB2_READ               = 8,
		SMB2_WRITE              = 9,
		SMB2_LOCK               = 10,
		SMB2_IOCTL              = 11,
		SMB2_CANCEL             = 12,
		SMB2_ECHO               = 13,
		SMB2_QUERY_DIRECTORY    = 14,
		SMB2_CHANGE_NOTIFY      = 15,
		SMB2_QUERY_INFO         = 16,
		SMB2_SET_INFO           = 17,
		SMB2_OPLOCK_BREAK       = 18
	};

	Commands::Commands()
	{
		commands[SMB2_NEGOTIATE_PROTOCOL] = SMBX_COMMAND(SMB2_Negotiate);
		commands[SMB2_SESSION_SETUP] = SMBX_COMMAND(SMB2_Session_Setup);
		commands[SMB2_LOGOFF] = SMBX_COMMAND(SMB2_Logoff);
		commands[SMB2_TREE_CONNECT] = SMBX_COMMAND(SMB2_Tree_Connect);
		commands[SMB2_TREE_DISCONNECT] = SMBX_COMMAND(SMB2_Tree_Disconnect);
		commands[SMB2_CREATE] = SMBX_COMMAND(SMB2_Create);
		commands[SMB2_CLOSE] = SMBX_COMMAND(SMB2_Close);
		// commands[SMB2_FLUSH] = SMBX_COMMAND(SMB2_Flush);
		commands[SMB2_READ] = SMBX_COMMAND(SMB2_Read);
		commands[SMB2_WRITE] = SMBX_COMMAND(SMB2_Write);
		// commands[SMB2_LOCK] = SMBX_COMMAND(SMB2_Lock);
		// commands[SMB2_IOCTL] = SMBX_COMMAND(SMB2_Ioctl);
		commands[SMB2_CANCEL] = SMBX_COMMAND(SMB2_Cancel);
		// commands[SMB2_ECHO] = SMBX_COMMAND(SMB2_Echo);
		commands[SMB2_QUERY_DIRECTORY] = SMBX_COMMAND(SMB2_Query_Directory);
		// commands[SMB2_CHANGE_NOTIFY] = SMBX_COMMAND(SMB2_Change_Notify);
		// commands[SMB2_QUERY_INFO] = SMBX_COMMAND(SMB2_Query_Info);
		// commands[SMB2_SET_INFO] = SMBX_COMMAND(SMB2_Set_Info);
		// commands[SMB2_OPLOCK_BREAK] = SMBX_COMMAND(SMB2_Oplock_Break);
	}

	const CommandPair Commands::get(uint16 command, bool is_response) const
	{
		auto it = commands.find(command);
		if (it == commands.end())
			return default_command;

		return it->second;
	}

	void Handler::handle(int len, const u_char* data)
	{
		reader.reset(len, data);

		do {
			auto beginning = reader.current_pos;

			if (is_last_finished == true) {
				if (!is_last_parsed) {
					buffer.append(len, data);
					reader.reset(buffer.len(), buffer.data());
				}

				is_last_parsed = handle_new();

				if (!is_last_parsed) {
					buffer.append(len - beginning, data + beginning);
					reader.move_end();
				} else {
					buffer.reset();
				}
			} else {
				handle_continue();
			}

			if (is_last_finished == true && current_message != nullptr && current_message->header->is_response) {
				current_message = nullptr;
			}
		} while (reader.current_pos < reader.len);
	}

	bool Handler::handle_new()
	{
		// Find SMB2 magic
		while (reader.current_pos + 4 < reader.len && *((uint32*)(reader.data + reader.current_pos)) != 0x424D53FE)
		{
			reader.skip(1);
			if (reader.current_pos + 4 >= reader.len) {
				DEBUG_MSG("Not SMB2 package.\n");
				reader.move_end();
				return true;
			}
		}

		if (reader.len - reader.current_pos < 64) {
			return false;
		}

		auto header = make_shared<SMB2_Header>();
		header->New(reader);

		current_message = get_message(header);

		if (current_message == nullptr)
		{
			DEBUG_MSG("Unknown command: %hu\n", header->command);
			reader.move_end();
			return true;
		}

		auto avail = reader.len - reader.current_pos;
		if (header->structure_size - 2 > avail) // Structure is not part of header - hence the - 2
		{
			return false;
		} else {
			is_last_finished = current_message->New(context, reader);
			if (!header->is_response)
				context.state.PushMessage(header->messageId, current_message);

			return true;
		}
	}

	void Handler::handle_continue()
	{
		ASSERT(current_message != nullptr);
		is_last_finished = current_message->Continue(context, reader);
	}

	shared_ptr<SMB2_Body> Handler::get_message(shared_ptr<SMB2_Header> header)
	{
		auto command_id = header->command;
		auto status = header->status;
		auto is_response = header->is_response;
		shared_ptr<SMB2_Body> request;

		if (is_response)
			request = context.state.PopMessage(header->messageId);

		if (is_response && status != STATUS_SUCCESS
			&& !(command_id == SMB2_SESSION_SETUP && status == STATUS_MORE_PROCESSING_REQUIRED)
			&& !(command_id == SMB2_QUERY_INFO && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_READ && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_IOCTL && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_READ && status == STATUS_INVALID_PARAMETER)
			&& !(command_id == SMB2_READ && status == STATUS_NOTIFY_ENUM_DIR))
		{
			return make_shared<SMB2_Error>(header, request);
		}

		auto pair = commands.get(command_id, is_response);
		return is_response ? pair.response(header, request) : pair.request(header);
	}
}<commit_msg>Bug fix: Min. header len should include structure size<commit_after>#include <stdlib.h>
#include <memory>
#include "Reporter.h"
#include "Event.h"
#include "Handler.h"
#include "Message.h"

#define STATUS_SUCCESS						0x00000000
#define STATUS_LOGON_FAILURE				0xC000006D
#define STATUS_BAD_NETWORK_NAME				0xC00000CC
#define STATUS_ACCESS_DENIED				0xC0000022
#define STATUS_MORE_PROCESSING_REQUIRED		0xC0000016
#define STATUS_USER_SESSION_DELETED			0xC0000203
#define STATUS_INVALID_PARAMETER			0xC000000D
#define STATUS_FS_DRIVER_REQUIRED			0xC000019C
#define STATUS_NOT_SUPPORTED				0xC00000BB
#define STATUS_NETWORK_NAME_DELETED			0xC00000C9
#define STATUS_FILE_CLOSED					0xC0000128
#define STATUS_OBJECT_NAME_NOT_FOUND		0xC0000034
#define STATUS_NO_MORE_FILES				0x80000006
#define STATUS_BUFFER_OVERFLOW				0x80000005
#define STATUS_NO_MORE_FILES				0x80000006
#define STATUS_NOTIFY_ENUM_DIR				0x0000010C

#define SMBX_COMMAND(handlerType) \
	CommandPair( \
		[] (shared_ptr<SMB2_Header> header) -> shared_ptr<SMB2_Body> { return make_shared<handlerType##_Request>(header); }, \
		[] (shared_ptr<SMB2_Header> header, shared_ptr<SMB2_Body> request) -> shared_ptr<SMB2_Body> { return make_shared<handlerType##_Response>(header, request); } \
	)

namespace SMBx
{
	enum SMB2_COMMAND : uint16
	{
		SMB2_NEGOTIATE_PROTOCOL = 0,
		SMB2_SESSION_SETUP      = 1,
		SMB2_LOGOFF             = 2,
		SMB2_TREE_CONNECT       = 3,
		SMB2_TREE_DISCONNECT    = 4,
		SMB2_CREATE             = 5,
		SMB2_CLOSE              = 6,
		SMB2_FLUSH              = 7,
		SMB2_READ               = 8,
		SMB2_WRITE              = 9,
		SMB2_LOCK               = 10,
		SMB2_IOCTL              = 11,
		SMB2_CANCEL             = 12,
		SMB2_ECHO               = 13,
		SMB2_QUERY_DIRECTORY    = 14,
		SMB2_CHANGE_NOTIFY      = 15,
		SMB2_QUERY_INFO         = 16,
		SMB2_SET_INFO           = 17,
		SMB2_OPLOCK_BREAK       = 18
	};

	Commands::Commands()
	{
		commands[SMB2_NEGOTIATE_PROTOCOL] = SMBX_COMMAND(SMB2_Negotiate);
		commands[SMB2_SESSION_SETUP] = SMBX_COMMAND(SMB2_Session_Setup);
		commands[SMB2_LOGOFF] = SMBX_COMMAND(SMB2_Logoff);
		commands[SMB2_TREE_CONNECT] = SMBX_COMMAND(SMB2_Tree_Connect);
		commands[SMB2_TREE_DISCONNECT] = SMBX_COMMAND(SMB2_Tree_Disconnect);
		commands[SMB2_CREATE] = SMBX_COMMAND(SMB2_Create);
		commands[SMB2_CLOSE] = SMBX_COMMAND(SMB2_Close);
		// commands[SMB2_FLUSH] = SMBX_COMMAND(SMB2_Flush);
		commands[SMB2_READ] = SMBX_COMMAND(SMB2_Read);
		commands[SMB2_WRITE] = SMBX_COMMAND(SMB2_Write);
		// commands[SMB2_LOCK] = SMBX_COMMAND(SMB2_Lock);
		// commands[SMB2_IOCTL] = SMBX_COMMAND(SMB2_Ioctl);
		commands[SMB2_CANCEL] = SMBX_COMMAND(SMB2_Cancel);
		// commands[SMB2_ECHO] = SMBX_COMMAND(SMB2_Echo);
		commands[SMB2_QUERY_DIRECTORY] = SMBX_COMMAND(SMB2_Query_Directory);
		// commands[SMB2_CHANGE_NOTIFY] = SMBX_COMMAND(SMB2_Change_Notify);
		// commands[SMB2_QUERY_INFO] = SMBX_COMMAND(SMB2_Query_Info);
		// commands[SMB2_SET_INFO] = SMBX_COMMAND(SMB2_Set_Info);
		// commands[SMB2_OPLOCK_BREAK] = SMBX_COMMAND(SMB2_Oplock_Break);
	}

	const CommandPair Commands::get(uint16 command, bool is_response) const
	{
		auto it = commands.find(command);
		if (it == commands.end())
			return default_command;

		return it->second;
	}

	void Handler::handle(int len, const u_char* data)
	{
		reader.reset(len, data);

		do {
			auto beginning = reader.current_pos;

			if (is_last_finished == true) {
				if (!is_last_parsed) {
					buffer.append(len, data);
					reader.reset(buffer.len(), buffer.data());
				}

				is_last_parsed = handle_new();

				if (!is_last_parsed) {
					buffer.append(len - beginning, data + beginning);
					reader.move_end();
				} else {
					buffer.reset();
				}
			} else {
				handle_continue();
			}

			if (is_last_finished == true && current_message != nullptr && current_message->header->is_response) {
				current_message = nullptr;
			}
		} while (reader.current_pos < reader.len);
	}

	bool Handler::handle_new()
	{
		// Find SMB2 magic
		while (reader.current_pos + 4 < reader.len && *((uint32*)(reader.data + reader.current_pos)) != 0x424D53FE)
		{
			reader.skip(1);
			if (reader.current_pos + 4 >= reader.len) {
				DEBUG_MSG("Not SMB2 package.\n");
				reader.move_end();
				return true;
			}
		}

		// 64 bytes for header and 2 bytes for structure size
		if (reader.len - reader.current_pos < 66) {
			return false;
		}

		auto header = make_shared<SMB2_Header>();
		header->New(reader);

		current_message = get_message(header);

		if (current_message == nullptr)
		{
			DEBUG_MSG("Unknown command: %hu\n", header->command);
			reader.move_end();
			return true;
		}

		auto avail = reader.len - reader.current_pos;
		if (header->structure_size - 2 > avail) // Structure is not part of header - hence the - 2
		{
			return false;
		} else {
			is_last_finished = current_message->New(context, reader);
			if (!header->is_response)
				context.state.PushMessage(header->messageId, current_message);

			return true;
		}
	}

	void Handler::handle_continue()
	{
		ASSERT(current_message != nullptr);
		is_last_finished = current_message->Continue(context, reader);
	}

	shared_ptr<SMB2_Body> Handler::get_message(shared_ptr<SMB2_Header> header)
	{
		auto command_id = header->command;
		auto status = header->status;
		auto is_response = header->is_response;
		shared_ptr<SMB2_Body> request;

		if (is_response)
			request = context.state.PopMessage(header->messageId);

		if (is_response && status != STATUS_SUCCESS
			&& !(command_id == SMB2_SESSION_SETUP && status == STATUS_MORE_PROCESSING_REQUIRED)
			&& !(command_id == SMB2_QUERY_INFO && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_READ && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_IOCTL && status == STATUS_BUFFER_OVERFLOW)
			&& !(command_id == SMB2_READ && status == STATUS_INVALID_PARAMETER)
			&& !(command_id == SMB2_READ && status == STATUS_NOTIFY_ENUM_DIR))
		{
			return make_shared<SMB2_Error>(header, request);
		}

		auto pair = commands.get(command_id, is_response);
		return is_response ? pair.response(header, request) : pair.request(header);
	}
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: unload.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: jl $ $Date: 2001-06-11 15:57:04 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _RTL_UNLOAD_H_
#include <rtl/unload.h>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#include <hash_map>
#include <list>
#include <queue>

using namespace ::std;
using namespace ::rtl;
using namespace ::osl;

static void rtl_notifyUnloadingListeners();

static sal_Bool isEqualTimeValue ( const TimeValue* time1,  const TimeValue* time2)
{
    if( time1->Seconds == time2->Seconds &&
        time1->Nanosec == time2->Nanosec)
        return sal_True;
    else
        return sal_False;
}

static sal_Bool isGreaterTimeValue(  const TimeValue* time1,  const TimeValue* time2)
{
    sal_Bool retval;
    if ( time1->Seconds > time2->Seconds)
        retval= sal_True;
    else if ( time1->Seconds == time2->Seconds)
    {
        if( time1->Nanosec > time2->Nanosec)
            retval= sal_True;
    }
    else
        retval= sal_False;
    return retval;
}

static sal_Bool isGreaterEqualTimeValue( const TimeValue* time1, const TimeValue* time2)
{
    if( isEqualTimeValue( time1, time2) )
        return sal_True;
    else if( isGreaterTimeValue( time1, time2))
        return sal_True;
    else
        return sal_False;
}

static void addTimeValue( const TimeValue* value1, const TimeValue* value2, TimeValue* result)
{
    sal_uInt64 sum;
    result->Nanosec=0;
    result->Seconds=0;

    sum= value1->Nanosec + value2->Nanosec;
    if( sum >= 1000000000 )
    {
        result->Seconds=1;
        sum -= 1000000000;
    }
    result->Nanosec= (sal_uInt32)sum;
    result->Seconds += value1->Seconds + value2->Seconds;
}

static sal_Bool hasEnoughTimePassed( const TimeValue* unusedSince, const TimeValue* timespan)
{
    sal_Bool retval= sal_False;
    TimeValue currentTime;
    if( osl_getSystemTime( &currentTime))
    {
        TimeValue addedTime;
        addTimeValue( unusedSince, timespan, &addedTime);
        if( isGreaterEqualTimeValue( &currentTime, &addedTime))
            retval= sal_True;
    }

    return retval;
}

static Mutex* getUnloadingMutex()
{
    static Mutex* pMutex= NULL;

    if( ! pMutex)
    {
        MutexGuard guard( Mutex::getGlobalMutex() );
        if( !pMutex)
        {
            static Mutex aMutex;
            pMutex= &aMutex;
        }
    }
    return pMutex;
}

extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    osl_incrementInterlockedCount( &pMod->counter);
}

extern "C" void rtl_moduleCount_release( rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    OSL_ENSURE( pMod->counter >0 , "library counter incorrect" );
    osl_decrementInterlockedCount( &pMod->counter);
    if( pMod->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());

        if( sal_False == osl_getSystemTime( &pMod->unusedSince) )
        {// set the time to 0 if we could not get the time
            pMod->unusedSince.Seconds= 0;
            pMod->unusedSince.Nanosec= 0;
        }
    }
}

struct hashModule
{
    size_t operator()( const oslModule& rkey) const
    {
        return (size_t)rkey;
    }
};

struct equalModule
{
    bool operator()(const oslModule& m1, const oslModule& s2) const
    {
        return m1 == s2;
    }
};

typedef hash_map<oslModule,
        pair<sal_uInt32,component_canUnloadFunc>,
        hashModule, equalModule>
        ModuleMap;
typedef ModuleMap::iterator Mod_IT;
ModuleMap g_moduleMap;

extern "C" sal_Bool rtl_moduleCount_canUnload( rtl_StandardModuleCount * that, TimeValue * libUnused)
{
    sal_Bool retVal= sal_False;
    if( that->counter== 0)
    {
        MutexGuard guard( getUnloadingMutex());
        if( libUnused && that->counter == 0)
        {

            rtl_copyMemory( libUnused, &that->unusedSince,  sizeof( TimeValue));
        }
    }
    return that->counter == 0;
}

extern "C" sal_Bool SAL_CALL rtl_registerModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    sal_Bool ret= sal_True;
    // If the module has been registered before, then find it and increment
    // its reference cout
    Mod_IT it= g_moduleMap.find( module);
    if( it != g_moduleMap.end())
    {
        //module already registered, increment ref count
        it->second.first++;
    }
    else
    {
        //Test if the module supports unloading, that is, it exports
        // component_canUnload
        OUString name(RTL_CONSTASCII_USTRINGPARAM( COMPONENT_CANUNLOAD));
        component_canUnloadFunc  pFunc= ( component_canUnloadFunc)osl_getSymbol( module, name.pData);
        if( pFunc)
        {
            //register module for the first time, set ref count to 1
            g_moduleMap[module]= make_pair((sal_uInt32)1, pFunc);
        }
        else
            ret= sal_False;
    }
    return ret;
}

extern "C" void SAL_CALL rtl_unregisterModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    Mod_IT it= g_moduleMap.find( module);
    if( it != g_moduleMap.end() )
    {
        // The module is registered, decrement ref count.
        it->second.first --;

        // If the refcount == 0 then remove the module from the map
        if( it->second.first == 0)
            g_moduleMap.erase( it);
    }

}

extern "C" void SAL_CALL rtl_unloadUnusedModules( TimeValue* libUnused)
{
    MutexGuard guard( getUnloadingMutex());
    list<oslModule> unloadedModulesList;
    Mod_IT it_e= g_moduleMap.end();

    // notify all listeners
    rtl_notifyUnloadingListeners();
    // prepare default TimeValue if argumetn is NULL
    TimeValue nullTime={0,0};
    TimeValue* pLibUnused= libUnused? libUnused : &nullTime;
    for( Mod_IT it= g_moduleMap.begin(); it != it_e; it++)
    {
        //can the module be unloaded?
        component_canUnloadFunc func= it->second.second;
        TimeValue unusedSince= {0, 0};
        if( func( &unusedSince) )
        {   // module can be unloaded if it has not been used at least for the time
            // specified by the argument libUnused
            if( hasEnoughTimePassed( &unusedSince, pLibUnused))
            {
                // get the reference count and unload the module as many times
                sal_uInt32 refCount= it->second.first;
                for ( sal_uInt32 i=0; i < refCount; i++)
                    osl_unloadModule( it->first);
                // mark the module for later removal
                unloadedModulesList.push_front( it->first);
            }
        }
    }

    // remove all entries containing invalid (unloaded) modules
    for( list<oslModule>::iterator un_it= unloadedModulesList.begin();
            un_it != unloadedModulesList.end(); un_it++)
    {
        g_moduleMap.erase( *un_it);
    }
}

// ==============================================================================
// Unloading Listener Administration
//===============================================================================
struct hashListener
{
    size_t operator()( const sal_Int32& rkey) const
    {
        return (size_t)rkey;
    }
};

struct equalListener
{
    bool operator()(const sal_Int32& m1, const sal_Int32& s2) const
    {
        return m1 == s2;
    }
};

typedef hash_map<sal_Int32,
        pair<rtl_unloadingListenerFunc, void*>,
        hashListener, equalListener>
        ListenerMap;
typedef ListenerMap::iterator Lis_IT;
ListenerMap g_listenerMap;

// This queue contains cookies which have been passed out by rtl_addUnloadingListener and
// which have been regainded by rtl_removeUnloadingListener. When rtl_addUnloadingListener
// is called then a cookie has to be returned. First we look into the set if there is one
// availabe. Otherwise a new cookie will be provided.
// not a new value is returned.
static queue<sal_Int32> g_regainedCookies;

static sal_Int32 getCookie()
{
    static sal_Int32 cookieValue= 1;

    sal_Int32 retval;
    if( g_regainedCookies.empty() )
        retval= cookieValue++;
    else
    {
        retval= g_regainedCookies.front();
        g_regainedCookies.pop();
    }
    return retval;
}

static inline void recycleCookie( sal_Int32 i)
{
    g_regainedCookies.push( i);
}

// calling the function twice with the same arguments will return tow different cookies.
// The listener will then notified twice.
extern "C"
sal_Int32 SAL_CALL rtl_addUnloadingListener( rtl_unloadingListenerFunc callback, void* _this)
{
    MutexGuard guard( getUnloadingMutex());
    sal_Int32 cookie= getCookie();
    g_listenerMap[ cookie]= make_pair( callback, _this);
    return cookie;
}

extern "C"
void SAL_CALL rtl_removeUnloadingListener( sal_Int32 cookie )
{
    MutexGuard guard( getUnloadingMutex());
    size_t removedElements= g_listenerMap.erase( cookie);
    if( removedElements )
        recycleCookie( cookie);
}

static void rtl_notifyUnloadingListeners()
{
    for( Lis_IT it= g_listenerMap.begin(); it != g_listenerMap.end(); it++)
    {
        rtl_unloadingListenerFunc callbackFunc= it->second.first;
        callbackFunc( it->second.second);
    }
}
<commit_msg>#97345# put static stl objects in get functions because of problems with solaris compiler<commit_after>/*************************************************************************
 *
 *  $RCSfile: unload.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: jl $ $Date: 2002-02-07 10:17:02 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _RTL_UNLOAD_H_
#include <rtl/unload.h>
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#include <hash_map>
#include <list>
#include <queue>

using namespace ::std;
using namespace ::rtl;
using namespace ::osl;

static void rtl_notifyUnloadingListeners();

static sal_Bool isEqualTimeValue ( const TimeValue* time1,  const TimeValue* time2)
{
    if( time1->Seconds == time2->Seconds &&
        time1->Nanosec == time2->Nanosec)
        return sal_True;
    else
        return sal_False;
}

static sal_Bool isGreaterTimeValue(  const TimeValue* time1,  const TimeValue* time2)
{
    sal_Bool retval;
    if ( time1->Seconds > time2->Seconds)
        retval= sal_True;
    else if ( time1->Seconds == time2->Seconds)
    {
        if( time1->Nanosec > time2->Nanosec)
            retval= sal_True;
    }
    else
        retval= sal_False;
    return retval;
}

static sal_Bool isGreaterEqualTimeValue( const TimeValue* time1, const TimeValue* time2)
{
    if( isEqualTimeValue( time1, time2) )
        return sal_True;
    else if( isGreaterTimeValue( time1, time2))
        return sal_True;
    else
        return sal_False;
}

static void addTimeValue( const TimeValue* value1, const TimeValue* value2, TimeValue* result)
{
    sal_uInt64 sum;
    result->Nanosec=0;
    result->Seconds=0;

    sum= value1->Nanosec + value2->Nanosec;
    if( sum >= 1000000000 )
    {
        result->Seconds=1;
        sum -= 1000000000;
    }
    result->Nanosec= (sal_uInt32)sum;
    result->Seconds += value1->Seconds + value2->Seconds;
}

static sal_Bool hasEnoughTimePassed( const TimeValue* unusedSince, const TimeValue* timespan)
{
    sal_Bool retval= sal_False;
    TimeValue currentTime;
    if( osl_getSystemTime( &currentTime))
    {
        TimeValue addedTime;
        addTimeValue( unusedSince, timespan, &addedTime);
        if( isGreaterEqualTimeValue( &currentTime, &addedTime))
            retval= sal_True;
    }

    return retval;
}

static Mutex* getUnloadingMutex()
{
    static Mutex* pMutex= NULL;

    if( ! pMutex)
    {
        MutexGuard guard( Mutex::getGlobalMutex() );
        if( !pMutex)
        {
            static Mutex aMutex;
            pMutex= &aMutex;
        }
    }
    return pMutex;
}

extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    osl_incrementInterlockedCount( &pMod->counter);
}

extern "C" void rtl_moduleCount_release( rtl_ModuleCount * that )
{
    rtl_StandardModuleCount* pMod= (rtl_StandardModuleCount*)that;
    OSL_ENSURE( pMod->counter >0 , "library counter incorrect" );
    osl_decrementInterlockedCount( &pMod->counter);
    if( pMod->counter == 0)
    {
        MutexGuard guard( getUnloadingMutex());

        if( sal_False == osl_getSystemTime( &pMod->unusedSince) )
        {// set the time to 0 if we could not get the time
            pMod->unusedSince.Seconds= 0;
            pMod->unusedSince.Nanosec= 0;
        }
    }
}

struct hashModule
{
    size_t operator()( const oslModule& rkey) const
    {
        return (size_t)rkey;
    }
};

struct equalModule
{
    bool operator()(const oslModule& m1, const oslModule& s2) const
    {
        return m1 == s2;
    }
};

typedef hash_map<oslModule,
        pair<sal_uInt32,component_canUnloadFunc>,
        hashModule, equalModule>
        ModuleMap;
typedef ModuleMap::iterator Mod_IT;

static ModuleMap& getModuleMap()
{
    static ModuleMap* pMap= NULL;
    if( ! pMap)
    {
        MutexGuard guard( getUnloadingMutex() );
        if( !pMap)
        {
            static ModuleMap aModuleMap;
            pMap= &aModuleMap;
        }
    }
    return *pMap;
}

extern "C" sal_Bool rtl_moduleCount_canUnload( rtl_StandardModuleCount * that, TimeValue * libUnused)
{
    sal_Bool retVal= sal_False;
    if( that->counter== 0)
    {
        MutexGuard guard( getUnloadingMutex());
        if( libUnused && that->counter == 0)
        {

            rtl_copyMemory( libUnused, &that->unusedSince,  sizeof( TimeValue));
        }
    }
    return that->counter == 0;
}

extern "C" sal_Bool SAL_CALL rtl_registerModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    ModuleMap& moduleMap= getModuleMap();
    sal_Bool ret= sal_True;
    // If the module has been registered before, then find it and increment
    // its reference cout
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end())
    {
        //module already registered, increment ref count
        it->second.first++;
    }
    else
    {
        //Test if the module supports unloading, that is, it exports
        // component_canUnload
        OUString name(RTL_CONSTASCII_USTRINGPARAM( COMPONENT_CANUNLOAD));
        component_canUnloadFunc  pFunc= ( component_canUnloadFunc)osl_getSymbol( module, name.pData);
        if( pFunc)
        {
            //register module for the first time, set ref count to 1
            moduleMap[module]= make_pair((sal_uInt32)1, pFunc);
        }
        else
            ret= sal_False;
    }
    return ret;
}

extern "C" void SAL_CALL rtl_unregisterModuleForUnloading( oslModule module)
{
    MutexGuard guard( getUnloadingMutex());
    ModuleMap& moduleMap= getModuleMap();
    Mod_IT it= moduleMap.find( module);
    if( it != moduleMap.end() )
    {
        // The module is registered, decrement ref count.
        it->second.first --;

        // If the refcount == 0 then remove the module from the map
        if( it->second.first == 0)
            moduleMap.erase( it);
    }

}

extern "C" void SAL_CALL rtl_unloadUnusedModules( TimeValue* libUnused)
{
    MutexGuard guard( getUnloadingMutex());
    ModuleMap& moduleMap= getModuleMap();
    list<oslModule> unloadedModulesList;
    Mod_IT it_e= moduleMap.end();

    // notify all listeners
    rtl_notifyUnloadingListeners();
    // prepare default TimeValue if argumetn is NULL
    TimeValue nullTime={0,0};
    TimeValue* pLibUnused= libUnused? libUnused : &nullTime;
    for( Mod_IT it= moduleMap.begin(); it != it_e; it++)
    {
        //can the module be unloaded?
        component_canUnloadFunc func= it->second.second;
        TimeValue unusedSince= {0, 0};
        if( func( &unusedSince) )
        {   // module can be unloaded if it has not been used at least for the time
            // specified by the argument libUnused
            if( hasEnoughTimePassed( &unusedSince, pLibUnused))
            {
                // get the reference count and unload the module as many times
                sal_uInt32 refCount= it->second.first;
                for ( sal_uInt32 i=0; i < refCount; i++)
                    osl_unloadModule( it->first);
                // mark the module for later removal
                unloadedModulesList.push_front( it->first);
            }
        }
    }

    // remove all entries containing invalid (unloaded) modules
    for( list<oslModule>::iterator un_it= unloadedModulesList.begin();
            un_it != unloadedModulesList.end(); un_it++)
    {
        moduleMap.erase( *un_it);
    }
}

// ==============================================================================
// Unloading Listener Administration
//===============================================================================
struct hashListener
{
    size_t operator()( const sal_Int32& rkey) const
    {
        return (size_t)rkey;
    }
};

struct equalListener
{
    bool operator()(const sal_Int32& m1, const sal_Int32& s2) const
    {
        return m1 == s2;
    }
};

typedef hash_map<sal_Int32,
        pair<rtl_unloadingListenerFunc, void*>,
        hashListener, equalListener>
        ListenerMap;
typedef ListenerMap::iterator Lis_IT;

static ListenerMap& getListenerMap()
{
    static ListenerMap* pListeners= NULL;
    if( ! pListeners)
    {
        MutexGuard guard( getUnloadingMutex() );
        if( !pListeners)
        {
            static ListenerMap aListenerMap;
            pListeners= &aListenerMap;
        }
    }
    return *pListeners;
}

// This queue contains cookies which have been passed out by rtl_addUnloadingListener and
// which have been regainded by rtl_removeUnloadingListener. When rtl_addUnloadingListener
// is called then a cookie has to be returned. First we look into the set if there is one
// availabe. Otherwise a new cookie will be provided.
// not a new value is returned.
static queue<sal_Int32>& getCookieQueue()
{
    static queue<sal_Int32>* pCookies= NULL;
    if( ! pCookies)
    {
        MutexGuard guard( getUnloadingMutex() );
        if( !pCookies)
        {
            static queue<sal_Int32> aCookieQueue;
            pCookies= &aCookieQueue;
        }
    }
    return *pCookies;
}

static sal_Int32 getCookie()
{
    static sal_Int32 cookieValue= 1;

    sal_Int32 retval;
    queue<sal_Int32>& regainedCookies= getCookieQueue();
    if( regainedCookies.empty() )
        retval= cookieValue++;
    else
    {
        retval= regainedCookies.front();
        regainedCookies.pop();
    }
    return retval;
}

static inline void recycleCookie( sal_Int32 i)
{
    getCookieQueue().push( i);
}

// calling the function twice with the same arguments will return tow different cookies.
// The listener will then notified twice.
extern "C"
sal_Int32 SAL_CALL rtl_addUnloadingListener( rtl_unloadingListenerFunc callback, void* _this)
{
    MutexGuard guard( getUnloadingMutex());
    sal_Int32 cookie= getCookie();
    ListenerMap& listenerMap= getListenerMap();
    listenerMap[ cookie]= make_pair( callback, _this);
    return cookie;
}

extern "C"
void SAL_CALL rtl_removeUnloadingListener( sal_Int32 cookie )
{
    MutexGuard guard( getUnloadingMutex());
    ListenerMap& listenerMap= getListenerMap();
    size_t removedElements= listenerMap.erase( cookie);
    if( removedElements )
        recycleCookie( cookie);
}

static void rtl_notifyUnloadingListeners()
{
    ListenerMap& listenerMap= getListenerMap();
    for( Lis_IT it= listenerMap.begin(); it != listenerMap.end(); it++)
    {
        rtl_unloadingListenerFunc callbackFunc= it->second.first;
        callbackFunc( it->second.second);
    }
}
<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexLua.cxx
 ** Lexer for Lua language.
 **
 ** Written by Paul Winwood.
 ** Folder by Alexey Yutkin.
 **/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

inline bool isLuaOperator(char ch) {
	if (isalnum(ch))
		return false;
	// '.' left out as it is used to make up numbers
	if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
	        ch == '(' || ch == ')' || ch == '=' ||
	        ch == '{' || ch == '}' || ch == '~' ||
	        ch == '[' || ch == ']' || ch == ';' ||
	        ch == '<' || ch == '>' || ch == ',' ||
	        ch == '.' || ch == '^' || ch == '%' || ch == ':')
		return true;
	return false;
}

static void classifyWordLua(unsigned int start,
                            unsigned int end,
                            WordList	&keywords,
                            Accessor	&styler) {
	char s[100];
	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');

	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
		s[i] = styler[start + i];
		s[i + 1] = '\0';
	}

	char chAttr = SCE_LUA_IDENTIFIER;

	if (wordIsNumber)
		chAttr = SCE_LUA_NUMBER;
	else {
		if (keywords.InList(s)) {
			chAttr = SCE_LUA_WORD;
		}
	}
	styler.ColourTo(end, chAttr);
}

static void ColouriseLuaDoc(unsigned int startPos,
                            int length,
                            int initStyle,
                            WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);
	styler.GetLine(startPos);

	int state = initStyle;
	char chPrev = ' ';
	char chNext = styler[startPos];
	unsigned int lengthDoc = startPos + length;
	bool firstChar = true;
	int literalString = 0;

	styler.StartSegment(startPos);
	for (unsigned int i = startPos; i <= lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if (styler.IsLeadByte(ch)) {
			chNext = styler.SafeGetCharAt(i + 2);
			chPrev = ' ';
			i += 1;
			continue;
		}

		if (state == SCE_LUA_STRINGEOL) {
			if (ch != '\r' && ch != '\n') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_DEFAULT;
			}
		}

		if (state == SCE_LUA_LITERALSTRING && ch == '[' && chNext == '[') {
			literalString++;
		} else if (state == SCE_LUA_DEFAULT) {
			if (ch == '-' && chNext == '-') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_COMMENTLINE;
			} else if (ch == '[' && chNext == '[') {
				state = SCE_LUA_LITERALSTRING;
				literalString = 1;
			} else if (ch == '\"') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_STRING;
			} else if (ch == '\'') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_CHARACTER;
			} else if (ch == '$' && firstChar) {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_PREPROCESSOR;
			} else if (ch == '#' && firstChar)	// Should be only on the first line of the file! Cannot be tested here
			{
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_COMMENTLINE;
			} else if (isLuaOperator(ch)) {
				styler.ColourTo(i - 1, state);
				styler.ColourTo(i, SCE_LUA_OPERATOR);
			} else if (iswordstart(ch)) {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_WORD;
			}
		} else if (state == SCE_LUA_WORD) {
			if (!iswordchar(ch)) {
				classifyWordLua(styler.GetStartSegment(), i - 1, keywords, styler);
				state = SCE_LUA_DEFAULT;
				if (ch == '[' && chNext == '[') {
					literalString = 1;
					state = SCE_LUA_LITERALSTRING;
				} else if (ch == '-' && chNext == '-') {
					state = SCE_LUA_COMMENTLINE;
				} else if (ch == '\"') {
					state = SCE_LUA_STRING;
				} else if (ch == '\'') {
					state = SCE_LUA_CHARACTER;
				} else if (ch == '$' && firstChar) {
					state = SCE_LUA_PREPROCESSOR;
				} else if (isLuaOperator(ch)) {
					styler.ColourTo(i, SCE_LUA_OPERATOR);
				}
			} else if (ch == '.' && chNext == '.') {
				classifyWordLua(styler.GetStartSegment(), i - 1, keywords, styler);
				styler.ColourTo(i, SCE_LUA_OPERATOR);
				state = SCE_LUA_DEFAULT;
			}
		} else {
			if (state == SCE_LUA_LITERALSTRING) {
				if (ch == ']' && (chPrev == ']') && (--literalString == 0)) {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_PREPROCESSOR) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_COMMENTLINE) {
				if (ch == '\r' || ch == '\n') {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_STRING) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_STRINGEOL;
				} else if (ch == '\\') {
					if (chNext == '\"' || chNext == '\\') {
						i++;
						ch = chNext;
						chNext = styler.SafeGetCharAt(i + 1);
					}
				} else if (ch == '\"') {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
					i++;
					ch = chNext;
					chNext = styler.SafeGetCharAt(i + 1);
				}
			} else if (state == SCE_LUA_CHARACTER) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_STRINGEOL;
				} else if (ch == '\\') {
					if (chNext == '\'' || chNext == '\\') {
						i++;
						ch = chNext;
						chNext = styler.SafeGetCharAt(i + 1);
					}
				} else if (ch == '\'') {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
					i++;
					ch = chNext;
					chNext = styler.SafeGetCharAt(i + 1);
				}
			}

			if (state == SCE_LUA_DEFAULT) {
				if (ch == '-' && chNext == '-') {
					state = SCE_LUA_COMMENTLINE;
				} else if (ch == '\"') {
					state = SCE_LUA_STRING;
				} else if (ch == '\'') {
					state = SCE_LUA_CHARACTER;
				} else if (ch == '$' && firstChar) {
					state = SCE_LUA_PREPROCESSOR;
				} else if (iswordstart(ch)) {
					state = SCE_LUA_WORD;
				} else if (isLuaOperator(ch)) {
					styler.ColourTo(i, SCE_LUA_OPERATOR);
				}
			}
		}
		chPrev = ch;
		firstChar = (ch == '\r' || ch == '\n');
	}
	styler.ColourTo(lengthDoc - 1, state);
}

static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
                       Accessor &styler) {
	unsigned int lengthDoc = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	char s[10];
	for (unsigned int i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (style == SCE_LUA_WORD)
			if ( ch == 'e' || ch == 't' || ch == 'd' || ch == 'f') {
				for (unsigned int j = 0; j < 8; j++) {
					if (!iswordchar(styler[i + j])) break;
					s[j] = styler[i + j];
					s[j + 1] = '\0';
				}

				if ((strcmp(s, "then") == 0) || (strcmp(s, "do") == 0)
				        || (strcmp(s, "function") == 0))
					levelCurrent++;
				if (strcmp(s, "end") == 0) levelCurrent--;
			}

		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if ((levelCurrent > levelPrev) && (visibleChars > 0))
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch))
			visibleChars++;
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc);
<commit_msg>Patch from Philippe to set literal string state correctly based on initial state.<commit_after>// Scintilla source code edit control
/** @file LexLua.cxx
 ** Lexer for Lua language.
 **
 ** Written by Paul Winwood.
 ** Folder by Alexey Yutkin.
 **/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

inline bool isLuaOperator(char ch) {
	if (isalnum(ch))
		return false;
	// '.' left out as it is used to make up numbers
	if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
	        ch == '(' || ch == ')' || ch == '=' ||
	        ch == '{' || ch == '}' || ch == '~' ||
	        ch == '[' || ch == ']' || ch == ';' ||
	        ch == '<' || ch == '>' || ch == ',' ||
	        ch == '.' || ch == '^' || ch == '%' || ch == ':')
		return true;
	return false;
}

static void classifyWordLua(unsigned int start,
                            unsigned int end,
                            WordList	&keywords,
                            Accessor	&styler) {
	char s[100];
	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');

	for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
		s[i] = styler[start + i];
		s[i + 1] = '\0';
	}

	char chAttr = SCE_LUA_IDENTIFIER;

	if (wordIsNumber)
		chAttr = SCE_LUA_NUMBER;
	else {
		if (keywords.InList(s)) {
			chAttr = SCE_LUA_WORD;
		}
	}
	styler.ColourTo(end, chAttr);
}

static void ColouriseLuaDoc(unsigned int startPos,
                            int length,
                            int initStyle,
                            WordList *keywordlists[],
                            Accessor &styler) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);
	styler.GetLine(startPos);

	int state = initStyle;
	char chPrev = ' ';
	char chNext = styler[startPos];
	unsigned int lengthDoc = startPos + length;
	bool firstChar = true;

	/* Must initialize the literalString level, if we are inside such a string.
	 * Note: this isn't enough, because literal strings can be nested,
	 * we should go back to see at what level we are...
	 */
	int literalString = (initStyle == SCE_LUA_LITERALSTRING) ? 1 : 0;

	styler.StartSegment(startPos);
	for (unsigned int i = startPos; i <= lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if (styler.IsLeadByte(ch)) {
			chNext = styler.SafeGetCharAt(i + 2);
			chPrev = ' ';
			i += 1;
			continue;
		}

		if (state == SCE_LUA_STRINGEOL) {
			if (ch != '\r' && ch != '\n') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_DEFAULT;
			}
		}

		if (state == SCE_LUA_LITERALSTRING && ch == '[' && chNext == '[') {
			literalString++;
		} else if (state == SCE_LUA_DEFAULT) {
			if (ch == '-' && chNext == '-') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_COMMENTLINE;
			} else if (ch == '[' && chNext == '[') {
				state = SCE_LUA_LITERALSTRING;
				literalString = 1;
			} else if (ch == '\"') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_STRING;
			} else if (ch == '\'') {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_CHARACTER;
			} else if (ch == '$' && firstChar) {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_PREPROCESSOR;
			} else if (ch == '#' && firstChar)	// Should be only on the first line of the file! Cannot be tested here
			{
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_COMMENTLINE;
			} else if (isLuaOperator(ch)) {
				styler.ColourTo(i - 1, state);
				styler.ColourTo(i, SCE_LUA_OPERATOR);
			} else if (iswordstart(ch)) {
				styler.ColourTo(i - 1, state);
				state = SCE_LUA_WORD;
			}
		} else if (state == SCE_LUA_WORD) {
			if (!iswordchar(ch)) {
				classifyWordLua(styler.GetStartSegment(), i - 1, keywords, styler);
				state = SCE_LUA_DEFAULT;
				if (ch == '[' && chNext == '[') {
					literalString = 1;
					state = SCE_LUA_LITERALSTRING;
				} else if (ch == '-' && chNext == '-') {
					state = SCE_LUA_COMMENTLINE;
				} else if (ch == '\"') {
					state = SCE_LUA_STRING;
				} else if (ch == '\'') {
					state = SCE_LUA_CHARACTER;
				} else if (ch == '$' && firstChar) {
					state = SCE_LUA_PREPROCESSOR;
				} else if (isLuaOperator(ch)) {
					styler.ColourTo(i, SCE_LUA_OPERATOR);
				}
			} else if (ch == '.' && chNext == '.') {
				classifyWordLua(styler.GetStartSegment(), i - 1, keywords, styler);
				styler.ColourTo(i, SCE_LUA_OPERATOR);
				state = SCE_LUA_DEFAULT;
			}
		} else {
			if (state == SCE_LUA_LITERALSTRING) {
				if (ch == ']' && (chPrev == ']') && (--literalString == 0)) {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_PREPROCESSOR) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_COMMENTLINE) {
				if (ch == '\r' || ch == '\n') {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_DEFAULT;
				}
			} else if (state == SCE_LUA_STRING) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_STRINGEOL;
				} else if (ch == '\\') {
					if (chNext == '\"' || chNext == '\\') {
						i++;
						ch = chNext;
						chNext = styler.SafeGetCharAt(i + 1);
					}
				} else if (ch == '\"') {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
					i++;
					ch = chNext;
					chNext = styler.SafeGetCharAt(i + 1);
				}
			} else if (state == SCE_LUA_CHARACTER) {
				if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
					styler.ColourTo(i - 1, state);
					state = SCE_LUA_STRINGEOL;
				} else if (ch == '\\') {
					if (chNext == '\'' || chNext == '\\') {
						i++;
						ch = chNext;
						chNext = styler.SafeGetCharAt(i + 1);
					}
				} else if (ch == '\'') {
					styler.ColourTo(i, state);
					state = SCE_LUA_DEFAULT;
					i++;
					ch = chNext;
					chNext = styler.SafeGetCharAt(i + 1);
				}
			}

			if (state == SCE_LUA_DEFAULT) {
				if (ch == '-' && chNext == '-') {
					state = SCE_LUA_COMMENTLINE;
				} else if (ch == '\"') {
					state = SCE_LUA_STRING;
				} else if (ch == '\'') {
					state = SCE_LUA_CHARACTER;
				} else if (ch == '$' && firstChar) {
					state = SCE_LUA_PREPROCESSOR;
				} else if (iswordstart(ch)) {
					state = SCE_LUA_WORD;
				} else if (isLuaOperator(ch)) {
					styler.ColourTo(i, SCE_LUA_OPERATOR);
				}
			}
		}
		chPrev = ch;
		firstChar = (ch == '\r' || ch == '\n');
	}
	styler.ColourTo(lengthDoc - 1, state);
}

static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
                       Accessor &styler) {
	unsigned int lengthDoc = startPos + length;
	int visibleChars = 0;
	int lineCurrent = styler.GetLine(startPos);
	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
	int levelCurrent = levelPrev;
	char chNext = styler[startPos];
	int styleNext = styler.StyleAt(startPos);
	char s[10];
	for (unsigned int i = startPos; i < lengthDoc; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);
		int style = styleNext;
		styleNext = styler.StyleAt(i + 1);
		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
		if (style == SCE_LUA_WORD)
			if ( ch == 'e' || ch == 't' || ch == 'd' || ch == 'f') {
				for (unsigned int j = 0; j < 8; j++) {
					if (!iswordchar(styler[i + j])) break;
					s[j] = styler[i + j];
					s[j + 1] = '\0';
				}

				if ((strcmp(s, "then") == 0) || (strcmp(s, "do") == 0)
				        || (strcmp(s, "function") == 0))
					levelCurrent++;
				if (strcmp(s, "end") == 0) levelCurrent--;
			}

		if (atEOL) {
			int lev = levelPrev;
			if (visibleChars == 0)
				lev |= SC_FOLDLEVELWHITEFLAG;
			if ((levelCurrent > levelPrev) && (visibleChars > 0))
				lev |= SC_FOLDLEVELHEADERFLAG;
			if (lev != styler.LevelAt(lineCurrent)) {
				styler.SetLevel(lineCurrent, lev);
			}
			lineCurrent++;
			levelPrev = levelCurrent;
			visibleChars = 0;
		}
		if (!isspacechar(ch))
			visibleChars++;
	}
	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}

LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc);
<|endoftext|>