1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
#include "sass.hpp"
#include <iostream>
#include "output.hpp"
#include "plugins.hpp"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <dlfcn.h>
#endif
namespace Sass {
Plugins::Plugins(void) { }
Plugins::~Plugins(void)
{
for (auto function : functions) {
sass_delete_function(function);
}
for (auto importer : importers) {
sass_delete_importer(importer);
}
for (auto header : headers) {
sass_delete_importer(header);
}
}
// check if plugin is compatible with this version
// plugins may be linked static against libsass
// we try to be compatible between major versions
inline bool compatibility(const char* their_version)
{
// const char* their_version = "3.1.2";
// first check if anyone has an unknown version
const char* our_version = libsass_version();
if (!strcmp(their_version, "[na]")) return false;
if (!strcmp(our_version, "[na]")) return false;
// find the position of the second dot
size_t pos = std::string(our_version).find('.', 0);
if (pos != std::string::npos) pos = std::string(our_version).find('.', pos + 1);
// if we do not have two dots we fallback to compare complete string
if (pos == std::string::npos) { return strcmp(their_version, our_version) ? 0 : 1; }
// otherwise only compare up to the second dot (major versions)
else { return strncmp(their_version, our_version, pos) ? 0 : 1; }
}
// load one specific plugin
bool Plugins::load_plugin (const std::string& path)
{
typedef const char* (*__plugin_version__)(void);
typedef Sass_Function_List (*__plugin_load_fns__)(void);
typedef Sass_Importer_List (*__plugin_load_imps__)(void);
if (LOAD_LIB(plugin, path))
{
// try to load initial function to query libsass version suppor
if (LOAD_LIB_FN(__plugin_version__, plugin_version, "libsass_get_version"))
{
// get the libsass version of the plugin
if (!compatibility(plugin_version())) return false;
// try to get import address for "libsass_load_functions"
if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions"))
{
Sass_Function_List fns = plugin_load_functions(), _p = fns;
while (fns && *fns) { functions.push_back(*fns); ++ fns; }
sass_free_memory(_p); // only delete the container, items not yet
}
// try to get import address for "libsass_load_importers"
if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_importers, "libsass_load_importers"))
{
Sass_Importer_List imps = plugin_load_importers(), _p = imps;
while (imps && *imps) { importers.push_back(*imps); ++ imps; }
sass_free_memory(_p); // only delete the container, items not yet
}
// try to get import address for "libsass_load_headers"
if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers"))
{
Sass_Importer_List imps = plugin_load_headers(), _p = imps;
while (imps && *imps) { headers.push_back(*imps); ++ imps; }
sass_free_memory(_p); // only delete the container, items not yet
}
// success
return true;
}
else
{
// print debug message to stderr (should not happen)
std::cerr << "failed loading 'libsass_support' in <" << path << ">" << std::endl;
if (const char* dlsym_error = dlerror()) std::cerr << dlsym_error << std::endl;
CLOSE_LIB(plugin);
}
}
else
{
// print debug message to stderr (should not happen)
std::cerr << "failed loading plugin <" << path << ">" << std::endl;
if (const char* dlopen_error = dlerror()) std::cerr << dlopen_error << std::endl;
}
return false;
}
size_t Plugins::load_plugins(const std::string& path)
{
// count plugins
size_t loaded = 0;
#ifdef _WIN32
try
{
// use wchar (utf16)
WIN32_FIND_DATAW data;
// trailing slash is guaranteed
std::string globsrch(path + "*.dll");
// convert to wide chars (utf16) for system call
std::wstring wglobsrch(UTF_8::convert_to_utf16(globsrch));
HANDLE hFile = FindFirstFileW(wglobsrch.c_str(), &data);
// check if system called returned a result
// ToDo: maybe we should print a debug message
if (hFile == INVALID_HANDLE_VALUE) return -1;
// read directory
while (true)
{
try
{
// the system will report the filenames with wide chars (utf16)
std::string entry = UTF_8::convert_from_utf16(data.cFileName);
// check if file ending matches exactly
if (!ends_with(entry, ".dll")) continue;
// load the plugin and increase counter
if (load_plugin(path + entry)) ++ loaded;
// check if there should be more entries
if (GetLastError() == ERROR_NO_MORE_FILES) break;
// load next entry (check for return type)
if (!FindNextFileW(hFile, &data)) break;
}
catch (...)
{
// report the error to the console (should not happen)
// seems like we got strange data from the system call?
std::cerr << "filename in plugin path has invalid utf8?" << std::endl;
}
}
}
catch (utf8::invalid_utf8)
{
// report the error to the console (should not happen)
// implementors should make sure to provide valid utf8
std::cerr << "plugin path contains invalid utf8" << std::endl;
}
#else
DIR *dp;
struct dirent *dirp;
if((dp = opendir(path.c_str())) == NULL) return -1;
while ((dirp = readdir(dp)) != NULL) {
#if __APPLE__
if (!ends_with(dirp->d_name, ".dylib")) continue;
#else
if (!ends_with(dirp->d_name, ".so")) continue;
#endif
if (load_plugin(path + dirp->d_name)) ++ loaded;
}
closedir(dp);
#endif
return loaded;
}
}
|