1 /* babl - dynamically extendable universal pixel conversion library.
2 * Copyright (C) 2005, Øyvind Kolås.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18
19 #define BABL_DYNAMIC_EXTENSIONS
20
21 #include "config.h"
22
23
24 #ifdef BABL_DYNAMIC_EXTENSIONS
25 /* must be defined before inclusion of babl-internal.h */
26 #undef BABL_INIT_HOOK
27 #define BABL_INIT_HOOK init_hook (); dynamic_init_hook ();
28 #endif
29
30 #define NEEDS_BABL_DB
31 #include "babl-internal.h"
32 #include "babl-db.h"
33 #include "babl-base.h"
34 #include <string.h>
35 #include <stdarg.h>
36
37 static Babl *babl_extension_current_extender = NULL;
38
39 Babl *
babl_extender(void)40 babl_extender (void)
41 {
42 if (babl_extension_current_extender)
43 return babl_extension_current_extender;
44 return NULL;
45 }
46
47 void
babl_set_extender(Babl * new_extender)48 babl_set_extender (Babl *new_extender)
49 {
50 babl_extension_current_extender = new_extender;
51 }
52
53 static int
54 babl_extension_destroy (void *data);
55
56 static Babl *
extension_new(const char * path,void * dl_handle,void (* destroy)(void))57 extension_new (const char *path,
58 void *dl_handle,
59 void (*destroy)(void))
60 {
61 Babl *babl;
62
63 babl = babl_malloc (sizeof (BablExtension) + strlen (path) + 1);
64 babl_set_destructor (babl, babl_extension_destroy);
65 babl->instance.name = (char *) babl + sizeof (BablExtension);
66 strcpy (babl->instance.name, path);
67 babl->instance.id = 0;
68 babl->class_type = BABL_EXTENSION;
69 babl->extension.dl_handle = dl_handle;
70 babl->extension.destroy = destroy;
71
72 return babl;
73 }
74
75 static Babl *babl_quiet = NULL;
76
77 Babl *
babl_extension_quiet_log(void)78 babl_extension_quiet_log (void)
79 {
80 if (babl_quiet)
81 return babl_quiet;
82 babl_quiet = extension_new ("", NULL, NULL);
83 return babl_quiet;
84 }
85
86 Babl *
babl_extension_base(void)87 babl_extension_base (void)
88 {
89 Babl *babl;
90 void *dl_handle = NULL;
91
92 void (*destroy)(void) = NULL;
93
94 if (!db)
95 {
96 babl_extension_quiet_log ();
97 babl_set_extender (NULL);
98 db = babl_db_init ();
99 }
100 babl = extension_new ("BablBase",
101 dl_handle,
102 destroy);
103 babl_set_extender (babl);
104
105 {
106 Babl *ret = babl_db_insert (db, babl);
107 if (ret != babl)
108 babl_free (babl);
109 else
110 babl_base_init ();
111 babl = ret;
112 }
113 babl_set_extender (NULL);
114 return babl;
115 }
116
117 void
babl_extension_deinit(void)118 babl_extension_deinit (void)
119 {
120 babl_free (babl_quiet);
121 babl_quiet = NULL;
122 }
123
124 #ifdef BABL_DYNAMIC_EXTENSIONS
125
126 #include <dirent.h>
127 #include <sys/types.h>
128 #include <sys/stat.h>
129 #include <unistd.h>
130
131 #ifdef HAVE_DLFCN_H
132 #ifndef WIN32
133
134 #include <dlfcn.h>
135 #define HLIB void *
136
137 #endif /* WIN32 */
138 #elif HAVE_DL_H
139
140
141 #include <dl.h>
142 #include <errno.h>
143 #if !defined(DYNAMIC_PATH)
144 # define DYNAMIC_PATH 0
145 #endif
146 #if !defined(BIND_RESTRICTED)
147 # define BIND_RESTRICTED 0
148 #endif
149 #define RTLD_NOW (BIND_IMMEDIATE|BIND_NONFATAL|DYNAMIC_PATH)
150 #define HLIB shl_t
151 #define dlopen(path, flags) shl_load (path, flags, 0L)
152 #define dlclose(handle) shl_unload (handle)
153 #define dlerror() strerror (errno)
154
155 static void *
dlsym(HLIB handle,const char * name)156 dlsym (HLIB handle,
157 const char *name)
158 {
159 void *address = 0;
160 shl_findsym(&handle, name, TYPE_UNDEFINED, &address);
161 return address;
162 }
163
164 #endif
165
166 #ifndef RTLD_NOW
167 #define RTLD_NOW 0
168 #endif
169
170 #ifdef WIN32
171 #define WIN32_LEAN_AND_MEAN
172 #include <windows.h>
173 #define HLIB HINSTANCE
174
175 #define dlopen(a, b) LoadLibrary (a)
176 #define dlsym(l, s) GetProcAddress (l, s)
177 #define dlclose(l) FreeLibrary (l)
178 #define dlerror() GetLastError ()
179 #endif
180
181 typedef int (*BablExtensionInitFunc) (void);
182 typedef void (*BablExtensionDestroyFunc)(void);
183
184
185 static Babl *
load_failed(Babl * babl)186 load_failed (Babl *babl)
187 {
188 if (babl)
189 {
190 babl_free (babl);
191 }
192 babl_set_extender (NULL);
193 return NULL;
194 }
195
196 static Babl *
babl_extension_load(const char * path)197 babl_extension_load (const char *path)
198 {
199 Babl *babl = NULL;
200 /* do the actual loading thing */
201 HLIB dl_handle = NULL;
202
203 BablExtensionInitFunc init = NULL;
204 BablExtensionDestroyFunc destroy = NULL;
205
206 dl_handle = dlopen (path, RTLD_NOW);
207 if (!dl_handle)
208 {
209 babl_log ("dlopen() failed:\n\t%s", dlerror ());
210 return load_failed (babl);
211 }
212 init = (BablExtensionInitFunc) dlsym (dl_handle, "init");
213 if (!init)
214 {
215 babl_log ("\n\tint babl_extension_init() function not found in extension '%s'", path);
216 dlclose (dl_handle);
217 return load_failed (babl);
218 }
219
220 destroy = (BablExtensionDestroyFunc) dlsym (dl_handle, "destroy");
221 babl = extension_new (path,
222 dl_handle,
223 destroy);
224
225 babl_set_extender (babl);
226 if (init ())
227 {
228 babl_log ("babl_extension_init() in extension '%s' failed (return!=0)", path);
229 dlclose (dl_handle);
230 return load_failed (babl);
231 }
232
233 babl_db_insert (db, babl);
234 if (babl == babl_db_exist_by_name (db, path))
235 {
236 babl_set_extender (NULL);
237 return babl;
238 }
239 else
240 {
241 return load_failed (babl);
242 }
243 }
244
245 static void
babl_extension_load_dir(const char * base_path)246 babl_extension_load_dir (const char *base_path)
247 {
248 DIR *dir;
249
250 if ((dir = opendir (base_path)))
251 {
252 struct dirent *dentry;
253
254 while ((dentry = readdir (dir)) != NULL)
255 {
256 if (dentry->d_name[0] != '.')
257 {
258 char *path = NULL;
259 char *extension;
260
261 path = babl_strcat (path, base_path);
262 path = babl_strcat (path, BABL_DIR_SEPARATOR);
263 path = babl_strcat (path, dentry->d_name);
264
265 if ((extension = strrchr (dentry->d_name, '.')) != NULL &&
266 !strcmp (extension, SHREXT))
267 {
268 babl_extension_load (path);
269 }
270
271 babl_free (path);
272 }
273 }
274 closedir (dir);
275 }
276 }
277
278 static char *
expand_path(char * path)279 expand_path (char *path)
280 {
281 char *src;
282 char *dst;
283
284 dst = NULL;
285
286 src = path;
287
288 while (*src)
289 {
290 char *home;
291 switch (*src)
292 {
293 case '~':
294 home = getenv ("HOME");
295 if (NULL != home)
296 dst = babl_strcat (dst, home);
297 break;
298
299 default:
300 {
301 char tmp[2] = "?";
302 tmp[0] = *src;
303 dst = babl_strcat (dst, tmp);
304 }
305 }
306 src++;
307 }
308 return dst;
309 }
310
311
312 /* parse the provided colon seperated list of paths to search
313 */
314 void
babl_extension_load_dir_list(const char * dir_list)315 babl_extension_load_dir_list (const char *dir_list)
316 {
317 int eos = 0;
318 const char *src;
319 char *path, *dst;
320
321
322 path = babl_strdup (dir_list);
323 src = dir_list;
324 dst = path;
325
326 while (!eos)
327 {
328 switch (*src)
329 {
330 case '\0':
331 eos = 1;
332 /* don't break here, the path needs to be processed */
333
334 case BABL_PATH_SEPARATOR:
335 {
336 char *expanded_path = expand_path (path);
337 if (expanded_path) {
338 babl_extension_load_dir (expanded_path);
339 babl_free (expanded_path);
340 }
341 }
342 dst = path;
343 src++;
344 *dst = '\0';
345 break;
346
347 default:
348 *(dst++) = *(src++);
349 *dst = '\0';
350 break;
351 }
352 }
353 babl_free (path);
354 if (babl_db_count (db) <= 1)
355 {
356 babl_log ("WARNING: the babl installation seems broken, no extensions found in queried\n"
357 "BABL_PATH (%s) this means no SIMD/instructions/special case fast paths and\n"
358 "only slow reference conversions are available, applications might still\n"
359 "run but software relying on babl for conversions will be slow\n", dir_list);
360 }
361 }
362
363 #endif
364
365
366 static int
babl_extension_destroy(void * data)367 babl_extension_destroy (void *data)
368 {
369 Babl *babl = data;
370 if (babl->extension.destroy)
371 babl->extension.destroy ();
372 if (babl->extension.dl_handle)
373 dlclose (babl->extension.dl_handle);
374 return 0;
375 }
376
377 BABL_CLASS_IMPLEMENT (extension)
378