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