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
19namespace 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 header : 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__, plugin_load_headers, "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

source code of gtk/subprojects/libsass/src/plugins.cpp