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 | |
21 | Plugins::Plugins(void) { } |
22 | Plugins::~Plugins(void) |
23 | { |
24 | for (auto function : functions) { |
25 | sass_delete_function(entry: function); |
26 | } |
27 | for (auto importer : importers) { |
28 | sass_delete_importer(cb: importer); |
29 | } |
30 | for (auto : headers) { |
31 | sass_delete_importer(cb: 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 |
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(s1: their_version, s2: "[na]" )) return false; |
44 | if (!strcmp(s1: our_version, s2: "[na]" )) return false; |
45 | |
46 | // find the position of the second dot |
47 | size_t pos = sass::string(our_version).find(c: '.', pos: 0); |
48 | if (pos != sass::string::npos) pos = sass::string(our_version).find(c: '.', pos: 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(s1: their_version, s2: our_version) ? 0 : 1; } |
52 | // otherwise only compare up to the second dot (major versions) |
53 | else { return strncmp(s1: their_version, s2: our_version, n: pos) ? 0 : 1; } |
54 | |
55 | } |
56 | |
57 | // load one specific plugin |
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(their_version: 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(x: *fns); ++ fns; } |
77 | sass_free_memory(ptr: _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(x: *imps); ++ imps; } |
84 | sass_free_memory(ptr: _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__, , "libsass_load_headers" )) |
88 | { |
89 | Sass_Importer_List imps = plugin_load_headers(), _p = imps; |
90 | while (imps && *imps) { headers.push_back(x: *imps); ++ imps; } |
91 | sass_free_memory(ptr: _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 | |
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(name: path.c_str())) == NULL) return -1; |
173 | while ((dirp = readdir(dirp: dp)) != NULL) { |
174 | #if __APPLE__ |
175 | if (!ends_with(dirp->d_name, ".dylib" )) continue; |
176 | #else |
177 | if (!ends_with(str: dirp->d_name, suffix: ".so" )) continue; |
178 | #endif |
179 | if (load_plugin(path: path + dirp->d_name)) ++ loaded; |
180 | } |
181 | closedir(dirp: dp); |
182 | |
183 | #endif |
184 | return loaded; |
185 | |
186 | } |
187 | |
188 | } |
189 | |