]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/synapse/synapse.cpp
fix warning: format not a string literal and no format arguments
[xonotic/netradiant.git] / libs / synapse / synapse.cpp
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include <assert.h>
23
24 // seems to be required for str.h
25 #include <glib.h>
26 #include <glib/gstdio.h>
27
28 #include "synapse.h"
29 #if defined ( __linux__ ) || defined ( __APPLE__ )
30   #include <dirent.h>
31 #endif
32
33 /*
34    ===================================================
35    diagnostic stuff
36    ===================================================
37  */
38
39 extern "C"
40 {
41
42 static PFN_SYN_PRINTF_VA g_pPrintf = NULL;
43
44 void Set_Syn_Printf( PFN_SYN_PRINTF_VA pf ){
45         g_pPrintf = pf;
46 }
47
48 #define BUFFER_SIZE 4096
49
50 void Syn_Printf( const char *text, ... ){
51         char buf[BUFFER_SIZE];
52         va_list args;
53
54         if ( !text ) {
55                 return;
56         }
57
58         if ( g_pPrintf ) {
59                 va_start( args, text );
60                 ( *g_pPrintf )( text, args );
61                 va_end( args );
62         }
63         else
64         {
65                 va_start( args, text );
66                 vsnprintf( buf, BUFFER_SIZE, text, args );
67                 buf[BUFFER_SIZE - 1] = 0;
68                 printf( "%s", buf );
69                 va_end( args );
70         }
71 }
72
73 }
74
75 /*
76    =======================================================================
77    server
78    =======================================================================
79  */
80
81 // this must be kept in sync with EAPIType
82 static const char* APITypeName[4] =
83 {
84         "SYN_UNKNOWN",
85         "SYN_PROVIDE",
86         "SYN_REQUIRE",
87         "SYN_REQUIRE_ANY"
88 };
89
90 CSynapseServer::CSynapseServer(){
91         mpDoc = NULL;
92         m_api_name = NULL;
93         m_content = NULL;
94         mpFocusedNode = NULL;
95 }
96
97 CSynapseServer::~CSynapseServer(){
98         if ( m_api_name ) {
99                 xmlFree( m_api_name );
100         }
101         if ( m_content ) {
102                 g_free( m_content );
103         }
104         Syn_Printf( "TODO: free API managers\n" );
105 }
106
107 void CSynapseServer::AddSearchPath( char* path ){
108         char *pLocalPath = new char[strlen( path ) + 1];
109         strcpy( pLocalPath, path );
110         mSearchPaths.push_front( pLocalPath );
111 }
112
113 bool CSynapseServer::Initialize( const char* conf_file, PFN_SYN_PRINTF_VA pf ){
114         // browse the paths to locate all potential modules
115
116         Set_Syn_Printf( pf );
117
118         if ( conf_file ) {
119                 // if a config file is specified and we fail to load it, we fail
120                 Syn_Printf( "loading synapse XML config file '%s'\n", conf_file );
121                 mpDoc = xmlParseFile( conf_file );
122                 if ( !mpDoc ) {
123                         Syn_Printf( "'%s' invalid/not found\n", conf_file );
124                         return false;
125                 }
126         }
127
128         for ( list<char *>::iterator iPath = mSearchPaths.begin(); iPath != mSearchPaths.end(); iPath++ )
129         {
130                 const char* path = *iPath;
131
132                 Syn_Printf( "Synapse Scanning modules path: %s\n", path );
133
134                 GDir* dir = g_dir_open( path, 0, NULL );
135
136                 if ( dir != NULL ) {
137                         while ( 1 )
138                         {
139                                 const gchar* name = g_dir_read_name( dir );
140                                 if ( name == NULL ) {
141                                         break;
142                                 }
143
144                                 // too small to be isolated in win32/ and linux/ directories..
145 #if defined( _WIN32 )
146                                 const char* ext_so = ".dll";
147 #elif defined ( __linux__ ) || defined ( __APPLE__ )
148                                 const char* ext_so = ".so";
149 #endif
150                                 const char* ext = strrchr( name, '.' );
151                                 if ( ( ext == NULL ) || ( stricmp( ext, ext_so ) != 0 ) ) {
152                                         continue;
153                                 }
154
155                                 Str newModule;
156                                 newModule.Format( "%s%s", path, name );
157                                 Syn_Printf( "Found '%s'\n", newModule.GetBuffer() );
158                                 EnumerateInterfaces( newModule );
159                         }
160
161                         g_dir_close( dir );
162                 }
163         }
164         return true;
165 }
166
167 #if defined( _WIN32 )
168 #define FORMAT_BUFSIZE 2048
169 const char* CSynapseServer::FormatGetLastError(){
170         static char buf[FORMAT_BUFSIZE];
171         FormatMessage(
172                 FORMAT_MESSAGE_FROM_SYSTEM |
173                 FORMAT_MESSAGE_IGNORE_INSERTS,
174                 NULL,
175                 GetLastError(),
176                 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
177                 buf,
178                 FORMAT_BUFSIZE,
179                 NULL
180                 );
181         return buf;
182 }
183
184 void CSynapseServer::EnumerateInterfaces( Str &soname ){
185         CSynapseClientSlot slot;
186         slot.mpDLL = LoadLibrary( soname.GetBuffer() );
187         if ( !slot.mpDLL ) {
188                 Syn_Printf( "LoadLibrary '%s' failed\n", soname.GetBuffer() );
189                 Syn_Printf( "  GetLastError: %s", FormatGetLastError() );
190                 return;
191         }
192         slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)GetProcAddress( slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES );
193         if ( !slot.mpEnumerate ) {
194                 Syn_Printf( "GetProcAddress('%s') failed\n", NAME_SYNAPSE_ENUMERATEINTERFACES );
195                 Syn_Printf( "  GetLastError: %s", FormatGetLastError() );
196                 return;
197         }
198         Syn_Printf( "Enumerate interfaces on '%s'\n", soname.GetBuffer() );
199         slot.mpClient = slot.mpEnumerate( SYNAPSE_VERSION, this );
200         if ( !slot.mpClient ) {
201                 Syn_Printf( "Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer() );
202                 if ( !FreeLibrary( slot.mpDLL ) ) {
203                         Syn_Printf( " FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError() );
204                 }
205                 return;
206         }
207         slot.mFileName = soname;
208         mClients.push_front( slot );
209 }
210
211 void CSynapseClientSlot::ReleaseSO(){
212         if ( !mpDLL ) {
213                 Syn_Printf( "ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo() );
214                 return;
215         }
216         Syn_Printf( "FreeLibrary '%s'\n", mpClient->GetInfo() );
217         if ( !FreeLibrary( mpDLL ) ) {
218                 Syn_Printf( " FreeLibrary failed: GetLastError: '%s'\n", CSynapseServer::FormatGetLastError() );
219         }
220         mpDLL = NULL;
221 }
222
223 #elif defined( __linux__ ) || defined ( __APPLE__ )
224 void CSynapseServer::EnumerateInterfaces( Str &soname ){
225         CSynapseClientSlot slot;
226         slot.mpDLL = dlopen( soname.GetBuffer(), RTLD_NOW );
227         if ( !slot.mpDLL ) {
228                 const char* error;
229                 if ( ( error = (char *)dlerror() ) == NULL ) {
230                         error = "Unknown";
231                 }
232                 Syn_Printf( "dlopen '%s' failed\n  dlerror: '%s'\n", soname.GetBuffer(), error );
233                 return;
234         }
235         slot.mpEnumerate = (PFN_SYNAPSE_ENUMERATEINTERFACES)dlsym( slot.mpDLL, NAME_SYNAPSE_ENUMERATEINTERFACES );
236         if ( !slot.mpEnumerate ) {
237                 const char* error;
238                 if ( ( error = (char *)dlerror() ) == NULL ) {
239                         error = "Unknown";
240                 }
241                 Syn_Printf( "dlsym '%s' failed on shared object '%s'\n  dlerror: '%s'\n", NAME_SYNAPSE_ENUMERATEINTERFACES, soname.GetBuffer(), error );
242                 return;
243         }
244         Syn_Printf( "Enumerate interfaces on '%s'\n", soname.GetBuffer() );
245         slot.mpClient = slot.mpEnumerate( SYNAPSE_VERSION, this );
246         if ( !slot.mpClient ) {
247                 Syn_Printf( "Enumerate interfaces on '%s' returned NULL, unloading.\n", soname.GetBuffer() );
248                 if ( dlclose( slot.mpDLL ) ) {
249                         const char* error;
250                         if ( ( error = (char *)dlerror() ) == NULL ) {
251                                 error = "Unknown";
252                         }
253                         Syn_Printf( "  dlclose failed: dlerror: '%s'\n", error );
254                 }
255                 return;
256         }
257         slot.mFileName = soname;
258         mClients.push_front( slot );
259 }
260
261 void CSynapseClientSlot::ReleaseSO(){
262         if ( !mpDLL ) {
263                 Syn_Printf( "ERROR: no shared object handle for client '%s' in CSynapseClientSlot::ReleaseSO\n", mpClient->GetInfo() );
264                 return;
265         }
266         Syn_Printf( "dlclose '%s'\n", mpClient->GetInfo() );
267         if ( dlclose( mpDLL ) ) {
268                 const char* error;
269                 if ( ( error = (char *)dlerror() ) == NULL ) {
270                         error = "Unknown";
271                 }
272                 Syn_Printf( "  dlclose failed: dlerror: '%s'\n", error );
273         }
274         mpDLL = NULL;
275 }
276
277 #endif
278
279 void CSynapseServer::EnumerateBuiltinModule( CSynapseBuiltinClient *pClient ){
280         CSynapseClientSlot slot;
281         pClient->EnumerateInterfaces( this );
282         slot.mpClient = pClient;
283         slot.mType = SYN_BUILTIN;
284         mClients.push_front( slot );
285 }
286
287 PFN_SYN_PRINTF_VA CSynapseServer::Get_Syn_Printf(){
288         return g_pPrintf;
289 }
290
291 void CSynapseServer::TryPushStack( APIDescriptor_t *pAPI ){
292         list<APIDescriptor_t*>::iterator iAPI;
293         for ( iAPI = mStack.begin(); iAPI != mStack.end(); iAPI++ )
294         {
295                 if ( ( *iAPI ) == pAPI ) {
296                         return;
297                 }
298         }
299         mStack.push_front( pAPI );
300         mbStackChanged = true;
301 }
302
303 list<CSynapseClientSlot>::iterator CSynapseServer::ShutdownClient( list<CSynapseClientSlot>::iterator iSlot ){
304         CSynapseClientSlot *pClientSlot = &( *iSlot );
305         if ( pClientSlot->mpClient->IsActive() ) {
306                 // this should not happen except during core shutdown (i.e. editor is shutting down)
307                 Syn_Printf( "WARNING: ShutdownClient attempted on an active module '%s'\n", pClientSlot->mpClient->GetInfo() );
308         }
309         // cleanup mStack
310         int i,api_count;
311         api_count = pClientSlot->mpClient->GetAPICount();
312         for ( i = 0; i < api_count; i++ )
313         {
314                 APIDescriptor_t *pAPI = pClientSlot->mpClient->GetAPIDescriptor( i );
315                 // search this API in mStack
316                 list< APIDescriptor_t *>::iterator iStack = mStack.begin();
317                 while ( iStack != mStack.end() )
318                 {
319                         if ( *iStack == pAPI ) {
320                                 break;
321                         }
322                         iStack++;
323                 }
324                 if ( iStack != mStack.end() ) {
325                         if ( pAPI->mType == SYN_REQUIRE ) {
326                                 if ( pAPI->mbTableInitDone ) {
327                                         // even if non active, some SYN_REQUIRE may have been filled up
328                                         // look for the corresponding SYN_PROVIDE and decref
329                                         list< APIDescriptor_t *>::iterator iStackRequire = mStack.begin();
330                                         APIDescriptor_t *pMatchAPI;
331                                         while ( iStackRequire != mStack.end() )
332                                         {
333                                                 pMatchAPI = *iStackRequire;
334                                                 if ( pMatchAPI->mType == SYN_PROVIDE && MatchAPI( pMatchAPI, pAPI ) ) {
335                                                         break;
336                                                 }
337                                                 iStackRequire++;
338                                         }
339                                         if ( iStackRequire != mStack.end() ) {
340                                                 // we have found the corresponding SYN_PROVIDE
341                                                 pMatchAPI->mRefCount--;
342                                         }
343                                         else
344                                         {
345                                                 // this is not supposed to happen at all
346                                                 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() );
347                                         }
348                                 }
349                         }
350                         else if ( pAPI->mType == SYN_PROVIDE ) {
351                                 // this should never happen on non active clients, it may happen during a core shutdown though
352                                 // if the mRefCount is != 0, that means there is at least a function table out there that will segfault things
353                                 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() );
354                         }
355                         // mostly safe to remove it now
356                         mStack.erase( iStack );
357                 }
358         }
359         // we can actually release the module now
360         // 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)
361         if ( pClientSlot->mType == SYN_SO ) {
362                 pClientSlot->ReleaseSO();
363         }
364         return mClients.erase( iSlot );
365 }
366
367 void CSynapseServer::PushRequired( CSynapseClient *pClient ){
368         /* walk through the standard APIs and push them in */
369         int i,max = pClient->GetAPICount();
370         for ( i = 0; i < max; i++ )
371         {
372                 APIDescriptor_t* pAPI = pClient->GetAPIDescriptor( i );
373                 if ( pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone ) {
374                         TryPushStack( pAPI );
375                 }
376         }
377
378         /* if this client has 'List' API Manager types, walk through them for addition too */
379         max = pClient->GetManagerListCount();
380         for ( i = 0; i < max; i++ )
381         {
382                 CSynapseAPIManager *pManager = pClient->GetManagerList( i );
383                 assert( pManager->GetType() == API_LIST );
384                 pManager->InitializeAPIList();
385                 int j;
386                 for ( j = 0; j < pManager->GetAPICount(); j++ )
387                 {
388                         TryPushStack( pManager->GetAPI( j ) );
389                 }
390         }
391
392         /* if there are loose match managers, prompt them against the current list of SYN_PROVIDE interfaces
393          * and let them decide which ones they might want
394          */
395
396         max = pClient->GetManagerMatchCount();
397
398         for ( i = 0; i < max; i++ )
399         {
400                 CSynapseAPIManager *pManager = pClient->GetManagerMatch( i );
401                 // start matching all known SYN_PROVIDE APIs against this manager
402                 list<CSynapseClientSlot>::iterator iClientSlot;
403                 for ( iClientSlot = mClients.begin(); iClientSlot != mClients.end(); iClientSlot++ )
404                 {
405                         CSynapseClient *pScanClient = ( *iClientSlot ).
406                                                                                   mpClient;
407                         int j,jmax = pScanClient->GetAPICount();
408                         for ( j = 0; j < jmax; j++ )
409                         {
410                                 APIDescriptor_t *pAPI = pScanClient->GetAPIDescriptor( j );
411                                 if ( pAPI->mType == SYN_PROVIDE ) {
412                                         if ( pManager->MatchAPI( pAPI->major_name, pAPI->minor_name ) ) {
413                                                 /*! we are going to want to load this one
414                                                  * NOTE TTimo: what if this can not be resolved in the end?
415                                                  * if this happens, then the whole startup will fail instead
416                                                  * or we can use SYN_REQUIRE_ANY and drop it without consequences
417                                                  */
418                                                 APIDescriptor_t *pPushAPI = pManager->BuildRequireAPI( pAPI );
419                                                 TryPushStack( pPushAPI );
420                                         }
421                                 }
422                         }
423                 }
424         }
425 }
426
427 bool CSynapseServer::MatchAPI( APIDescriptor_t *p1, APIDescriptor_t *p2 ){
428         return MatchAPI( p1->major_name, p1->minor_name, p2->major_name, p2->minor_name );
429 }
430
431 bool CSynapseServer::MatchAPI( const char* major1, const char* minor1, const char* major2, const char* minor2 ){
432         if ( strcmp( major1, major2 ) ) {
433                 return false;
434         }
435         // either no minor at all for this API, or matching
436         if ( ( minor1 && minor2 ) && !strcmp( minor1, minor2 ) ) {
437                 return true;
438         }
439         // or one of the minors says "*" (knowing that the majors match already)
440         if ( ( minor1 && !strcmp( minor1, "*" ) ) || ( minor2 && !strcmp( minor2, "*" ) ) ) {
441                 return true;
442         }
443         return false;
444 }
445
446 bool CSynapseServer::ResolveAPI( APIDescriptor_t* pAPI ){
447         //Syn_Printf("In ResolveAPI %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name);
448         // loop through active clients, search for a client providing what we are looking for
449         list<CSynapseClientSlot>::iterator iClient;
450         for ( iClient = mClients.begin(); iClient != mClients.end(); iClient++ )
451         {
452                 // walk through interfaces on this client for a match
453                 CSynapseClient *pScanClient = ( *iClient ).mpClient;
454                 int i,max = pScanClient->GetAPICount();
455                 for ( i = 0; i < max; i++ )
456                 {
457                         APIDescriptor_t *pScanClientAPI = pScanClient->GetAPIDescriptor( i );
458                         if ( pScanClientAPI->mType == SYN_PROVIDE ) {
459                                 if ( MatchAPI( pAPI, pScanClientAPI ) ) {
460                                         // can this client provide APIs yet
461                                         // it is possible that all of it's APIs have been filled and it's not been activated yet
462                                         pScanClient->CheckSetActive();
463                                         if ( pScanClient->IsActive() ) {
464                                                 // make sure this interface has correct size (this is our version check)
465                                                 if ( pAPI->mSize != pScanClientAPI->mSize ) {
466                                                         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 );
467                                                         Syn_Printf( "  the module and the server are incompatible\n" );
468                                                         // keep going to other APIs
469                                                         continue;
470                                                 }
471                                                 // this is an active client, we can request
472                         #ifdef SYNAPSE_VERBOSE
473                                                 Syn_Printf( "RequestAPI '%s' '%s' from '%s' for API %p\n", pAPI->major_name, pAPI->minor_name, pScanClient->GetInfo(), pAPI );
474                         #endif
475                                                 if ( !pScanClient->RequestAPI( pAPI ) ) {
476                                                         // this should never happen, means we think this module provides the API, but it answers that it doesn't
477                                                         Syn_Printf( "ERROR: RequestAPI failed\n" );
478                                                         return false;
479                                                 }
480                                                 pScanClientAPI->mRefCount++;
481                                                 pAPI->mbTableInitDone = true;
482                                                 return true; // job done
483                                         }
484                                         else
485                                         {
486                                                 // this client is not active yet, some of it's required interfaces are not filled in
487                                                 PushRequired( pScanClient );
488                                                 // we will exit the scan through the APIDescriptor of this client and look at other clients
489                                                 break;
490                                         }
491                                 }
492                         }
493                 }
494         }
495         return false;
496 }
497
498 bool CSynapseServer::DoResolve( CSynapseClient *pClient ){
499         list<CSynapseClientSlot>::iterator iSlot;
500         for ( iSlot = mClients.begin(); iSlot != mClients.end(); iSlot++ )
501         {
502                 if ( ( *iSlot ).mpClient == pClient ) {
503                         break;
504                 }
505         }
506         if ( iSlot == mClients.end() ) {
507                 Syn_Printf( "CSynapserServer::Resolve adding new client slot '%s'\n", pClient->GetInfo() );
508                 CSynapseClientSlot slot;
509                 slot.mpClient = pClient;
510                 slot.mFileName = "local client";
511                 // make it active so we can request the interfaces already
512                 pClient->ForceSetActive();
513                 mClients.push_front( slot );
514         }
515         else
516         {
517                 // make it active so we can request the interfaces already
518                 ( *iSlot ).mpClient->ForceSetActive();
519         }
520
521         // push the interfaces that need to be resolved for this client
522         // NOTE: this doesn't take care of the SYN_REQUIRE_ANY interfaces
523         PushRequired( pClient );
524         // start resolving now
525         // working till the stack is emptied or till we reach a dead end situation
526         // we do a depth first traversal, we will grow the interface stack to be resolved till we start finding solutions
527         list<APIDescriptor_t*>::iterator iCurrent;
528         mbStackChanged = true; // init to true so we try the first elem
529         while ( !mStack.empty() )
530         {
531                 //DumpStack();
532                 if ( !mbStackChanged ) {
533                         // the stack didn't change last loop
534                         iCurrent++;
535                         if ( iCurrent == mStack.end() ) {
536                                 Syn_Printf( "ERROR: CSynapseServer::Resolve, failed to resolve\n" );
537                                 DumpStack();
538                                 return false;
539                         }
540                         if ( ResolveAPI( *iCurrent ) ) {
541                                 iCurrent = mStack.erase( iCurrent );
542                                 mbStackChanged = true;
543                         }
544                 }
545                 else
546                 {
547                         // the stack changed at last loop
548                         mbStackChanged = false;
549                         iCurrent = mStack.begin();
550                         if ( ResolveAPI( *iCurrent ) ) {
551                                 iCurrent = mStack.erase( iCurrent );
552                                 mbStackChanged = true;
553                         }
554                 }
555         }
556         return true;
557 }
558
559 bool CSynapseServer::Resolve( CSynapseClient *pClient ){
560         bool ret = DoResolve( pClient );
561         list<CSynapseClientSlot>::iterator iClient;
562         iClient = mClients.begin();
563         while ( iClient != mClients.end() )
564         {
565                 CSynapseClient *pClient = ( *iClient ).mpClient;
566                 if ( !pClient->IsActive() ) {
567                         Syn_Printf( "Unloading an unused module: '%s'\n", pClient->GetInfo() );
568                         iClient = ShutdownClient( iClient );
569                 }
570                 else{
571                         iClient++;
572                 }
573         }
574         return ret;
575 }
576
577 void CSynapseServer::Shutdown(){
578         Syn_Printf( "Synapse server core is shutting down\n" );
579         // do a first pass to shutdown the clients nicely (i.e. decref, release memory and drop everything)
580         // 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
581         list<CSynapseClientSlot>::iterator iClient;
582         iClient = mClients.begin();
583         for ( iClient = mClients.begin(); iClient != mClients.end(); iClient++ )
584         {
585                 ( *iClient ).mpClient->Shutdown();
586         }
587         // now release them from the server's point of view
588         iClient = mClients.begin();
589         while ( iClient != mClients.end() )
590         {
591                 iClient = ShutdownClient( iClient );
592         }
593 }
594
595 void CSynapseServer::DumpStack(){
596         list<APIDescriptor_t*>::iterator iCurrent;
597         for ( iCurrent = mStack.begin(); iCurrent != mStack.end(); iCurrent++ )
598         {
599                 APIDescriptor_t*pAPI = *iCurrent;
600                 Syn_Printf( "interface %s %p '%s' '%s'\n", APITypeName[pAPI->mType], pAPI, pAPI->major_name, pAPI->minor_name );
601         }
602 }
603
604 void CSynapseServer::DumpActiveClients(){
605         list<CSynapseClientSlot>::iterator iClient;
606         for ( iClient = mClients.begin(); iClient != mClients.end(); iClient++ )
607         {
608                 CSynapseClient *pClient = ( *iClient ).mpClient;
609                 Syn_Printf( "%s", pClient->GetInfo() );
610                 if ( pClient->IsActive() ) {
611                         Syn_Printf( "\n" );
612                 }
613                 else{
614                         Syn_Printf( " (not active)\n" );
615                 }
616         }
617 }
618
619 bool CSynapseServer::SelectClientConfig( const char *client_name ){
620         if ( !mpDoc ) {
621                 return false;
622         }
623         xmlNodePtr pNode = xmlDocGetRootElement( mpDoc );
624         if ( !pNode ) {
625                 return false;
626         }
627         // look for the client
628         pNode = pNode->children;
629         while ( pNode )
630         {
631                 if ( pNode->type == XML_ELEMENT_NODE ) {
632                         xmlChar *prop = xmlGetProp( pNode, (const xmlChar *)"name" );
633                         if ( !strcmp( (const char *)prop, client_name ) ) {
634                                 xmlFree( prop );
635                                 break;
636                         }
637                         xmlFree( prop );
638                 }
639                 pNode = pNode->next;
640         }
641         if ( !pNode ) {
642                 return false; // config you asked for isn't there
643         }
644         // focus
645         mpFocusedNode = pNode->children;
646         mpCurrentClientConfig = pNode;
647         return true;
648 }
649
650 bool CSynapseServer::GetNextConfig( char **api_name, char **minor ){
651         while ( mpFocusedNode && mpFocusedNode->name )
652         {
653                 if ( mpFocusedNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)mpFocusedNode->name, "api" ) ) {
654                         if ( m_api_name ) {
655                                 xmlFree( m_api_name );
656                         }
657                         m_api_name = xmlGetProp( mpFocusedNode, (const xmlChar *)"name" );
658                         *api_name = (char *)m_api_name;
659                         if ( m_content ) {
660                                 g_free( m_content );
661                         }
662                         m_content = g_strdup( (const gchar *)mpFocusedNode->children->content );
663                         g_strstrip( m_content );
664                         *minor = m_content;
665                         mpFocusedNode = mpFocusedNode->next;
666                         return true;
667                 }
668                 mpFocusedNode = mpFocusedNode->next;
669         }
670         return false;
671 }
672
673 bool CSynapseServer::GetConfigForAPI( const char *api, char **minor ) {
674         xmlNodePtr pNode = mpCurrentClientConfig->children;
675         while ( pNode && pNode->name ) {
676                 if ( pNode->type == XML_ELEMENT_NODE && !strcmp( (const char *)pNode->name, "api" ) ) {
677                         if ( m_api_name ) {
678                                 xmlFree( m_api_name );
679                         }
680                         m_api_name = xmlGetProp( pNode, (const xmlChar *)"name" );
681                         if ( !strcmp( (const char *)m_api_name, api ) ) {
682                                 if ( m_content ) {
683                                         g_free( m_content );
684                                 }
685                                 m_content = g_strdup( (const gchar *)pNode->children->content );
686                                 g_strstrip( m_content );
687                                 *minor = m_content;
688                                 return true;
689                         }
690                 }
691                 pNode = pNode->next;
692         }
693         return false;
694 }
695
696 const char *CSynapseServer::GetModuleFilename( CSynapseClient *pClient ){
697         list<CSynapseClientSlot>::iterator iSlot;
698         for ( iSlot = mClients.begin(); iSlot != mClients.end(); iSlot++ )
699         {
700                 if ( ( *iSlot ).mpClient == pClient ) {
701                         if ( ( *iSlot ).mType == SYN_BUILTIN ) {
702                                 return ""; // FIXME
703                         }
704                         else
705                         {
706                                 return ( *iSlot ).mFileName;
707                         }
708                 }
709         }
710         return NULL;
711 }
712
713 /*
714    =======================================================================
715    client
716    =======================================================================
717  */
718
719 CSynapseClient::CSynapseClient(){
720 }
721
722 void CSynapseClient::Shutdown(){
723         vector<APIDescriptor_t *>::iterator iAPI;
724         for ( iAPI = mAPIDescriptors.begin(); iAPI != mAPIDescriptors.end(); iAPI++ )
725         {
726                 APIDescriptor_t *pAPI = *iAPI;
727                 if ( pAPI->mRefCount != 0 ) {
728                         Syn_Printf( "WARNING: ~CSynapseClient '%s' has non-zero ref count for interface '%s' '%s'\n", GetInfo(), pAPI->major_name, pAPI->minor_name );
729                 }
730                 else{
731                         delete pAPI;
732                 }
733                 *iAPI = NULL;
734         }
735         mAPIDescriptors.clear();
736         vector<CSynapseAPIManager *>::iterator iManager;
737         for ( iManager = mManagersList.begin(); iManager != mManagersList.end(); iManager++ )
738         {
739                 CSynapseAPIManager *pManager = *iManager;
740                 pManager->DecRef();
741                 *iManager = NULL;
742         }
743         mManagersList.clear();
744         for ( iManager = mManagersMatch.begin(); iManager != mManagersMatch.end(); iManager++ )
745         {
746                 CSynapseAPIManager *pManager = *iManager;
747                 pManager->DecRef();
748                 *iManager = NULL;
749         }
750         mManagersMatch.clear();
751 }
752
753 CSynapseClient::~CSynapseClient(){
754         // this should not be doing anything when called from here if everything went right
755         // otherwise it's likely to crash .. at least that's the sign we missed something
756         Shutdown();
757 }
758
759 int CSynapseClient::GetAPICount(){
760         return mAPIDescriptors.size();
761 }
762
763 APIDescriptor_t* CSynapseClient::GetAPIDescriptor( int i ){
764         return mAPIDescriptors[i];
765 }
766
767 int CSynapseClient::GetManagerMatchCount(){
768         return mManagersMatch.size();
769 }
770
771 CSynapseAPIManager* CSynapseClient::GetManagerMatch( int i ){
772         return mManagersMatch[i];
773 }
774
775 int CSynapseClient::GetManagerListCount(){
776         return mManagersList.size();
777 }
778
779 CSynapseAPIManager* CSynapseClient::GetManagerList( int i ){
780         return mManagersList[i];
781 }
782
783 bool CSynapseClient::AddAPI( const char *major, const char *minor, int size, EAPIType type, void *pTable ){
784         // do some safe checks before actual addition
785         if ( type == SYN_REQUIRE && !pTable ) {
786                 Syn_Printf( "ERROR: interface '%s' '%s' from '%s' is SYN_REQUIRE and doesn't provide a function table pointer\n", major, minor, GetInfo() );
787                 return false;
788         }
789         if ( pTable ) {
790                 int *pi = (int *)pTable;
791                 if ( pi == 0 ) {
792                         Syn_Printf( "ERROR: forgot to init function table size for interface '%s' '%s' from '%s'?\n", major, minor, GetInfo() );
793                         return false;
794                 }
795         }
796         APIDescriptor_t *pAPI = new APIDescriptor_t;
797         memset( pAPI, 0, sizeof( APIDescriptor_t ) );
798         strncpy( pAPI->major_name, major, MAX_APINAME );
799         if ( minor ) {
800                 strncpy( pAPI->minor_name, minor, MAX_APINAME );
801         }
802         pAPI->mType = type;
803         pAPI->mpTable = pTable;
804         // store the interface size
805         if ( type == SYN_PROVIDE ) {
806                 if ( size == 0 ) {
807                         Syn_Printf( "ERROR: size of the interface required for a SYN_PROVIDE ('%s' '%s' from '%s')\n", major, minor, GetInfo() );
808                         delete pAPI;
809                         return false;
810                 }
811                 pAPI->mSize = size;
812         }
813         else if ( type == SYN_REQUIRE ) {
814                 if ( size != 0 ) {
815                         // if a non-zero value is given in function call, use this instead of the val in table
816                         *( (int *)pAPI->mpTable ) = size;
817                         pAPI->mSize = size;
818                 }
819                 else
820                 {
821                         pAPI->mSize = *( (int *)pAPI->mpTable );
822                         if ( pAPI->mSize == 0 ) {
823                                 Syn_Printf( "ERROR: didn't get an interface size ('%s' '%s' from '%s')\n", major, minor, GetInfo() );
824                                 delete pAPI;
825                                 return false;
826                         }
827                 }
828         }
829         else
830         {
831                 Syn_Printf( "ERROR: AddAPI type '%d' not supported\n", type );
832                 return false;
833         }
834         mAPIDescriptors.push_back( pAPI );
835   #ifdef SYNAPSE_VERBOSE
836         Syn_Printf( "AddAPI: %s %p '%s' '%s' from '%s', size %d\n", APITypeName[pAPI->mType], pAPI, major, minor, GetInfo(), pAPI->mSize );
837   #endif
838         return true;
839 }
840
841 #include "version.h"
842
843 const char* CSynapseClient::GetInfo(){
844         return "CSynapseClient built " __DATE__ " " RADIANT_VERSION;
845 }
846
847 bool CSynapseClient::CheckSetActive(){
848         if ( mbActive ) {
849                 return true;
850         }
851         int i,max = GetAPICount();
852         for ( i = 0; i < max; i++ )
853         {
854                 APIDescriptor_t *pAPI = GetAPIDescriptor( i );
855                 if ( pAPI->mType == SYN_REQUIRE && !pAPI->mbTableInitDone ) {
856                         return false;
857                 }
858         }
859         // if we have managers with fixed list, those need to be completely filled in too
860         vector<CSynapseAPIManager *>::iterator iManager;
861         for ( iManager = mManagersList.begin(); iManager != mManagersList.end(); iManager++ )
862         {
863                 if ( !( *iManager )->CheckSetActive() ) {
864                         return false; // one of the managers doesn't have all it needs yet
865                 }
866         }
867         // call OnActivate to let the client perform last minute checks
868         // NOTE: this should be fatal instead of letting the engine try other combinations
869         if ( !OnActivate() ) {
870                 return false;
871         }
872         // yes, all required interfaces have been initialized
873         Syn_Printf( "'%s' activated\n", GetInfo() );
874         mbActive = true;
875         return true;
876 }
877
878 bool CSynapseClient::ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] ) {
879
880         if ( !client_name ) {
881                 client_name = GetName();
882         }
883
884         Syn_Printf( "Dynamic APIs for client '%s'\n", GetInfo() );
885         if ( !pServer->SelectClientConfig( client_name ) ) {
886                 Syn_Printf( "Failed to select synapse client config '%s'\n", client_name );
887                 return false;
888         }
889
890         int i = 0;
891         while ( entries[i].type != SYN_UNKNOWN ) { // don't test pTable, for a SYN_PROVIDE it will be empty
892                 char *minor;
893                 if ( !pServer->GetConfigForAPI( entries[i].api, &minor ) ) {
894                         Syn_Printf( "GetConfigForAPI '%s' failed - invalid XML config file?\n", entries[i].api );
895                         return false;
896                 }
897                 AddAPI( entries[i].api, minor, entries[i].size, entries[i].type, entries[i].pTable );
898                 i++;
899         }
900         Syn_Printf( "%d dynamic interfaces parsed for '%s'\n", i, client_name );
901         return true;
902 }
903
904 void CSynapseClient::AddManager( CSynapseAPIManager *pManager ){
905         pManager->IncRef();
906         if ( pManager->GetType() == API_LIST ) {
907                 mManagersList.push_back( pManager );
908         }
909         else{
910                 mManagersMatch.push_back( pManager );
911         }
912 }
913
914 CSynapseAPIManager::~CSynapseAPIManager(){
915         vector<APIDescriptor_t *>::iterator iAPI;
916         for ( iAPI = mAPIs.begin(); iAPI != mAPIs.end(); iAPI++ )
917         {
918                 APIDescriptor_t *pAPI = *iAPI;
919                 if ( pAPI->mRefCount != 0 ) {
920                         Syn_Printf( "WARNING: ~CSynapseAPIManager has non-zero ref count for interface '%s' '%s'\n", pAPI->major_name, pAPI->minor_name );
921                 }
922                 delete pAPI;
923                 *iAPI = NULL;
924         }
925 }
926
927 APIDescriptor_t* CSynapseAPIManager::PrepareRequireAPI( APIDescriptor_t *pAPI ){
928 #ifdef _DEBUG
929         if ( pAPI->mType != SYN_PROVIDE ) {
930                 Syn_Printf( "ERROR: unexpected pAPI->mType != SYN_PROVIDE in CSynapseAPIManager::PrepareRequireAPI\n" );
931                 return NULL;
932         }
933 #endif
934         APIDescriptor_t *pRequireAPI = new APIDescriptor_t;
935         memcpy( pRequireAPI, pAPI, sizeof( APIDescriptor_t ) );
936         pRequireAPI->mType = SYN_REQUIRE_ANY;
937         pRequireAPI->mpTable = NULL;
938         pRequireAPI->mbTableInitDone = false;
939         pRequireAPI->mSize = 0; // this will have to be set correctly by the child for version checking
940         pRequireAPI->mRefCount = 0;
941         return pRequireAPI;
942 }
943
944 void CSynapseAPIManager::SetMatchAPI( const char *major, const char *minor ){
945         if ( strlen( minor ) > MAX_PATTERN_STRING ) {
946                 Syn_Printf( "ERROR: MAX_TOKEN_STRING exceeded in CSynapseAPIManager::SetMatchAPI: '%s'\n", minor );
947                 return;
948         }
949         strcpy( major_pattern, major );
950         strcpy( minor_pattern, minor );
951         if ( strcmp( minor, "*" ) ) {
952                 mType = API_LIST;
953         }
954 }
955
956 bool CSynapseAPIManager::MatchAPI( const char *major, const char *minor ){
957         assert( mType == API_MATCH );
958
959         /*!
960            if this interface has been allocated already, avoid requesting it again..
961          */
962         vector<APIDescriptor_t *>::iterator iAPI;
963         for ( iAPI = mAPIs.begin(); iAPI != mAPIs.end(); iAPI++ )
964         {
965                 if ( CSynapseServer::MatchAPI( ( *iAPI )->major_name, ( *iAPI )->minor_name, major, minor ) ) {
966                         return false;
967                 }
968         }
969
970         if ( !strcmp( major, major_pattern ) ) {
971                 return true;
972         }
973         return false;
974 }
975
976 bool CSynapseAPIManager::CheckSetActive(){
977         if ( mType == API_MATCH ) {
978                 return false;
979         }
980         // mType == API_LIST
981         int i,max = GetAPICount();
982         for ( i = 0; i < max; i++ )
983         {
984                 if ( !GetAPI( i )->mbTableInitDone ) {
985                         return false;
986                 }
987         }
988         return true;
989 }
990
991 void CSynapseAPIManager::InitializeAPIList(){
992         char minor_tok[MAX_PATTERN_STRING];
993         char *token;
994
995         if ( mAPIs.size() ) {
996                 Syn_Printf( "WARNING: CSynapseAPIManager::InitializeAPIList on an already initialized APIManager\n" );
997                 return;
998         }
999
1000         strncpy( minor_tok, minor_pattern, MAX_PATTERN_STRING );
1001         token = strtok( minor_tok, " " );
1002         while ( token )
1003         {
1004                 /* ask the child to build from scratch */
1005                 APIDescriptor_t *pAPI = new APIDescriptor_t;
1006                 memset( pAPI, 0, sizeof( APIDescriptor_t ) );
1007                 strncpy( pAPI->major_name, major_pattern, MAX_APINAME );
1008                 strncpy( pAPI->minor_name, token, MAX_APINAME );
1009                 pAPI->mType = SYN_REQUIRE_ANY;
1010                 FillAPITable( pAPI );
1011                 mAPIs.push_back( pAPI );
1012                 token = strtok( NULL, " " );
1013         }
1014 }
1015
1016 int CSynapseAPIManager::GetAPICount(){
1017         return mAPIs.size();
1018 }
1019
1020 APIDescriptor_t* CSynapseAPIManager::GetAPI( int i ){
1021         return mAPIs[i];
1022 }