1 /*
2 Software License :
3
4 Copyright (c) 2007-2009, The Open Effects Association Ltd. All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14 * Neither the name The Open Effects Association Ltd, nor the names of its
15 contributors may be used to endorse or promote products derived from this
16 software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <assert.h>
31
32 #include <map>
33 #include <string>
34 #include <iostream>
35 #include <fstream>
36 #include <sstream> // stringstream
37 #include <stdexcept>
38
39 #include <string.h>
40 #include <stdlib.h>
41
42 #include "expat.h"
43
44 // ofx
45 #include "ofxCore.h"
46 #include "ofxImageEffect.h"
47
48 // ofx host
49 #include "ofxhBinary.h"
50 #include "ofxhPropertySuite.h"
51 #include "ofxhMemory.h"
52 #include "ofxhPluginAPICache.h"
53 #include "ofxhPluginCache.h"
54 #include "ofxhHost.h"
55 #include "ofxhXml.h"
56 #include "ofxhUtilities.h"
57
58 #if defined (__linux__) || defined (__DragonFly__)
59
60 #define DIRLIST_SEP_CHARS ":;"
61 #define DIRSEP "/"
62 #include <dirent.h>
63
getArchStr()64 static const char *getArchStr()
65 {
66 if(sizeof(void *) == 4) {
67 #if defined(__linux__)
68 return "Linux-x86";
69 #else
70 return "FreeBSD-x86";
71 #endif
72 }
73 else {
74 #if defined(__linux__)
75 return "Linux-x86-64";
76 #else
77 return "FreeBSD-x86-64";
78 #endif
79 }
80 }
81
82 #define ARCHSTR getArchStr()
83
84 #elif defined (__APPLE__)
85
86 #define DIRLIST_SEP_CHARS ";:"
87 #if defined(__x86_64) || defined(__x86_64__)
88 #define ARCHSTR "MacOS-x86-64"
89 #else
90 #define ARCHSTR "MacOS"
91 #endif
92 #define DIRSEP "/"
93 #include <dirent.h>
94
95 #elif defined (WINDOWS)
96 #define DIRLIST_SEP_CHARS ";"
97 #ifdef _WIN64
98 #define ARCHSTR "win64"
99 #else
100 #define ARCHSTR "win32"
101 #endif
102 #define DIRSEP "\\"
103
104 #include "shlobj.h"
105 #endif
106
107
108 bool OFX::Host::PluginCache::_useStdOFXPluginsLocation = true;
109 OFX::Host::PluginCache* OFX::Host::PluginCache::gPluginCachePtr = 0;
110
111 // Define this to enable ofx plugin cache debug messages.
112 //#define CACHE_DEBUG
113
114 using namespace OFX::Host;
115
116
117 /// try to open the plugin bundle object and query it for plugins
loadPluginInfo(PluginCache * cache)118 void PluginBinary::loadPluginInfo(PluginCache *cache) {
119 if (isInvalid()) {
120 return;
121 }
122
123 if (_binary) {
124 _fileModificationTime = _binary->getTime();
125 _fileSize = _binary->getSize();
126
127 // Take a reference to load the binary only once per session. It will
128 // eventually be unloaded in the destructor (see below).
129 // This avoid lots of useless calls to dlopen()/dlclose().
130 if (!_binary->isLoaded()) {
131 _binary->ref();
132 }
133 }
134
135
136 if (_binary) {
137 _getNumberOfPlugins = (OfxGetNumberOfPluginsFunc) _binary->findSymbol("OfxGetNumberOfPlugins");
138 _getPluginFunc = (OfxGetPluginFunc) _binary->findSymbol("OfxGetPlugin");
139 }
140
141
142 if (_getNumberOfPlugins == 0 || _getPluginFunc == 0) {
143 if (_binary) {
144 _binary->setInvalid(true);
145 }
146
147 } else {
148 int pluginCount = (*_getNumberOfPlugins)();
149 _plugins.clear();
150 _plugins.reserve(pluginCount);
151
152 for (int i=0;i<pluginCount;i++) {
153 OfxPlugin *plug = (*_getPluginFunc)(i);
154
155 APICache::PluginAPICacheI *api = cache->findApiHandler(plug->pluginApi, plug->apiVersion);
156 assert(api);
157
158 _plugins.push_back(api->newPlugin(this, i, plug));
159 }
160 }
161 }
162
~PluginBinary()163 PluginBinary::~PluginBinary() {
164 std::vector<Plugin*>::iterator i = _plugins.begin();
165 while (i != _plugins.end()) {
166 const APICache::PluginAPICacheI &api = (*i)->getApiHandler();
167 api.unloadPlugin(*i);
168 delete *i;
169 i++;
170 }
171 // release the last reference to the binary, which should unload it
172 // if this reference was taken by loadPluginInfo().
173 if (_binary) {
174 if (_binary->isLoaded()) {
175 _binary->unref();
176 }
177 assert(!_binary->isLoaded());
178 }
179 }
180
PluginHandle(Plugin * p,OFX::Host::Host * host)181 PluginHandle::PluginHandle(Plugin *p, OFX::Host::Host *host)
182 {
183 _b = p->getBinary();
184 if (_b->_binary) {
185 _b->_binary->ref();
186 }
187 _op = 0;
188 OfxPlugin* (*getPlug)(int) = 0;
189 if (_b->_getPluginFunc) {
190 getPlug = _b->_getPluginFunc;
191 }
192 if (!getPlug && _b->_binary) {
193 getPlug = (OfxPlugin*(*)(int)) _b->_binary->findSymbol("OfxGetPlugin");
194 }
195 if (getPlug) {
196 _op = getPlug(p->getIndex());
197 if (_op) {
198 _op->setHost(host->getHandle());
199 }
200 }
201 }
202
~PluginHandle()203 PluginHandle::~PluginHandle() {
204 if (_b->_binary) {
205 _b->_binary->unref();
206 }
207 }
208
209
210 #if defined (WINDOWS)
211 const std::wstring&
getStdOFXPluginPath(const std::string & hostId)212 PluginCache::getStdOFXPluginPath(const std::string &hostId)
213 {
214 static std::wstring ret;
215 static int gotIt = 0;
216 if(!gotIt) {
217 gotIt = 1;
218 wchar_t buffer[MAX_PATH];
219 SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, SHGFP_TYPE_CURRENT, buffer);
220 std::wstring str;
221 str.append(L"\\OFX\\");
222 str.append(OFX::utf8_to_utf16(hostId));
223 wcscat_s(buffer, MAX_PATH, str.c_str());
224 ret = std::wstring(buffer);
225 }
226 return ret;
227 }
228 #endif
229
230 static
OFXGetEnv(const char * e)231 std::string OFXGetEnv(const char* e)
232 {
233 #if defined(WINDOWS) && !defined(__MINGW32__)
234 size_t requiredSize;
235 getenv_s(&requiredSize, 0, 0, e);
236 std::vector<char> buffer(requiredSize);
237 if(requiredSize >0)
238 {
239 getenv_s(&requiredSize, &buffer.front(), requiredSize, e);
240 return &buffer.front();
241 }
242 return "";
243 #else
244 if(getenv(e))
245 return getenv(e);
246 #endif
247 return "";
248 }
249
getPluginCache()250 PluginCache* PluginCache::getPluginCache()
251 {
252 if(!gPluginCachePtr)
253 gPluginCachePtr = new PluginCache();
254 return gPluginCachePtr;
255 }
256
clearPluginCache()257 void PluginCache::clearPluginCache()
258 {
259 delete gPluginCachePtr;
260 gPluginCachePtr = 0;
261 }
262
~PluginCache()263 PluginCache::~PluginCache()
264 {
265 for(std::list<PluginBinary *>::iterator it=_binaries.begin(); it != _binaries.end(); ++it) {
266 delete (*it);
267 }
268 _binaries.clear();
269 }
270
PluginCache()271 PluginCache::PluginCache()
272 : _hostSpec(NULL)
273 #ifdef OFX_USE_STATIC_PLUGINS
274 , _staticBinary(NULL)
275 #endif
276 , _xmlCurrentBinary(NULL)
277 , _xmlCurrentPlugin(NULL)
278 {
279 _cacheVersion = "";
280 _ignoreCache = false;
281 _dirty = false;
282 _enablePluginSeek = true;
283
284 std::string s = OFXGetEnv("OFX_PLUGIN_PATH");
285
286
287 while (s.length()) {
288
289 int spos = int(s.find_first_of(DIRLIST_SEP_CHARS));
290
291 std::string path;
292
293 if (spos != -1) {
294 path = s.substr(0, spos);
295 s = s.substr(spos+1);
296 }
297 else {
298 path = s;
299 s = "";
300 }
301
302 _pluginPath.push_back(path);
303 }
304
305 if (!_useStdOFXPluginsLocation)
306 return;
307
308 #if defined(WINDOWS)
309
310 std::wstring wpath = getStdOFXPluginPath();
311 std::string path = OFX::utf16_to_utf8(wpath);
312
313 _pluginPath.push_back(path);
314 _pluginPath.push_back("C:\\Program Files\\Common Files\\OFX\\Plugins");
315 #endif
316 #if defined(__linux__) || defined(__DragonFly__)
317 _pluginPath.push_back("/usr/local/OFX/Plugins");
318 #endif
319 #if defined(__APPLE__)
320 _pluginPath.push_back("/Library/OFX/Plugins");
321 #endif
322 }
323
setPluginHostPath(const std::string & hostId)324 void PluginCache::setPluginHostPath(const std::string &hostId) {
325 #if defined(WINDOWS)
326 std::wstring wpath = getStdOFXPluginPath(hostId);
327 std::string path = OFX::utf16_to_utf8(wpath);
328
329 _pluginPath.push_back(path);
330 _pluginPath.push_back("C:\\Program Files\\Common Files\\OFX\\" + hostId);
331 #endif
332 #if defined(__linux__) || defined(__DragonFly__)
333 _pluginPath.push_back("/usr/OFX/" + hostId);
334 #endif
335 #if defined(__APPLE__)
336 _pluginPath.push_back("/Library/OFX/" + hostId);
337 #endif
338 }
339
scanDirectory(std::set<std::string> & foundBinFiles,const std::string & dir,bool recurse)340 void PluginCache::scanDirectory(std::set<std::string> &foundBinFiles, const std::string &dir, bool recurse)
341 {
342 #ifdef CACHE_DEBUG
343 printf("looking in %s for plugins\n", dir.c_str());
344 #endif
345
346 #if defined (WINDOWS)
347 WIN32_FIND_DATAW findData;
348 HANDLE findHandle;
349 #else
350 DIR *d = opendir(dir.c_str());
351 if (!d) {
352 return;
353 }
354 #endif
355
356 _pluginDirs.push_back(dir.c_str());
357
358 #if defined (UNIX)
359 while (dirent *de = readdir(d))
360 #elif defined (WINDOWS)
361 {
362
363 std::wstring ws = OFX::utf8_to_utf16((dir + "\\*"));
364 findHandle = FindFirstFileW(ws.c_str(), &findData);
365 }
366 if (findHandle == INVALID_HANDLE_VALUE)
367 {
368 return;
369 }
370
371 while (1)
372 #endif
373 {
374 #if defined (UNIX)
375 std::string name = de->d_name;
376 bool isdir = true;
377 #else
378 std::wstring wname = findData.cFileName;
379 std::string name = OFX::utf16_to_utf8(wname);
380
381 bool isdir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
382 #endif
383 if (name.find(".ofx.bundle") != std::string::npos) {
384 std::string barename = name.substr(0, name.length() - strlen(".bundle"));
385 std::string bundlename = dir + DIRSEP + name;
386 std::string binpath = dir + DIRSEP + name + DIRSEP "Contents" DIRSEP + ARCHSTR + DIRSEP + barename;
387
388 // don't insert binpath yet, do it later because of Mac OS X Universal stuff
389 //foundBinFiles.insert(binpath);
390
391 #if defined(__APPLE__) && (defined(__x86_64) || defined(__x86_64__))
392 /* From the OpenFX specification:
393
394 MacOS-x86-64 - for Apple Macintosh OS X, specifically on
395 intel x86 CPUs running AMD's 64 bit extensions. 64 bit host
396 applications should check this first, and if it doesn't
397 exist or is empty, fall back to "MacOS" looking for a
398 universal binary.
399 */
400
401 std::string binpath_universal = dir + DIRSEP + name + DIRSEP "Contents" DIRSEP + "MacOS" + DIRSEP + barename;
402 if (_knownBinFiles.find(binpath_universal) != _knownBinFiles.end()) {
403 binpath = binpath_universal;
404 }
405 #endif
406
407 if (_knownBinFiles.find(binpath) == _knownBinFiles.end()) {
408 #ifdef CACHE_DEBUG
409 printf("found non-cached binary %s\n", binpath.c_str());
410 #endif
411 _dirty = true;
412
413 // the binary was not in the cache
414
415 PluginBinary *pb = 0;
416 #if defined(__x86_64) || defined(__x86_64__)
417 pb = new PluginBinary(binpath, bundlename, this);
418 # if defined(__APPLE__)
419 if (pb->isInvalid()) {
420 // fallback to "MacOS"
421 delete pb;
422 binpath = binpath_universal;
423 pb = new PluginBinary(binpath, bundlename, this);
424 }
425 # endif
426 #else
427 pb = new PluginBinary(binpath, bundlename, this);
428 #endif
429 _binaries.push_back(pb);
430 _knownBinFiles.insert(binpath);
431 foundBinFiles.insert(binpath);
432
433 for (int j=0;j<pb->getNPlugins();j++) {
434 Plugin *plug = &pb->getPlugin(j);
435 const APICache::PluginAPICacheI &api = plug->getApiHandler();
436 api.loadFromPlugin(plug);
437 }
438 } else {
439 #ifdef CACHE_DEBUG
440 printf("found cached binary %s\n", binpath.c_str());
441 #endif
442 }
443 // insert final path (universal or not) in the list of found files
444 foundBinFiles.insert(binpath);
445 } else {
446 if (isdir && (recurse && !name.empty() && name[0] != '@' && name[name.size() - 1] != '.')) {
447 scanDirectory(foundBinFiles, dir + DIRSEP + name, recurse);
448 }
449 }
450 #if defined(WINDOWS)
451 int rval = FindNextFileW(findHandle, &findData);
452
453 if (rval == 0) {
454 break;
455 }
456 #endif
457 }
458
459 #if defined(UNIX)
460 closedir(d);
461 #else
462 FindClose(findHandle);
463 #endif
464 }
465
seekPluginFile(const std::string & baseName) const466 std::string PluginCache::seekPluginFile(const std::string &baseName) const {
467 // Exit early if disabled
468 if (!_enablePluginSeek)
469 return "";
470
471 for (std::list<std::string>::const_iterator paths= _pluginDirs.begin();
472 paths != _pluginDirs.end();
473 paths++) {
474 std::string candidate = *paths + DIRSEP + baseName;
475 FILE *f = fopen(candidate.c_str(), "r");
476 if (f) {
477 fclose(f);
478 return candidate;
479 }
480 }
481 return "";
482 }
483
scanPluginFiles()484 void PluginCache::scanPluginFiles()
485 {
486 std::set<std::string> foundBinFiles;
487
488 for (std::list<std::string>::iterator paths= _pluginPath.begin();
489 paths != _pluginPath.end();
490 paths++) {
491 scanDirectory(foundBinFiles, *paths, _nonrecursePath.find(*paths) == _nonrecursePath.end());
492 }
493
494 #ifdef OFX_USE_STATIC_PLUGINS
495 // Now add all static plug-ins
496
497 // If already loaded from the cache, the static binary must point to the same host binary file
498 assert(!_staticBinary || _staticBinary->getFilePath() == _hostAppBinFilePath);
499 if (!_staticBinary) {
500 _staticBinary = new PluginBinary(_hostAppBinFilePath, &OfxGetNumberOfPlugins,&OfxGetPlugin,this, 0, 0);
501 if (_staticBinary->isInvalid()) {
502 std::cerr << "WARNING: ignoring statically linked plug-ins because host application binary file path (" << _hostAppBinFilePath << ") is wrongly set" << std::endl;
503 delete _staticBinary;
504 _staticBinary = 0;
505 } else {
506 _dirty = true;
507 for (int j=0;j<_staticBinary->getNPlugins();j++) {
508 Plugin *plug = &_staticBinary->getPlugin(j);
509 const APICache::PluginAPICacheI &api = plug->getApiHandler();
510 api.loadFromPlugin(plug);
511 }
512 _binaries.push_back(_staticBinary);
513 }
514 }
515 #endif
516
517 std::list<PluginBinary *>::iterator i=_binaries.begin();
518 while (i!=_binaries.end()) {
519 PluginBinary *pb = *i;
520
521 if (!pb->isStaticallyLinkedPlugin() && foundBinFiles.find(pb->getFilePath()) == foundBinFiles.end()) {
522
523 // the binary was in the cache, but was not on the path
524
525 _dirty = true;
526 i = _binaries.erase(i);
527 delete pb;
528
529 } else {
530
531 bool binChanged = pb->hasBinaryChanged();
532
533 // the binary was in the cache, but the binary has changed and thus we need to reload
534 if (binChanged) {
535 // The static PluginBinary already had loadPluginInfo called anyway at this point
536 if (!pb->isStaticallyLinkedPlugin()) {
537 pb->loadPluginInfo(this);
538 }
539 _dirty = true;
540 }
541
542 for (int j=0;j<pb->getNPlugins();j++) {
543 Plugin *plug = &pb->getPlugin(j);
544 APICache::PluginAPICacheI &api = plug->getApiHandler();
545
546 if (binChanged) {
547 api.loadFromPlugin(plug);
548 }
549
550 std::string reason;
551
552 if (api.pluginSupported(plug, reason)) {
553 _plugins.push_back(plug);
554 api.confirmPlugin(plug, _pluginPath);
555 } else {
556 std::cerr << "ignoring plugin " << plug->getIdentifier() <<
557 " as unsupported (" << reason << ")" << std::endl;
558 }
559 }
560
561 i++;
562 }
563 }
564 }
565
566
567 /// callback for XML parser
elementBeginHandler(void * userData,const XML_Char * name,const XML_Char ** atts)568 static void elementBeginHandler(void *userData, const XML_Char *name, const XML_Char **atts) {
569 PluginCache::getPluginCache()->elementBeginCallback(userData, name, atts);
570 }
571
572 /// callback for XML parser
elementCharHandler(void * userData,const XML_Char * data,int len)573 static void elementCharHandler(void *userData, const XML_Char *data, int len) {
574 PluginCache::getPluginCache()->elementCharCallback(userData, data, len);
575 }
576
577 /// callback for XML parser
elementEndHandler(void * userData,const XML_Char * name)578 static void elementEndHandler(void *userData, const XML_Char *name) {
579 PluginCache::getPluginCache()->elementEndCallback(userData, name);
580 }
581
mapHasAll(const std::map<std::string,std::string> & attmap,const char ** atts)582 static bool mapHasAll(const std::map<std::string, std::string> &attmap, const char **atts) {
583 while (*atts) {
584 if (attmap.find(*atts) == attmap.end()) {
585 return false;
586 }
587 atts++;
588 }
589 return true;
590 }
591
elementBeginCallback(void *,const XML_Char * name,const XML_Char ** atts)592 void PluginCache::elementBeginCallback(void */*userData*/, const XML_Char *name, const XML_Char **atts) {
593 if (_ignoreCache) {
594 return;
595 }
596
597 std::string ename = name;
598 std::map<std::string, std::string> attmap;
599
600 while (*atts) {
601 attmap[atts[0]] = atts[1];
602 atts += 2;
603 }
604
605 /// XXX: validate in general
606
607 if (ename == "cache") {
608 std::string cacheversion = attmap["version"];
609 if (cacheversion != _cacheVersion) {
610 #ifdef CACHE_DEBUG
611 printf("mismatched version, ignoring cache (got '%s', wanted '%s')\n",
612 cacheversion.c_str(),
613 _cacheVersion.c_str());
614 #endif
615 _ignoreCache = true;
616 }
617 }
618
619 if (ename == "binary") {
620 const char *binAtts[] = {"static_bin", "path", "bundle_path", "mtime", "size", NULL};
621
622 if (!mapHasAll(attmap, binAtts)) {
623 // no path: bad XML
624 }
625
626 #ifdef OFX_USE_STATIC_PLUGINS
627 bool isStaticLinkedCachedBinary = OFX::Host::Property::stringToInt(attmap["static_bin"]);
628 #endif
629
630 std::string fname = attmap["path"];
631 std::string bname = attmap["bundle_path"];
632 time_t mtime = OFX::Host::Property::stringToInt(attmap["mtime"]);
633 off_t size = OFX::Host::Property::stringToInt(attmap["size"]);
634
635 PluginBinary* pb;
636 #ifdef OFX_USE_STATIC_PLUGINS
637 if (isStaticLinkedCachedBinary) {
638 // only 1 static binary allowed!
639 if (_staticBinary) {
640 _ignoreCache = true;
641 return;
642 }
643 // We need to provide the 2 function pointers for the static binary
644 pb = new PluginBinary(_hostAppBinFilePath, &OfxGetNumberOfPlugins,&OfxGetPlugin, this, &fname, &mtime, &size);
645 _staticBinary = pb;
646 } else
647 #endif
648 {
649 pb = new PluginBinary(fname, bname, mtime, size);
650 }
651 _xmlCurrentBinary = pb;
652 _binaries.push_back(_xmlCurrentBinary);
653 _knownBinFiles.insert(fname);
654
655 return;
656 }
657
658 if (ename == "plugin" && _xmlCurrentBinary && !_xmlCurrentBinary->hasBinaryChanged()) {
659 const char *plugAtts[] = {"api", "name", "index", "api_version", "major_version", "minor_version", NULL};
660
661 if (!mapHasAll(attmap, plugAtts)) {
662 // no path: bad XML
663 }
664
665 std::string api = attmap["api"];
666 std::string rawIdentifier = attmap["name"];
667
668 std::string identifier = rawIdentifier;
669
670 // Who says the pluginIdentifier is case-insensitive? OFX 1.3 spec doesn't mention this.
671 // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id472588
672 //for (size_t i=0;i<identifier.size();i++) {
673 // identifier[i] = tolower(identifier[i]);
674 //}
675
676 int idx = OFX::Host::Property::stringToInt(attmap["index"]);
677 int api_version = OFX::Host::Property::stringToInt(attmap["api_version"]);
678 int major_version = OFX::Host::Property::stringToInt(attmap["major_version"]);
679 int minor_version = OFX::Host::Property::stringToInt(attmap["minor_version"]);
680
681 APICache::PluginAPICacheI *apiCache = findApiHandler(api, api_version);
682 if (apiCache) {
683
684 Plugin *pe = apiCache->newPlugin(_xmlCurrentBinary, idx, api, api_version, identifier, rawIdentifier, major_version, minor_version);
685 _xmlCurrentBinary->addPlugin(pe);
686 _xmlCurrentPlugin = pe;
687 apiCache->beginXmlParsing(pe);
688 }
689
690 return;
691 }
692
693 if (_xmlCurrentPlugin) {
694 APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
695 api.xmlElementBegin(name, attmap);
696 }
697
698 }
699
elementCharCallback(void *,const XML_Char * data,int size)700 void PluginCache::elementCharCallback(void */*userData*/, const XML_Char *data, int size)
701 {
702 if (_ignoreCache) {
703 return;
704 }
705
706 std::string s(data, size);
707 if (_xmlCurrentPlugin) {
708 APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
709 api.xmlCharacterHandler(s);
710 } else {
711 /// XXX: we only want whitespace
712 }
713 }
714
elementEndCallback(void *,const XML_Char * name)715 void PluginCache::elementEndCallback(void */*userData*/, const XML_Char *name) {
716 if (_ignoreCache) {
717 return;
718 }
719
720 std::string ename = name;
721
722 /// XXX: validation?
723
724 if (ename == "plugin") {
725 if (_xmlCurrentPlugin) {
726 APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
727 api.endXmlParsing();
728 }
729 _xmlCurrentPlugin = 0;
730 return;
731 }
732
733 if (ename == "bundle") {
734 _xmlCurrentBinary = 0;
735 return;
736 }
737
738 if (_xmlCurrentPlugin) {
739 APICache::PluginAPICacheI &api = _xmlCurrentPlugin->getApiHandler();
740 api.xmlElementEnd(name);
741 }
742 }
743
readCache(std::istream & ifs)744 void PluginCache::readCache(std::istream &ifs) {
745 XML_Parser xP = XML_ParserCreate(NULL);
746 if (!xP) {
747 throw std::runtime_error("Error creating XML parser");
748
749 }
750 XML_SetElementHandler(xP, elementBeginHandler, elementEndHandler);
751 XML_SetCharacterDataHandler(xP, elementCharHandler);
752 int done = false;
753 char buf[4096];
754 while (!done && !ifs.eof()) {
755 if (!ifs.good()) {
756 throw std::runtime_error("Error reading cache stream");
757 }
758 ifs.read(buf, sizeof(buf));
759 int n = (int)ifs.gcount();
760 done = (n != sizeof(buf));
761
762 if (XML_Parse(xP, buf, n, done) == XML_STATUS_ERROR) {
763 XML_Error errorCode = XML_GetErrorCode(xP);
764 XML_Size errorLine = XML_GetCurrentLineNumber(xP);
765 XML_Size errorCol = XML_GetCurrentColumnNumber(xP);
766 const XML_LChar *errorString = XML_ErrorString(errorCode);
767 std::stringstream errorDesc;
768 errorDesc << "XML parsing error at line " << errorLine << ":" << errorCol;
769 errorDesc << ": " << errorString;
770 XML_ParserFree(xP);
771 throw std::runtime_error(errorDesc.str());
772 }
773 }
774 XML_ParserFree(xP);
775 }
776
writePluginCache(std::ostream & os) const777 void PluginCache::writePluginCache(std::ostream &os) const {
778 #ifdef CACHE_DEBUG
779 printf("writing pluginCache with version = %s\n", _cacheVersion.c_str());
780 #endif
781
782 os << "<cache version=\"" << _cacheVersion << "\">\n";
783 for (std::list<PluginBinary *>::const_iterator i=_binaries.begin();i!=_binaries.end();i++) {
784 PluginBinary *b = *i;
785
786 std::vector<Plugin*> plugins(b->getNPlugins());
787 for (std::size_t j = 0; j < plugins.size(); ++j) {
788 plugins[j] = &b->getPlugin((int)j);
789 }
790
791 os << "<bundle>\n";
792 os << " <binary "
793 << XML::attribute("static_bin", int(b->isStaticallyLinkedPlugin()))
794 << XML::attribute("bundle_path", b->getBundlePath())
795 << XML::attribute("path", b->getFilePath())
796 << XML::attribute("mtime", int(b->getFileModificationTime()))
797 << XML::attribute("size", int(b->getFileSize())) << "/>\n";
798
799 for (int j=0;j<(int)plugins.size();j++) {
800 Plugin *p = plugins[j];
801
802
803 os << " <plugin "
804 << XML::attribute("name", p->getRawIdentifier())
805 << XML::attribute("index", p->getIndex())
806 << XML::attribute("api", p->getPluginApi())
807 << XML::attribute("api_version", p->getApiVersion())
808 << XML::attribute("major_version", p->getVersionMajor())
809 << XML::attribute("minor_version", p->getVersionMinor())
810 << ">\n";
811
812 const APICache::PluginAPICacheI &api = p->getApiHandler();
813 os << " <apiproperties>\n";
814 api.saveXML(p, os);
815 os << " </apiproperties>\n";
816
817 os << " </plugin>\n";
818 }
819 os << "</bundle>\n";
820 }
821 os << "</cache>\n";
822 }
823
824
findApiHandler(const std::string & api,int version)825 APICache::PluginAPICacheI *PluginCache::findApiHandler(const std::string &api, int version) {
826 std::list<PluginCacheSupportedApi>::iterator i = _apiHandlers.begin();
827 while (i != _apiHandlers.end()) {
828 if (i->matches(api, version)) {
829 return i->handler;
830 }
831 i++;
832 }
833 return 0;
834 }
835