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