2007-11-04 03:51:54 +00:00
/*
Copyright ( C ) 1999 - 2007 id Software , Inc . and contributors .
For a list of contributors , see the accompanying CONTRIBUTORS file .
This file is part of GtkRadiant .
GtkRadiant is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
GtkRadiant is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with GtkRadiant ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <assert.h>
// seems to be required for str.h
# include <glib.h>
# include <glib/gstdio.h>
# include "synapse.h"
# if defined (__linux__) || defined (__APPLE__)
# include <dirent.h>
# endif
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
diagnostic stuff
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
extern " C "
{
static PFN_SYN_PRINTF_VA g_pPrintf = NULL ;
void Set_Syn_Printf ( PFN_SYN_PRINTF_VA pf )
{
g_pPrintf = pf ;
}
# define BUFFER_SIZE 4096
void Syn_Printf ( const char * text , . . . )
{
char buf [ BUFFER_SIZE ] ;
va_list args ;
if ( ! text )
return ;
if ( g_pPrintf )
{
va_start ( args , text ) ;
( * g_pPrintf ) ( text , args ) ;
va_end ( args ) ;
}
else
{
va_start ( args , text ) ;
vsnprintf ( buf , BUFFER_SIZE , text , args ) ;
buf [ BUFFER_SIZE - 1 ] = 0 ;
printf ( buf ) ;
va_end ( args ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
server
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// this must be kept in sync with EAPIType
static const char * APITypeName [ 4 ] =
{
" SYN_UNKNOWN " ,
" SYN_PROVIDE " ,
" SYN_REQUIRE " ,
" SYN_REQUIRE_ANY "
} ;
CSynapseServer : : CSynapseServer ( )
{
mpDoc = NULL ;
m_api_name = NULL ;
m_content = NULL ;
mpFocusedNode = NULL ;
}
CSynapseServer : : ~ CSynapseServer ( )
{
if ( m_api_name )
xmlFree ( m_api_name ) ;
if ( m_content )
g_free ( m_content ) ;
Syn_Printf ( " TODO: free API managers \n " ) ;
}
void CSynapseServer : : AddSearchPath ( char * path )
{
char * pLocalPath = new char [ strlen ( path ) + 1 ] ;
strcpy ( pLocalPath , path ) ;
mSearchPaths . push_front ( pLocalPath ) ;
}
bool CSynapseServer : : Initialize ( const char * conf_file , PFN_SYN_PRINTF_VA pf )
{
// browse the paths to locate all potential modules
Set_Syn_Printf ( pf ) ;
if ( conf_file )
{
// if a config file is specified and we fail to load it, we fail
Syn_Printf ( " loading synapse XML config file '%s' \n " , conf_file ) ;
mpDoc = xmlParseFile ( conf_file ) ;
if ( ! mpDoc )
{
Syn_Printf ( " '%s' invalid/not found \n " , conf_file ) ;
return false ;
}
}
for ( list < char * > : : iterator iPath = mSearchPaths . begin ( ) ; iPath ! = mSearchPaths . end ( ) ; iPath + + )
{
const char * path = * iPath ;
Syn_Printf ( " Synapse Scanning modules path: %s \n " , path ) ;
GDir * dir = g_dir_open ( path , 0 , NULL ) ;
if ( dir ! = NULL )
{
while ( 1 )
{
const gchar * name = g_dir_read_name ( dir ) ;
if ( name = = NULL )
break ;
// too small to be isolated in win32/ and linux/ directories..
# if defined(_WIN32)
const char * ext_so = " .dll " ;
# elif defined (__linux__) || defined (__APPLE__)
const char * ext_so = " .so " ;
# endif
const char * ext = strrchr ( name , ' . ' ) ;
if ( ( ext = = NULL ) | | ( stricmp ( ext , ext_so ) ! = 0 ) )
continue ;
Str newModule ;
newModule . Format ( " %s%s " , path , name ) ;
Syn_Printf ( " Found '%s' \n " , newModule . GetBuffer ( ) ) ;
EnumerateInterfaces ( newModule ) ;
}
g_dir_close ( dir ) ;
}
}
return true ;
}
# if defined(_WIN32)
# define FORMAT_BUFSIZE 2048
const char * CSynapseServer : : FormatGetLastError ( )
{
static char buf [ FORMAT_BUFSIZE ] ;
FormatMessage (
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS ,
NULL ,
GetLastError ( ) ,
MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , // Default language
buf ,
FORMAT_BUFSIZE ,
NULL
) ;
return buf ;
}
void CSynapseServer : : EnumerateInterfaces ( Str & soname )
{
CSynapseClientSlot slot ;
slot . mpDLL = LoadLibrary ( soname . GetBuffer ( ) ) ;
if ( ! slot . mpDLL )
{
Syn_Printf ( " LoadLibrary '%s' failed \n " , soname . GetBuffer ( ) ) ;
Syn_Printf ( " GetLastError: %s " , FormatGetLastError ( ) ) ;
return ;
}
slot . mpEnumerate = ( PFN_SYNAPSE_ENUMERATEINTERFACES ) GetProcAddress ( slot . mpDLL , NAME_SYNAPSE_ENUMERATEINTERFACES ) ;
if ( ! slot . mpEnumerate )
{
Syn_Printf ( " GetProcAddress('%s') failed \n " , NAME_SYNAPSE_ENUMERATEINTERFACES ) ;
Syn_Printf ( " GetLastError: %s " , FormatGetLastError ( ) ) ;
return ;
}
Syn_Printf ( " Enumerate interfaces on '%s' \n " , soname . GetBuffer ( ) ) ;
slot . mpClient = slot . mpEnumerate ( SYNAPSE_VERSION , this ) ;
if ( ! slot . mpClient )
{
Syn_Printf ( " Enumerate interfaces on '%s' returned NULL, unloading. \n " , soname . GetBuffer ( ) ) ;
if ( ! FreeLibrary ( slot . mpDLL ) )
{
Syn_Printf ( " FreeLibrary failed: GetLastError: '%s' \n " , CSynapseServer : : FormatGetLastError ( ) ) ;
}
return ;
}
slot . mFileName = soname ;
mClients . push_front ( slot ) ;
}
void CSynapseClientSlot : : ReleaseSO ( )
{
if ( ! mpDLL )
{
Syn_Printf ( " ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO \n " , mpClient - > GetInfo ( ) ) ;
return ;
}
Syn_Printf ( " FreeLibrary '%s' \n " , mpClient - > GetInfo ( ) ) ;
if ( ! FreeLibrary ( mpDLL ) )
{
Syn_Printf ( " FreeLibrary failed: GetLastError: '%s' \n " , CSynapseServer : : FormatGetLastError ( ) ) ;
}
mpDLL = NULL ;
}
# elif defined(__linux__) || defined (__APPLE__)
void CSynapseServer : : EnumerateInterfaces ( Str & soname )
{
CSynapseClientSlot slot ;
slot . mpDLL = dlopen ( soname . GetBuffer ( ) , RTLD_NOW ) ;
PFN_SYNAPSE_ENUMERATEINTERFACES * pEnumerate ;
if ( ! slot . mpDLL )
{
char * error ;
if ( ( error = ( char * ) dlerror ( ) ) = = NULL )
error = " Unknown " ;
Syn_Printf ( " dlopen '%s' failed \n dlerror: '%s' \n " , soname . GetBuffer ( ) , error ) ;
return ;
}
slot . mpEnumerate = ( PFN_SYNAPSE_ENUMERATEINTERFACES ) dlsym ( slot . mpDLL , NAME_SYNAPSE_ENUMERATEINTERFACES ) ;
if ( ! slot . mpEnumerate )
{
char * error ;
if ( ( error = ( char * ) dlerror ( ) ) = = NULL )
error = " Unknown " ;
Syn_Printf ( " dlsym '%s' failed on shared object '%s' \n dlerror: '%s' \n " , NAME_SYNAPSE_ENUMERATEINTERFACES , soname . GetBuffer ( ) , error ) ;
return ;
}
Syn_Printf ( " Enumerate interfaces on '%s' \n " , soname . GetBuffer ( ) ) ;
slot . mpClient = slot . mpEnumerate ( SYNAPSE_VERSION , this ) ;
if ( ! slot . mpClient )
{
Syn_Printf ( " Enumerate interfaces on '%s' returned NULL, unloading. \n " , soname . GetBuffer ( ) ) ;
if ( dlclose ( slot . mpDLL ) )
{
char * error ;
if ( ( error = ( char * ) dlerror ( ) ) = = NULL )
error = " Unknown " ;
Syn_Printf ( " dlclose failed: dlerror: '%s' \n " , error ) ;
}
return ;
}
slot . mFileName = soname ;
mClients . push_front ( slot ) ;
}
void CSynapseClientSlot : : ReleaseSO ( )
{
if ( ! mpDLL )
{
Syn_Printf ( " ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO \n " , mpClient - > GetInfo ( ) ) ;
return ;
}
Syn_Printf ( " dlclose '%s' \n " , mpClient - > GetInfo ( ) ) ;
if ( dlclose ( mpDLL ) )
{
char * error ;
if ( ( error = ( char * ) dlerror ( ) ) = = NULL )
error = " Unknown " ;
Syn_Printf ( " dlclose failed: dlerror: '%s' \n " , error ) ;
}
mpDLL = NULL ;
}
# endif
void CSynapseServer : : EnumerateBuiltinModule ( CSynapseBuiltinClient * pClient )
{
CSynapseClientSlot slot ;
pClient - > EnumerateInterfaces ( this ) ;
slot . mpClient = pClient ;
slot . mType = SYN_BUILTIN ;
mClients . push_front ( slot ) ;
}
PFN_SYN_PRINTF_VA CSynapseServer : : Get_Syn_Printf ( )
{
return g_pPrintf ;
}
void CSynapseServer : : TryPushStack ( APIDescriptor_t * pAPI )
{
list < APIDescriptor_t * > : : iterator iAPI ;
for ( iAPI = mStack . begin ( ) ; iAPI ! = mStack . end ( ) ; iAPI + + )
{
if ( ( * iAPI ) = = pAPI )
{
return ;
}
}
mStack . push_front ( pAPI ) ;
mbStackChanged = true ;
}
list < CSynapseClientSlot > : : iterator CSynapseServer : : ShutdownClient ( list < CSynapseClientSlot > : : iterator iSlot )
{
CSynapseClientSlot * pClientSlot = & ( * iSlot ) ;
if ( pClientSlot - > mpClient - > IsActive ( ) )
{
// this should not happen except during core shutdown (i.e. editor is shutting down)
Syn_Printf ( " WARNING: ShutdownClient attempted on an active module '%s' \n " , pClientSlot - > mpClient - > GetInfo ( ) ) ;
}
// cleanup mStack
int i , api_count ;
api_count = pClientSlot - > mpClient - > GetAPICount ( ) ;
for ( i = 0 ; i < api_count ; i + + )
{
APIDescriptor_t * pAPI = pClientSlot - > mpClient - > GetAPIDescriptor ( i ) ;
// search this API in mStack
list < APIDescriptor_t * > : : iterator iStack = mStack . begin ( ) ;
while ( iStack ! = mStack . end ( ) )
{
if ( * iStack = = pAPI )
break ;
iStack + + ;
}
if ( iStack ! = mStack . end ( ) )
{
if ( pAPI - > mType = = SYN_REQUIRE )
{
if ( pAPI - > mbTableInitDone )
{
// even if non active, some SYN_REQUIRE may have been filled up
// look for the corresponding SYN_PROVIDE and decref
list < APIDescriptor_t * > : : iterator iStackRequire = mStack . begin ( ) ;
APIDescriptor_t * pMatchAPI ;
while ( iStackRequire ! = mStack . end ( ) )
{
pMatchAPI = * iStackRequire ;
if ( pMatchAPI - > mType = = SYN_PROVIDE & & MatchAPI ( pMatchAPI , pAPI ) )
break ;
iStackRequire + + ;
}
if ( iStackRequire ! = mStack . end ( ) )
{
// we have found the corresponding SYN_PROVIDE
pMatchAPI - > mRefCount - - ;
}
else
{
// this is not supposed to happen at all
Syn_Printf ( " ERROR: couldn't find the SYN_PROVIDE for an initialized SYN_REQUIRE API '%s' '%s' '%s' \n " , pAPI - > major_name , pAPI - > minor_name , pClientSlot - > mpClient - > GetInfo ( ) ) ;
}
}
}
else if ( pAPI - > mType = = SYN_PROVIDE )
{
// this should never happen on non active clients, it may happen during a core shutdown though
// if the mRefCount is != 0, that means there is at least a function table out there that will segfault things
Syn_Printf ( " WARNING: found a SYN_PROVIDE API '%s' '%s' with refcount %d in CSynapseServer::ShutdownClient for '%s' \n " , pAPI - > major_name , pAPI - > minor_name , pAPI - > mRefCount , pClientSlot - > mpClient - > GetInfo ( ) ) ;
}
// mostly safe to remove it now
mStack . erase ( iStack ) ;
}
}
// we can actually release the module now
// NOTE: do we want to have a 'final shutdown' call to the client? (not as long as we don't have a use for it)
if ( pClientSlot - > mType = = SYN_SO )
{
pClientSlot - > ReleaseSO ( ) ;
}
return mClients . erase ( iSlot ) ;
}
void CSynapseServer : : PushRequired ( CSynapseClient * pClient )
{
/* walk through the standard APIs and push them in */
int i , max = pClient - > GetAPICount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
APIDescriptor_t * pAPI = pClient - > GetAPIDescriptor ( i ) ;
if ( pAPI - > mType = = SYN_REQUIRE & & ! pAPI - > mbTableInitDone )
{
TryPushStack ( pAPI ) ;
}
}
/* if this client has 'List' API Manager types, walk through them for addition too */
max = pClient - > GetManagerListCount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
CSynapseAPIManager * pManager = pClient - > GetManagerList ( i ) ;
assert ( pManager - > GetType ( ) = = API_LIST ) ;
pManager - > InitializeAPIList ( ) ;
int j ;
for ( j = 0 ; j < pManager - > GetAPICount ( ) ; j + + )
{
TryPushStack ( pManager - > GetAPI ( j ) ) ;
}
}
/* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces
* and let them decide which ones they might want
*/
max = pClient - > GetManagerMatchCount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
CSynapseAPIManager * pManager = pClient - > GetManagerMatch ( i ) ;
// start matching all known SYN_PROVIDE APIs against this manager
list < CSynapseClientSlot > : : iterator iClientSlot ;
for ( iClientSlot = mClients . begin ( ) ; iClientSlot ! = mClients . end ( ) ; iClientSlot + + )
{
CSynapseClient * pScanClient = ( * iClientSlot ) .
mpClient ;
int j , jmax = pScanClient - > GetAPICount ( ) ;
for ( j = 0 ; j < jmax ; j + + )
{
APIDescriptor_t * pAPI = pScanClient - > GetAPIDescriptor ( j ) ;
if ( pAPI - > mType = = SYN_PROVIDE )
{
if ( pManager - > MatchAPI ( pAPI - > major_name , pAPI - > minor_name ) )
{
/*! we are going to want to load this one
* NOTE TTimo : what if this can not be resolved in the end ?
* if this happens , then the whole startup will fail instead
* or we can use SYN_REQUIRE_ANY and drop it without consequences
*/
APIDescriptor_t * pPushAPI = pManager - > BuildRequireAPI ( pAPI ) ;
TryPushStack ( pPushAPI ) ;
}
}
}
}
}
}
bool CSynapseServer : : MatchAPI ( APIDescriptor_t * p1 , APIDescriptor_t * p2 )
{
return MatchAPI ( p1 - > major_name , p1 - > minor_name , p2 - > major_name , p2 - > minor_name ) ;
}
bool CSynapseServer : : MatchAPI ( const char * major1 , const char * minor1 , const char * major2 , const char * minor2 )
{
if ( strcmp ( major1 , major2 ) ) {
return false ;
}
// either no minor at all for this API, or matching
if ( ( minor1 & & minor2 ) & & ! strcmp ( minor1 , minor2 ) ) {
return true ;
}
// or one of the minors says "*" (knowing that the majors match already)
if ( ( minor1 & & ! strcmp ( minor1 , " * " ) ) | | ( minor2 & & ! strcmp ( minor2 , " * " ) ) ) {
return true ;
}
return false ;
}
bool CSynapseServer : : ResolveAPI ( APIDescriptor_t * pAPI )
{
//Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);
// loop through active clients, search for a client providing what we are looking for
list < CSynapseClientSlot > : : iterator iClient ;
for ( iClient = mClients . begin ( ) ; iClient ! = mClients . end ( ) ; iClient + + )
{
// walk through interfaces on this client for a match
CSynapseClient * pScanClient = ( * iClient ) . mpClient ;
int i , max = pScanClient - > GetAPICount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
APIDescriptor_t * pScanClientAPI = pScanClient - > GetAPIDescriptor ( i ) ;
if ( pScanClientAPI - > mType = = SYN_PROVIDE )
{
if ( MatchAPI ( pAPI , pScanClientAPI ) )
{
// can this client provide APIs yet
// it is possible that all of it's APIs have been filled and it's not been activated yet
pScanClient - > CheckSetActive ( ) ;
if ( pScanClient - > IsActive ( ) )
{
// make sure this interface has correct size (this is our version check)
if ( pAPI - > mSize ! = pScanClientAPI - > mSize )
{
Syn_Printf ( " ERROR: version mismatch for API '%s' '%s' found in '%s' (size %d != %d) \n " , pAPI - > major_name , pAPI - > minor_name , pScanClient - > GetInfo ( ) , pAPI - > mSize , pScanClientAPI - > mSize ) ;
Syn_Printf ( " the module and the server are incompatible \n " ) ;
// keep going to other APIs
continue ;
}
// this is an active client, we can request
# ifdef SYNAPSE_VERBOSE
Syn_Printf ( " RequestAPI '%s' '%s' from '%s' for API %p \n " , pAPI - > major_name , pAPI - > minor_name , pScanClient - > GetInfo ( ) , pAPI ) ;
# endif
if ( ! pScanClient - > RequestAPI ( pAPI ) )
{
// this should never happen, means we think this module provides the API, but it answers that it doesn't
Syn_Printf ( " ERROR: RequestAPI failed \n " ) ;
return false ;
}
pScanClientAPI - > mRefCount + + ;
pAPI - > mbTableInitDone = true ;
return true ; // job done
}
else
{
// this client is not active yet, some of it's required interfaces are not filled in
PushRequired ( pScanClient ) ;
// we will exit the scan through the APIDescriptor of this client and look at other clients
break ;
}
}
}
}
}
return false ;
}
bool CSynapseServer : : DoResolve ( CSynapseClient * pClient )
{
list < CSynapseClientSlot > : : iterator iSlot ;
for ( iSlot = mClients . begin ( ) ; iSlot ! = mClients . end ( ) ; iSlot + + )
{
if ( ( * iSlot ) . mpClient = = pClient )
break ;
}
if ( iSlot = = mClients . end ( ) )
{
Syn_Printf ( " CSynapserServer::Resolve adding new client slot '%s' \n " , pClient - > GetInfo ( ) ) ;
CSynapseClientSlot slot ;
slot . mpClient = pClient ;
slot . mFileName = " local client " ;
// make it active so we can request the interfaces already
pClient - > ForceSetActive ( ) ;
mClients . push_front ( slot ) ;
}
else
{
// make it active so we can request the interfaces already
( * iSlot ) . mpClient - > ForceSetActive ( ) ;
}
// push the interfaces that need to be resolved for this client
// NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces
PushRequired ( pClient ) ;
// start resolving now
// working till the stack is emptied or till we reach a dead end situation
// we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions
list < APIDescriptor_t * > : : iterator iCurrent ;
mbStackChanged = true ; // init to true so we try the first elem
while ( ! mStack . empty ( ) )
{
//DumpStack();
if ( ! mbStackChanged )
{
// the stack didn't change last loop
iCurrent + + ;
if ( iCurrent = = mStack . end ( ) )
{
Syn_Printf ( " ERROR: CSynapseServer::Resolve, failed to resolve \n " ) ;
DumpStack ( ) ;
return false ;
}
if ( ResolveAPI ( * iCurrent ) )
{
iCurrent = mStack . erase ( iCurrent ) ;
mbStackChanged = true ;
}
}
else
{
// the stack changed at last loop
mbStackChanged = false ;
iCurrent = mStack . begin ( ) ;
if ( ResolveAPI ( * iCurrent ) )
{
iCurrent = mStack . erase ( iCurrent ) ;
mbStackChanged = true ;
}
}
}
return true ;
}
bool CSynapseServer : : Resolve ( CSynapseClient * pClient )
{
bool ret = DoResolve ( pClient ) ;
list < CSynapseClientSlot > : : iterator iClient ;
iClient = mClients . begin ( ) ;
while ( iClient ! = mClients . end ( ) )
{
CSynapseClient * pClient = ( * iClient ) . mpClient ;
if ( ! pClient - > IsActive ( ) )
{
Syn_Printf ( " Unloading an unused module: '%s' \n " , pClient - > GetInfo ( ) ) ;
iClient = ShutdownClient ( iClient ) ;
}
else
iClient + + ;
}
return ret ;
}
void CSynapseServer : : Shutdown ( )
{
Syn_Printf ( " Synapse server core is shutting down \n " ) ;
// do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)
// we seperate the client shutdown calls from the dlclose cause that part is a clean decref / free situation whereas dlclose will break links without advice
list < CSynapseClientSlot > : : iterator iClient ;
iClient = mClients . begin ( ) ;
for ( iClient = mClients . begin ( ) ; iClient ! = mClients . end ( ) ; iClient + + )
{
( * iClient ) . mpClient - > Shutdown ( ) ;
}
// now release them from the server's point of view
iClient = mClients . begin ( ) ;
while ( iClient ! = mClients . end ( ) )
{
iClient = ShutdownClient ( iClient ) ;
}
}
void CSynapseServer : : DumpStack ( )
{
list < APIDescriptor_t * > : : iterator iCurrent ;
for ( iCurrent = mStack . begin ( ) ; iCurrent ! = mStack . end ( ) ; iCurrent + + )
{
APIDescriptor_t * pAPI = * iCurrent ;
Syn_Printf ( " interface %s %p '%s' '%s' \n " , APITypeName [ pAPI - > mType ] , pAPI , pAPI - > major_name , pAPI - > minor_name ) ;
}
}
void CSynapseServer : : DumpActiveClients ( )
{
list < CSynapseClientSlot > : : iterator iClient ;
for ( iClient = mClients . begin ( ) ; iClient ! = mClients . end ( ) ; iClient + + )
{
CSynapseClient * pClient = ( * iClient ) . mpClient ;
Syn_Printf ( " %s " , pClient - > GetInfo ( ) ) ;
if ( pClient - > IsActive ( ) )
Syn_Printf ( " \n " ) ;
else
Syn_Printf ( " (not active) \n " ) ;
}
}
bool CSynapseServer : : SelectClientConfig ( const char * client_name )
{
if ( ! mpDoc )
return false ;
xmlNodePtr pNode = xmlDocGetRootElement ( mpDoc ) ;
if ( ! pNode )
return false ;
// look for the client
pNode = pNode - > children ;
while ( pNode )
{
if ( pNode - > type = = XML_ELEMENT_NODE )
{
xmlChar * prop = xmlGetProp ( pNode , ( const xmlChar * ) " name " ) ;
if ( ! strcmp ( ( const char * ) prop , client_name ) )
{
xmlFree ( prop ) ;
break ;
}
xmlFree ( prop ) ;
}
pNode = pNode - > next ;
}
if ( ! pNode )
return false ; // config you asked for isn't there
// focus
mpFocusedNode = pNode - > children ;
mpCurrentClientConfig = pNode ;
return true ;
}
bool CSynapseServer : : GetNextConfig ( char * * api_name , char * * minor )
{
while ( mpFocusedNode & & mpFocusedNode - > name )
{
if ( mpFocusedNode - > type = = XML_ELEMENT_NODE & & ! strcmp ( ( const char * ) mpFocusedNode - > name , " api " ) )
{
if ( m_api_name )
xmlFree ( m_api_name ) ;
m_api_name = xmlGetProp ( mpFocusedNode , ( const xmlChar * ) " name " ) ;
* api_name = ( char * ) m_api_name ;
if ( m_content )
g_free ( m_content ) ;
m_content = g_strdup ( ( const gchar * ) mpFocusedNode - > children - > content ) ;
g_strstrip ( m_content ) ;
* minor = m_content ;
mpFocusedNode = mpFocusedNode - > next ;
return true ;
}
mpFocusedNode = mpFocusedNode - > next ;
}
return false ;
}
bool CSynapseServer : : GetConfigForAPI ( const char * api , char * * minor ) {
xmlNodePtr pNode = mpCurrentClientConfig - > children ;
while ( pNode & & pNode - > name ) {
if ( pNode - > type = = XML_ELEMENT_NODE & & ! strcmp ( ( const char * ) pNode - > name , " api " ) ) {
if ( m_api_name ) {
xmlFree ( m_api_name ) ;
}
m_api_name = xmlGetProp ( pNode , ( const xmlChar * ) " name " ) ;
if ( ! strcmp ( ( const char * ) m_api_name , api ) ) {
if ( m_content ) {
g_free ( m_content ) ;
}
m_content = g_strdup ( ( const gchar * ) pNode - > children - > content ) ;
g_strstrip ( m_content ) ;
* minor = m_content ;
return true ;
}
}
pNode = pNode - > next ;
}
return false ;
}
const char * CSynapseServer : : GetModuleFilename ( CSynapseClient * pClient )
{
list < CSynapseClientSlot > : : iterator iSlot ;
for ( iSlot = mClients . begin ( ) ; iSlot ! = mClients . end ( ) ; iSlot + + )
{
if ( ( * iSlot ) . mpClient = = pClient )
{
if ( ( * iSlot ) . mType = = SYN_BUILTIN )
{
return " " ; // FIXME
}
else
{
return ( * iSlot ) . mFileName ;
}
}
}
return NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
client
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
CSynapseClient : : CSynapseClient ( )
{
}
void CSynapseClient : : Shutdown ( )
{
vector < APIDescriptor_t * > : : iterator iAPI ;
for ( iAPI = mAPIDescriptors . begin ( ) ; iAPI ! = mAPIDescriptors . end ( ) ; iAPI + + )
{
APIDescriptor_t * pAPI = * iAPI ;
if ( pAPI - > mRefCount ! = 0 )
Syn_Printf ( " WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s' \n " , GetInfo ( ) , pAPI - > major_name , pAPI - > minor_name ) ;
else
delete pAPI ;
* iAPI = NULL ;
}
mAPIDescriptors . clear ( ) ;
vector < CSynapseAPIManager * > : : iterator iManager ;
for ( iManager = mManagersList . begin ( ) ; iManager ! = mManagersList . end ( ) ; iManager + + )
{
CSynapseAPIManager * pManager = * iManager ;
pManager - > DecRef ( ) ;
* iManager = NULL ;
}
mManagersList . clear ( ) ;
for ( iManager = mManagersMatch . begin ( ) ; iManager ! = mManagersMatch . end ( ) ; iManager + + )
{
CSynapseAPIManager * pManager = * iManager ;
pManager - > DecRef ( ) ;
* iManager = NULL ;
}
mManagersMatch . clear ( ) ;
}
CSynapseClient : : ~ CSynapseClient ( )
{
// this should not be doing anything when called from here if everything went right
// otherwise it's likely to crash .. at least that's the sign we missed something
Shutdown ( ) ;
}
int CSynapseClient : : GetAPICount ( )
{
return mAPIDescriptors . size ( ) ;
}
APIDescriptor_t * CSynapseClient : : GetAPIDescriptor ( int i )
{
return mAPIDescriptors [ i ] ;
}
int CSynapseClient : : GetManagerMatchCount ( )
{
return mManagersMatch . size ( ) ;
}
CSynapseAPIManager * CSynapseClient : : GetManagerMatch ( int i )
{
return mManagersMatch [ i ] ;
}
int CSynapseClient : : GetManagerListCount ( )
{
return mManagersList . size ( ) ;
}
CSynapseAPIManager * CSynapseClient : : GetManagerList ( int i )
{
return mManagersList [ i ] ;
}
bool CSynapseClient : : AddAPI ( const char * major , const char * minor , int size , EAPIType type , void * pTable )
{
// do some safe checks before actual addition
if ( type = = SYN_REQUIRE & & ! pTable )
{
Syn_Printf ( " ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer \n " , major , minor , GetInfo ( ) ) ;
return false ;
}
if ( pTable )
{
int * pi = ( int * ) pTable ;
if ( pi = = 0 )
{
Syn_Printf ( " ERROR: forgot to init function table size for interface '%s' '%s' from '%s'? \n " , major , minor , GetInfo ( ) ) ;
return false ;
}
}
APIDescriptor_t * pAPI = new APIDescriptor_t ;
memset ( pAPI , 0 , sizeof ( APIDescriptor_t ) ) ;
strncpy ( pAPI - > major_name , major , MAX_APINAME ) ;
if ( minor )
strncpy ( pAPI - > minor_name , minor , MAX_APINAME ) ;
pAPI - > mType = type ;
pAPI - > mpTable = pTable ;
// store the interface size
if ( type = = SYN_PROVIDE )
{
if ( size = = 0 )
{
Syn_Printf ( " ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s') \n " , major , minor , GetInfo ( ) ) ;
delete pAPI ;
return false ;
}
pAPI - > mSize = size ;
}
else if ( type = = SYN_REQUIRE )
{
if ( size ! = 0 )
{
// if a non-zero value is given in function call, use this instead of the val in table
* ( ( int * ) pAPI - > mpTable ) = size ;
pAPI - > mSize = size ;
}
else
{
pAPI - > mSize = * ( ( int * ) pAPI - > mpTable ) ;
if ( pAPI - > mSize = = 0 )
{
Syn_Printf ( " ERROR: didn't get an interface size ('%s' '%s' from '%s') \n " , major , minor , GetInfo ( ) ) ;
delete pAPI ;
return false ;
}
}
}
else
{
Syn_Printf ( " ERROR: AddAPI type '%d' not supported \n " , type ) ;
return false ;
}
mAPIDescriptors . push_back ( pAPI ) ;
# ifdef SYNAPSE_VERBOSE
Syn_Printf ( " AddAPI: %s %p '%s' '%s' from '%s', size %d \n " , APITypeName [ pAPI - > mType ] , pAPI , major , minor , GetInfo ( ) , pAPI - > mSize ) ;
# endif
return true ;
}
# include "version.h"
const char * CSynapseClient : : GetInfo ( )
{
return " CSynapseClient built " __DATE__ " " RADIANT_VERSION ;
}
bool CSynapseClient : : CheckSetActive ( )
{
if ( mbActive )
return true ;
int i , max = GetAPICount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
APIDescriptor_t * pAPI = GetAPIDescriptor ( i ) ;
if ( pAPI - > mType = = SYN_REQUIRE & & ! pAPI - > mbTableInitDone )
return false ;
}
// if we have managers with fixed list, those need to be completely filled in too
vector < CSynapseAPIManager * > : : iterator iManager ;
for ( iManager = mManagersList . begin ( ) ; iManager ! = mManagersList . end ( ) ; iManager + + )
{
if ( ! ( * iManager ) - > CheckSetActive ( ) )
return false ; // one of the managers doesn't have all it needs yet
}
// call OnActivate to let the client perform last minute checks
// NOTE: this should be fatal instead of letting the engine try other combinations
if ( ! OnActivate ( ) ) {
return false ;
}
// yes, all required interfaces have been initialized
Syn_Printf ( " '%s' activated \n " , GetInfo ( ) ) ;
mbActive = true ;
return true ;
}
bool CSynapseClient : : ConfigXML ( CSynapseServer * pServer , const char * client_name , const XMLConfigEntry_t entries [ ] ) {
if ( ! client_name ) {
client_name = GetName ( ) ;
}
Syn_Printf ( " Dynamic APIs for client '%s' \n " , GetInfo ( ) ) ;
if ( ! pServer - > SelectClientConfig ( client_name ) )
{
Syn_Printf ( " Failed to select synapse client config '%s' \n " , client_name ) ;
return false ;
}
int i = 0 ;
while ( entries [ i ] . type ! = SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty
char * minor ;
if ( ! pServer - > GetConfigForAPI ( entries [ i ] . api , & minor ) ) {
Syn_Printf ( " GetConfigForAPI '%s' failed - invalid XML config file? \n " , entries [ i ] . api ) ;
return false ;
}
AddAPI ( entries [ i ] . api , minor , entries [ i ] . size , entries [ i ] . type , entries [ i ] . pTable ) ;
i + + ;
}
Syn_Printf ( " %d dynamic interfaces parsed for '%s' \n " , i , client_name ) ;
return true ;
}
void CSynapseClient : : AddManager ( CSynapseAPIManager * pManager )
{
pManager - > IncRef ( ) ;
if ( pManager - > GetType ( ) = = API_LIST )
mManagersList . push_back ( pManager ) ;
else
mManagersMatch . push_back ( pManager ) ;
}
CSynapseAPIManager : : ~ CSynapseAPIManager ( )
{
vector < APIDescriptor_t * > : : iterator iAPI ;
for ( iAPI = mAPIs . begin ( ) ; iAPI ! = mAPIs . end ( ) ; iAPI + + )
{
APIDescriptor_t * pAPI = * iAPI ;
if ( pAPI - > mRefCount ! = 0 )
{
Syn_Printf ( " WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s' \n " , pAPI - > major_name , pAPI - > minor_name ) ;
}
delete pAPI ;
* iAPI = NULL ;
}
}
APIDescriptor_t * CSynapseAPIManager : : PrepareRequireAPI ( APIDescriptor_t * pAPI )
{
# ifdef _DEBUG
if ( pAPI - > mType ! = SYN_PROVIDE )
{
Syn_Printf ( " ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI \n " ) ;
return NULL ;
}
# endif
APIDescriptor_t * pRequireAPI = new APIDescriptor_t ;
memcpy ( pRequireAPI , pAPI , sizeof ( APIDescriptor_t ) ) ;
pRequireAPI - > mType = SYN_REQUIRE_ANY ;
pRequireAPI - > mpTable = NULL ;
pRequireAPI - > mbTableInitDone = false ;
pRequireAPI - > mSize = 0 ; // this will have to be set correctly by the child for version checking
pRequireAPI - > mRefCount = 0 ;
return pRequireAPI ;
}
void CSynapseAPIManager : : SetMatchAPI ( const char * major , const char * minor )
{
if ( strlen ( minor ) > MAX_PATTERN_STRING )
{
Syn_Printf ( " ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s' \n " , minor ) ;
return ;
}
strcpy ( major_pattern , major ) ;
strcpy ( minor_pattern , minor ) ;
if ( strcmp ( minor , " * " ) )
{
mType = API_LIST ;
}
}
bool CSynapseAPIManager : : MatchAPI ( const char * major , const char * minor )
{
assert ( mType = = API_MATCH ) ;
/*!
if this interface has been allocated already , avoid requesting it again . .
*/
vector < APIDescriptor_t * > : : iterator iAPI ;
for ( iAPI = mAPIs . begin ( ) ; iAPI ! = mAPIs . end ( ) ; iAPI + + )
{
if ( CSynapseServer : : MatchAPI ( ( * iAPI ) - > major_name , ( * iAPI ) - > minor_name , major , minor ) )
return false ;
}
if ( ! strcmp ( major , major_pattern ) )
return true ;
return false ;
}
bool CSynapseAPIManager : : CheckSetActive ( )
{
if ( mType = = API_MATCH )
return false ;
// mType == API_LIST
int i , max = GetAPICount ( ) ;
for ( i = 0 ; i < max ; i + + )
{
if ( ! GetAPI ( i ) - > mbTableInitDone )
return false ;
}
return true ;
}
void CSynapseAPIManager : : InitializeAPIList ( )
{
char minor_tok [ MAX_PATTERN_STRING ] ;
char * token ;
if ( mAPIs . size ( ) )
{
Syn_Printf ( " WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager \n " ) ;
return ;
}
strncpy ( minor_tok , minor_pattern , MAX_PATTERN_STRING ) ;
token = strtok ( minor_tok , " " ) ;
while ( token )
{
/* ask the child to build from scratch */
APIDescriptor_t * pAPI = new APIDescriptor_t ;
memset ( pAPI , 0 , sizeof ( APIDescriptor_t ) ) ;
strncpy ( pAPI - > major_name , major_pattern , MAX_APINAME ) ;
strncpy ( pAPI - > minor_name , token , MAX_APINAME ) ;
pAPI - > mType = SYN_REQUIRE_ANY ;
FillAPITable ( pAPI ) ;
mAPIs . push_back ( pAPI ) ;
token = strtok ( NULL , " " ) ;
}
}
int CSynapseAPIManager : : GetAPICount ( )
{
return mAPIs . size ( ) ;
}
APIDescriptor_t * CSynapseAPIManager : : GetAPI ( int i )
{
return mAPIs [ i ] ;
}
// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=879
void fini_stub ( ) {
printf ( " fini_stub \n " ) ;
}