/* Copyright (c) 2015 Stanislaw Halik * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. */ #pragma once #include "plugin-api.hpp" #include "options.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 # include #endif #if defined(__APPLE__) # define OPENTRACK_SONAME "dylib" #elif defined(_WIN32) # define OPENTRACK_SONAME "dll" #else # define OPENTRACK_SONAME "so" #endif #include #ifdef _MSC_VER # define OPENTRACK_LIB_PREFIX "" #else # define OPENTRACK_LIB_PREFIX "lib" #endif extern "C" typedef void* (*OPENTRACK_CTOR_FUNPTR)(void); extern "C" typedef Metadata* (*OPENTRACK_METADATA_FUNPTR)(void); struct dylib { enum Type { Filter, Tracker, Protocol }; dylib(const QString& filename, Type t) : type(t), filename(filename), Dialog(nullptr), Constructor(nullptr), Meta(nullptr), handle(nullptr) { // otherwise dlopen opens the calling executable if (filename.size() == 0) return; #if defined(_WIN32) QString fullPath = QCoreApplication::applicationDirPath() + "/" + this->filename; handle = new QLibrary(fullPath); struct _foo { static bool die(QLibrary*& l, bool failp) { if (failp) { qDebug() << "failed" << l->errorString(); delete l; l = nullptr; } return failp; } }; if (_foo::die(handle, !handle->load())) return; Dialog = (OPENTRACK_CTOR_FUNPTR) handle->resolve("GetDialog"); if (_foo::die(handle, !Dialog)) return; Constructor = (OPENTRACK_CTOR_FUNPTR) handle->resolve("GetConstructor"); if (_foo::die(handle, !Constructor)) return; Meta = (OPENTRACK_METADATA_FUNPTR) handle->resolve("GetMetadata"); if (_foo::die(handle, !Meta)) return; #else QByteArray latin1 = QFile::encodeName(filename); handle = dlopen(latin1.constData(), # if defined(__APPLE__) RTLD_LOCAL|RTLD_FIRST|RTLD_NOW # else RTLD_LOCAL|RTLD_NOW // XXX RTLD_DEEPBIND on Linux? # endif ); struct _foo { static bool err(void*& handle) { const char* err = dlerror(); if (err) { fprintf(stderr, "Error, ignoring: %s\n", err); fflush(stderr); if (handle) dlclose(handle); handle = nullptr; return true; } return false; } }; if (handle) { if (_foo::err(handle)) return; Dialog = (OPENTRACK_CTOR_FUNPTR) dlsym(handle, "GetDialog"); if (_foo::err(handle)) return; Constructor = (OPENTRACK_CTOR_FUNPTR) dlsym(handle, "GetConstructor"); if (_foo::err(handle)) return; Meta = (OPENTRACK_METADATA_FUNPTR) dlsym(handle, "GetMetadata"); if (_foo::err(handle)) return; } else { (void) _foo::err(handle); return; } #endif auto m = mem(Meta()); icon = m->icon(); name = m->name(); } ~dylib() { #if defined(_WIN32) if (handle) delete handle; #else if (handle) (void) dlclose(handle); #endif } static QList> enum_libraries() { const char* filters_n[] = { "opentrack-filter-*.", "opentrack-tracker-*.", "opentrack-proto-*." }; const Type filters_t[] = { Filter, Tracker, Protocol }; QDir settingsDir( QCoreApplication::applicationDirPath() ); QList> ret; for (int i = 0; i < 3; i++) { QString filter = filters_n[i]; auto t = filters_t[i]; QStringList filenames = settingsDir.entryList(QStringList { OPENTRACK_LIB_PREFIX + filter + OPENTRACK_SONAME }, QDir::Files, QDir::Name); for (int i = 0; i < filenames.size(); i++) { QIcon icon; QString longName; QString str = filenames.at(i); auto lib = std::make_shared(str, t); qDebug() << "Loading" << str; std::cout.flush(); if (!get_metadata(lib, longName, icon)) continue; using d = const mem&; if (std::any_of(ret.cbegin(), ret.cend(), [&](d a) {return a->type == lib->type && a->name == lib->name;})) { qDebug() << "Duplicate lib" << lib->filename; continue; } ret.push_back(lib); } } return ret; } Type type; QString filename; QIcon icon; QString name; OPENTRACK_CTOR_FUNPTR Dialog; OPENTRACK_CTOR_FUNPTR Constructor; OPENTRACK_METADATA_FUNPTR Meta; private: #if defined(_WIN32) QLibrary* handle; #else void* handle; #endif static bool get_metadata(mem lib, QString& name, QIcon& icon) { Metadata* meta; if (!lib->Meta || ((meta = lib->Meta()), !meta)) return false; name = meta->name(); icon = meta->icon(); delete meta; return true; } }; struct Modules { Modules() : module_list(dylib::enum_libraries()), filter_modules(filter(dylib::Filter)), tracker_modules(filter(dylib::Tracker)), protocol_modules(filter(dylib::Protocol)) {} QList>& filters() { return filter_modules; } QList>& trackers() { return tracker_modules; } QList>& protocols() { return protocol_modules; } private: QList> module_list; QList> filter_modules; QList> tracker_modules; QList> protocol_modules; template static void sort(QList& xs) { std::sort(xs.begin(), xs.end(), [&](const t& a, const t& b) { return a->name.toLower() < b->name.toLower(); }); } QList> filter(dylib::Type t) { QList> ret; for (auto x : module_list) if (x->type == t) ret.push_back(x); sort(ret); return ret; } }; template mem make_dylib_instance(mem lib) { mem ret; if (lib != nullptr && lib->Constructor) ret = mem(reinterpret_cast(reinterpret_cast(lib->Constructor)())); return ret; }