Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
module.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2017 Tobias Ellinghaus.
4 * Copyright (C) 2020-2021 Pascal Obry.
5 * Copyright (C) 2021 Ralf Brown.
6 * Copyright (C) 2022 Martin Bařinka.
7 * Copyright (C) 2023 Alynx Zhou.
8 * Copyright (C) 2023, 2025-2026 Aurélien PIERRE.
9 *
10 * darktable is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * darktable is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "system/macros.h"
25#include "system/mem_alloc.h"
26#include <stdlib.h>
27#include <string.h>
28#include <gmodule.h>
29#include <glib/gi18n.h>
30
31#include "config.h"
33#include "common/logging.h"
34#include "common/module.h"
35#include "common/paths.h" // DT_PATH_MAX
36#include "common/conf.h"
37
46static gboolean _manifest_entry_is_sane(const char *entry)
47{
48 if(IS_NULL_PTR(entry) || *entry == '\0') return FALSE;
49 if(*entry == '.') return FALSE;
50 if(!IS_NULL_PTR(strchr(entry, '/'))) return FALSE;
51 if(!IS_NULL_PTR(strchr(entry, '\\'))) return FALSE;
52 if(!IS_NULL_PTR(strstr(entry, ".."))) return FALSE;
53 return TRUE;
54}
55
56gchar **dt_module_read_manifest(const char *subdir, char *moduledir)
57{
58 moduledir[0] = '\0';
60 g_strlcat(moduledir, subdir, DT_PATH_MAX);
61
62 gchar *manifest_path = g_build_filename(moduledir, DT_MODULE_MANIFEST_NAME, NULL);
63 gchar *contents = NULL;
64 GError *error = NULL;
65
66 if(!g_file_get_contents(manifest_path, &contents, NULL, &error))
67 {
68 /* No manifest, no modules. This used to be a g_dir_open() scan that loaded every
69 * shared object it found, which meant an install directory a failed upgrade had
70 * left in a mixed state could hand us a module built against a struct layout that
71 * has since moved -- accepted, because DT_MODULE_VERSION had not changed, and then
72 * writing through offsets that no longer exist. Refusing to load beats guessing. */
73 dt_print(DT_DEBUG_ALWAYS, "[dt_module_load_modules] no module manifest at `%s': %s\n"
74 " No module will be loaded from that directory. This is an\n"
75 " incomplete or damaged installation -- reinstall Ansel.\n",
76 manifest_path, error ? error->message : "unknown error");
77 if(error) g_error_free(error);
78 g_free(manifest_path);
79 return NULL;
80 }
81
82 gchar **lines = g_strsplit(contents, "\n", -1);
83 g_free(contents);
84
85 GPtrArray *names = g_ptr_array_new();
86 for(gchar **line = lines; !IS_NULL_PTR(*line); line++)
87 {
88 gchar *entry = g_strstrip(*line);
89 if(*entry == '\0' || *entry == '#') continue;
90
91 if(!_manifest_entry_is_sane(entry))
92 {
93 dt_print(DT_DEBUG_ALWAYS, "[dt_module_load_modules] `%s' lists `%s', which is not a module name. Ignored.\n",
94 manifest_path, entry);
95 continue;
96 }
97 g_ptr_array_add(names, g_strdup(entry));
98 }
99 g_ptr_array_add(names, NULL);
100 g_strfreev(lines);
101 g_free(manifest_path);
102
103 return (gchar **)g_ptr_array_free(names, FALSE);
104}
105
106GList *dt_module_load_modules(const char *subdir, size_t module_size,
107 int (*load_module_so)(void *module, const char *libname, const char *plugin_name),
108 void (*init_module)(void *module),
109 gint (*sort_modules)(gconstpointer a, gconstpointer b))
110{
111 GList *plugin_list = NULL;
112 char moduledir[DT_PATH_MAX] = { 0 };
113
114 gchar **plugin_names = dt_module_read_manifest(subdir, moduledir);
115 if(IS_NULL_PTR(plugin_names)) return NULL;
116
117 for(gchar **name = plugin_names; !IS_NULL_PTR(*name); name++)
118 {
119 const char *plugin_name = *name;
120 void *module = calloc(1, module_size);
121 gchar *libname = g_module_build_path(moduledir, plugin_name);
122
123 int res = 1;
124
125 // Get the preference to enable/disable the plugin.
126 gchar *pref_line = g_strdup_printf("%s/%s/enable", subdir, plugin_name);
127 int load;
128
129 if(dt_conf_key_exists(pref_line))
130 {
131 // Disable plugins only if we have an explicit rule saying so.
132 load = dt_conf_get_bool(pref_line);
133 }
134 else
135 {
136 // If no rule, then enable by default.
137 load = TRUE;
138 dt_conf_set_bool(pref_line, TRUE);
139 }
140
141 dt_free(pref_line);
142
143 if(load) res = load_module_so(module, libname, plugin_name);
144
145 dt_free(libname);
146
147 if(res)
148 {
149 dt_free(module);
150 continue;
151 }
152
153 plugin_list = g_list_prepend(plugin_list, module);
154
155 if(init_module) init_module(module);
156 }
157
158 g_strfreev(plugin_names);
159
160 if(sort_modules)
161 plugin_list = g_list_sort(plugin_list, sort_modules);
162 else
163 plugin_list = g_list_reverse(plugin_list); // list was built in reverse order, so un-reverse it
164
165 return plugin_list;
166}
167
168// clang-format off
169// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
170// vim: shiftwidth=2 expandtab tabstop=2 cindent
171// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
172// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
Does key have a value?
#define DT_MODULE_MANIFEST_NAME
Name of the manifest each module directory must carry.
GHashTable * names
GtkWidget* -> the name the application knows it by.
const int res
Definition dtpthread.h:351
void dt_loc_get_moduledir(char *moduledir, size_t bufsize)
@ DT_DEBUG_ALWAYS
Definition logging.h:49
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
GList * dt_module_load_modules(const char *subdir, size_t module_size, int(*load_module_so)(void *module, const char *libname, const char *plugin_name), void(*init_module)(void *module), gint(*sort_modules)(gconstpointer a, gconstpointer b))
Load every module the given directory's manifest lists.
Definition module.c:106
gchar ** dt_module_read_manifest(const char *subdir, char *moduledir)
Read a module directory's manifest and return the module base names it lists.
Definition module.c:56
static gboolean _manifest_entry_is_sane(const char *entry)
Is this manifest entry a plain module base name?
Definition module.c:46
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90