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