1/* -*- mode: C; c-file-style: "linux" -*- */
2/* GdkPixbuf library
3 * queryloaders.c:
4 *
5 * Copyright (C) 2002 The Free Software Foundation
6 *
7 * Author: Matthias Clasen <maclas@gmx.de>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include "config.h"
24
25#include <glib.h>
26#include <glib/gprintf.h>
27#include <gmodule.h>
28
29#include <errno.h>
30#include <string.h>
31#ifdef HAVE_UNISTD_H
32#include <unistd.h>
33#endif
34
35#include "gdk-pixbuf/gdk-pixbuf.h"
36#include "gdk-pixbuf/gdk-pixbuf-private.h"
37
38#ifdef USE_LA_MODULES
39#define SOEXT ".la"
40#else
41#define SOEXT ("." G_MODULE_SUFFIX)
42#endif
43#define SOEXT_LEN (strlen (SOEXT))
44
45#ifdef G_OS_WIN32
46#include <windows.h>
47#endif
48
49#ifdef OS_DARWIN
50#include <mach-o/dyld.h>
51#endif
52
53static void
54print_escaped (GString *contents, const char *str)
55{
56 gchar *tmp = g_strescape (source: str, exceptions: "");
57 g_string_append_printf (string: contents, format: "\"%s\" ", tmp);
58 g_free (mem: tmp);
59}
60
61static int
62loader_sanity_check (const char *path, GdkPixbufFormat *info, GdkPixbufModule *vtable)
63{
64 const GdkPixbufModulePattern *pattern;
65 const char *error = "";
66
67 for (pattern = info->signature; pattern->prefix; pattern++)
68 {
69 int prefix_len = strlen (s: pattern->prefix);
70 if (prefix_len == 0)
71 {
72 error = "empty pattern";
73
74 goto error;
75 }
76 if (pattern->mask)
77 {
78 int mask_len = strlen (s: pattern->mask);
79 if (mask_len != prefix_len)
80 {
81 error = "mask length mismatch";
82
83 goto error;
84 }
85 if (strspn (s: pattern->mask, accept: " !xzn*") < mask_len)
86 {
87 error = "bad char in mask";
88
89 goto error;
90 }
91 }
92 }
93
94 if (!vtable->load && !vtable->begin_load && !vtable->load_animation)
95 {
96 error = "no load method implemented";
97
98 goto error;
99 }
100
101 if (vtable->begin_load && (!vtable->stop_load || !vtable->load_increment))
102 {
103 error = "incremental loading support incomplete";
104
105 goto error;
106 }
107
108 if ((info->flags & GDK_PIXBUF_FORMAT_WRITABLE) && !(vtable->save || vtable->save_to_callback))
109 {
110 error = "loader claims to support saving but doesn't implement save";
111 goto error;
112 }
113
114 return 1;
115
116 error:
117 g_fprintf (stderr, format: "Loader sanity check failed for %s: %s\n",
118 path, error);
119
120 return 0;
121}
122
123#ifdef GDK_PIXBUF_RELOCATABLE
124
125/* Based on gdk_pixbuf_get_toplevel () */
126static gchar *
127get_toplevel (void)
128{
129 static gchar *toplevel = NULL;
130
131 if (toplevel == NULL) {
132#if defined (G_OS_WIN32)
133 toplevel = g_win32_get_package_installation_directory_of_module (NULL);
134#elif defined(OS_DARWIN)
135 char pathbuf[PATH_MAX + 1];
136 uint32_t bufsize = sizeof (pathbuf);
137 gchar *bin_dir;
138
139 _NSGetExecutablePath (pathbuf, &bufsize);
140 bin_dir = g_dirname (pathbuf);
141 toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, "..", NULL);
142 g_free (bin_dir);
143#elif defined (OS_LINUX) || defined (__MINGW32__)
144 gchar *exe_path, *bin_dir;
145
146 exe_path = g_file_read_link ("/proc/self/exe", NULL);
147 bin_dir = g_dirname (exe_path);
148 toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, "..", NULL);
149 g_free (exe_path);
150 g_free (bin_dir);
151#else
152#error "Relocations not supported for this platform"
153#endif
154 }
155 return toplevel;
156}
157
158/* Returns the relative path or NULL; transfer full */
159static gchar *
160get_relative_path (const gchar *parent, const gchar *descendant)
161{
162 GFile *parent_file, *descendant_file;
163 char *relative_path;
164
165 parent_file = g_file_new_for_path (parent);
166 descendant_file = g_file_new_for_path (descendant);
167 relative_path = g_file_get_relative_path (parent_file, descendant_file);
168 g_object_unref (parent_file);
169 g_object_unref (descendant_file);
170
171 return relative_path;
172}
173
174#endif /* GDK_PIXBUF_RELOCATABLE */
175
176static void
177write_loader_info (GString *contents, const char *path, GdkPixbufFormat *info)
178{
179 const GdkPixbufModulePattern *pattern;
180 char **mime;
181 char **ext;
182 gchar *module_path = NULL, *escaped_path;
183
184#ifdef GDK_PIXBUF_RELOCATABLE
185 module_path = get_relative_path (get_toplevel (), path);
186#endif
187
188 if (module_path == NULL) {
189 module_path = g_strdup (str: path);
190 }
191
192 escaped_path = g_strescape (source: module_path, exceptions: "");
193 g_string_append_printf (string: contents, format: "\"%s\"\n", escaped_path);
194 g_free (mem: module_path);
195 g_free (mem: escaped_path);
196
197 g_string_append_printf (string: contents, format: "\"%s\" %u \"%s\" \"%s\" \"%s\"\n",
198 info->name,
199 info->flags,
200 info->domain ? info->domain : GETTEXT_PACKAGE,
201 info->description,
202 info->license ? info->license : "");
203 for (mime = info->mime_types; *mime; mime++) {
204 g_string_append_printf (string: contents, format: "\"%s\" ", *mime);
205 }
206 g_string_append (string: contents, val: "\"\"\n");
207 for (ext = info->extensions; *ext; ext++) {
208 g_string_append_printf (string: contents, format: "\"%s\" ", *ext);
209 }
210 g_string_append (string: contents, val: "\"\"\n");
211 for (pattern = info->signature; pattern->prefix; pattern++) {
212 print_escaped (contents, str: pattern->prefix);
213 print_escaped (contents, str: pattern->mask ? (const char *)pattern->mask : "");
214 g_string_append_printf (string: contents, format: "%d\n", pattern->relevance);
215 }
216 g_string_append_c (contents, '\n');
217}
218
219static void
220query_module (GString *contents, const char *dir, const char *file)
221{
222 char *path;
223 GModule *module;
224 void (*fill_info) (GdkPixbufFormat *info);
225 void (*fill_vtable) (GdkPixbufModule *module);
226 gpointer fill_info_ptr;
227 gpointer fill_vtable_ptr;
228
229 if (g_path_is_absolute (file_name: file))
230 path = g_strdup (str: file);
231 else
232 path = g_build_filename (first_element: dir, file, NULL);
233
234 module = g_module_open (file_name: path, flags: 0);
235 if (module &&
236 g_module_symbol (module, symbol_name: "fill_info", symbol: &fill_info_ptr) &&
237 g_module_symbol (module, symbol_name: "fill_vtable", symbol: &fill_vtable_ptr)) {
238 GdkPixbufFormat *info;
239 GdkPixbufModule *vtable;
240
241#ifdef G_OS_WIN32
242 /* Replace backslashes in path with forward slashes, so that
243 * it reads in without problems.
244 */
245 {
246 char *p = path;
247 while (*p) {
248 if (*p == '\\')
249 *p = '/';
250 p++;
251 }
252 }
253#endif
254 info = g_new0 (GdkPixbufFormat, 1);
255 vtable = g_new0 (GdkPixbufModule, 1);
256
257 vtable->module = module;
258
259 fill_info = fill_info_ptr;
260 fill_vtable = fill_vtable_ptr;
261
262 (*fill_info) (info);
263 (*fill_vtable) (vtable);
264
265 if (loader_sanity_check (path, info, vtable))
266 write_loader_info (contents, path, info);
267
268 g_free (mem: info);
269 g_free (mem: vtable);
270 }
271 else {
272 if (module == NULL)
273 g_fprintf (stderr, format: "g_module_open() failed for %s: %s\n", path,
274 g_module_error());
275 else
276 g_fprintf (stderr, format: "Cannot load loader %s\n", path);
277 }
278 if (module)
279 g_module_close (module);
280 g_free (mem: path);
281}
282
283#if defined(G_OS_WIN32) && defined(GDK_PIXBUF_RELOCATABLE)
284
285static char *
286get_libdir (void)
287{
288 static char *libdir = NULL;
289
290 if (libdir == NULL)
291 libdir = g_build_filename (get_toplevel (), "lib", NULL);
292
293 return libdir;
294}
295
296#undef GDK_PIXBUF_LIBDIR
297#define GDK_PIXBUF_LIBDIR get_libdir()
298
299#endif
300
301static gchar *
302gdk_pixbuf_get_module_file (void)
303{
304 gchar *result = g_strdup (str: g_getenv (variable: "GDK_PIXBUF_MODULE_FILE"));
305
306 if (!result)
307 result = g_build_filename (GDK_PIXBUF_LIBDIR, "gdk-pixbuf-2.0", GDK_PIXBUF_BINARY_VERSION, "loaders.cache", NULL);
308
309 return result;
310}
311
312int main (int argc, char **argv)
313{
314 gint i;
315 const gchar *prgname;
316 GString *contents;
317 gchar *cache_file = NULL;
318 gint first_file = 1;
319 GFile *pixbuf_libdir_file;
320 gchar *pixbuf_libdir;
321
322#ifdef G_OS_WIN32
323 gchar *libdir;
324 GFile *pixbuf_prefix_file;
325 gchar *pixbuf_prefix;
326#endif
327
328 /* An intermediate GFile here will convert all the path separators
329 * to the right one used by the platform
330 */
331 pixbuf_libdir_file = g_file_new_for_path (PIXBUF_LIBDIR);
332 pixbuf_libdir = g_file_get_path (file: pixbuf_libdir_file);
333 g_object_unref (object: pixbuf_libdir_file);
334
335#ifdef G_OS_WIN32
336 pixbuf_prefix_file = g_file_new_for_path (GDK_PIXBUF_PREFIX);
337 pixbuf_prefix = g_file_get_path (pixbuf_prefix_file);
338 g_object_unref (pixbuf_prefix_file);
339
340 if (g_ascii_strncasecmp (pixbuf_libdir, pixbuf_prefix, strlen (pixbuf_prefix)) == 0 &&
341 G_IS_DIR_SEPARATOR (pixbuf_libdir[strlen (pixbuf_prefix)])) {
342 gchar *runtime_prefix;
343 gchar *slash;
344
345 /* pixbuf_prefix is a prefix of pixbuf_libdir, as it
346 * normally is. Replace that prefix in pixbuf_libdir
347 * with the installation directory on this machine.
348 * We assume this invokation of
349 * gdk-pixbuf-query-loaders is run from either a "bin"
350 * subdirectory of the installation directory, or in
351 * the installation directory itself.
352 */
353 wchar_t fn[1000];
354 GetModuleFileNameW (NULL, fn, G_N_ELEMENTS (fn));
355 runtime_prefix = g_utf16_to_utf8 (fn, -1, NULL, NULL, NULL);
356 slash = strrchr (runtime_prefix, '\\');
357 *slash = '\0';
358 slash = strrchr (runtime_prefix, '\\');
359 /* If running from some weird location, or from the
360 * build directory (either in the .libs folder where
361 * libtool places the real executable when using a
362 * wrapper, or directly from the gdk-pixbuf folder),
363 * use the compile-time libdir.
364 */
365 if (slash == NULL ||
366 g_ascii_strcasecmp (slash + 1, ".libs") == 0 ||
367 g_ascii_strcasecmp (slash + 1, "gdk-pixbuf") == 0) {
368 libdir = NULL;
369 }
370 else {
371 if (slash != NULL && g_ascii_strcasecmp (slash + 1, "bin") == 0) {
372 *slash = '\0';
373 }
374
375 libdir = g_build_filename (runtime_prefix,
376 pixbuf_libdir + strlen (pixbuf_prefix) + 1,
377 NULL);
378 }
379 }
380 else {
381 libdir = NULL;
382 }
383
384 g_free (pixbuf_prefix);
385
386 if (libdir != NULL) {
387 g_free (pixbuf_libdir);
388 pixbuf_libdir = libdir;
389 }
390#endif
391
392 /* This call is necessary to ensure we actually link against libgobject;
393 * otherwise it may be stripped if -Wl,--as-needed is in use.
394 *
395 * The reason we need to link against libgobject is because it now has
396 * a global constructor. If the dynamically loaded modules happen
397 * to dlclose() libgobject, then reopen it again, we're in for trouble.
398 *
399 * See: https://bugzilla.gnome.org/show_bug.cgi?id=686822
400 */
401 g_type_ensure (G_TYPE_OBJECT);
402
403 if (argc > 1 && strcmp (s1: argv[1], s2: "--update-cache") == 0) {
404 cache_file = gdk_pixbuf_get_module_file ();
405 first_file = 2;
406 }
407
408 contents = g_string_new (init: "");
409
410 prgname = g_get_prgname ();
411 g_string_append_printf (string: contents,
412 format: "# GdkPixbuf Image Loader Modules file\n"
413 "# Automatically generated file, do not edit\n"
414 "# Created by %s from gdk-pixbuf-%s\n"
415 "#\n",
416 (prgname ? prgname : "gdk-pixbuf-query-loaders"),
417 GDK_PIXBUF_VERSION);
418
419 if (argc == first_file) {
420#ifdef USE_GMODULE
421 char *moduledir;
422 GDir *dir;
423 GList *l, *modules;
424
425 moduledir = g_strdup (str: g_getenv (variable: "GDK_PIXBUF_MODULEDIR"));
426#ifdef G_OS_WIN32
427 if (moduledir != NULL && *moduledir != '\0') {
428 gchar *path;
429
430 path = g_locale_to_utf8 (moduledir, -1, NULL, NULL, NULL);
431 g_free (moduledir);
432 moduledir = path;
433 }
434#endif
435 if (moduledir == NULL || *moduledir == '\0') {
436 g_free (mem: moduledir);
437 moduledir = g_strdup (str: pixbuf_libdir);
438 }
439
440 g_string_append_printf (string: contents, format: "# LoaderDir = %s\n#\n", moduledir);
441
442 modules = NULL;
443 dir = g_dir_open (path: moduledir, flags: 0, NULL);
444 if (dir) {
445 const char *dent;
446
447 while ((dent = g_dir_read_name (dir))) {
448 gint len = strlen (s: dent);
449 if (len > SOEXT_LEN &&
450 strcmp (s1: dent + len - SOEXT_LEN, SOEXT) == 0) {
451 modules = g_list_prepend (list: modules,
452 data: g_strdup (str: dent));
453 }
454 }
455 g_dir_close (dir);
456 }
457 modules = g_list_sort (list: modules, compare_func: (GCompareFunc)strcmp);
458 for (l = modules; l != NULL; l = l->next)
459 query_module (contents, dir: moduledir, file: l->data);
460 g_list_free_full (list: modules, free_func: g_free);
461 g_free (mem: moduledir);
462#else
463 g_string_append_printf (contents, "# dynamic loading of modules not supported\n");
464#endif
465 }
466 else {
467 char *cwd = g_get_current_dir ();
468
469 for (i = first_file; i < argc; i++) {
470 char *infilename = argv[i];
471#ifdef G_OS_WIN32
472 infilename = g_locale_to_utf8 (infilename,
473 -1, NULL, NULL, NULL);
474#endif
475 query_module (contents, dir: cwd, file: infilename);
476 }
477 g_free (mem: cwd);
478 }
479
480 if (cache_file) {
481 GError *err;
482
483 err = NULL;
484 if (!g_file_set_contents (filename: cache_file, contents: contents->str, length: -1, error: &err)) {
485 g_fprintf (stderr, format: "%s\n", err->message);
486 }
487 }
488 else
489 g_print (format: "%s\n", contents->str);
490
491 g_free (mem: pixbuf_libdir);
492
493 return 0;
494}
495

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/queryloaders.c