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