#include "cog-modules.h"
#include "cog-config.h"
#include "cog-fallback-platform.h"
struct ExtensionPoints {
GIOExtensionPoint *platform;
};
static void *
ensure_extension_points_internal(void *func)
{
static struct ExtensionPoints ep = {};
ep.platform = g_io_extension_point_register(COG_MODULES_PLATFORM_EXTENSION_POINT);
g_debug("%s: Extension points registered.", (const char *) func);
return &ep;
}
const struct ExtensionPoints *
ensure_extension_points(void)
{
static GOnce once = G_ONCE_INIT;
g_once(&once, ensure_extension_points_internal, (void *) G_STRFUNC);
return once.retval;
}
static void *
ensure_builtin_types_internal(void *func)
{
ensure_extension_points();
g_type_ensure(cog_fallback_platform_get_type());
g_debug("%s: Built-in platform types initialized.", (const char *) func);
return NULL;
}
void
ensure_builtin_types(void)
{
static GOnce once = G_ONCE_INIT;
g_once(&once, ensure_builtin_types_internal, (void *) G_STRFUNC);
}
GIOExtensionPoint *
cog_modules_get_platform_extension_point(void)
{
return ensure_extension_points()->platform;
}
static gboolean
can_use_extension(GIOExtension *extension, size_t is_supported_offset)
{
if (extension == NULL)
return FALSE;
if (is_supported_offset == 0)
return TRUE;
typedef gboolean (*VerifyFunction)(void);
GType type = g_io_extension_get_type(extension);
g_autoptr(GTypeClass) type_class = g_type_class_ref(type);
return (*G_STRUCT_MEMBER(VerifyFunction, type_class, is_supported_offset))();
}
GType
cog_modules_get_preferred(GIOExtensionPoint *extension_point, const char *preferred_module, size_t is_supported_offset)
{
g_return_val_if_fail(extension_point != NULL, G_TYPE_INVALID);
ensure_builtin_types();
cog_modules_add_directory(NULL);
if (extension_point == COG_MODULES_PLATFORM && g_strcmp0(preferred_module, "fdo") == 0) {
g_warning("Platform module name 'fdo' is deprecated, please use 'wl' instead.");
preferred_module = "wl";
}
GIOExtension *extension, *chosen = NULL;
if (preferred_module) {
extension = g_io_extension_point_get_extension_by_name(extension_point, preferred_module);
if (can_use_extension(extension, is_supported_offset))
chosen = extension;
else if (!extension)
g_warning("%s: cannot find module '%s'", G_STRFUNC, preferred_module);
if (!chosen) {
g_warning("%s: preferred module '%s' not supported", G_STRFUNC, preferred_module);
return G_TYPE_INVALID;
}
}
GList *item = g_list_first(g_io_extension_point_get_extensions(extension_point));
while (!chosen && item) {
if (can_use_extension(item->data, is_supported_offset))
chosen = item->data;
item = g_list_next(item);
}
return chosen ? g_io_extension_get_type(chosen) : G_TYPE_INVALID;
}
void
cog_modules_foreach(GIOExtensionPoint *extension_point, void (*callback)(GIOExtension *, void *), void *userdata)
{
g_return_if_fail(extension_point != NULL);
g_return_if_fail(callback != NULL);
ensure_builtin_types();
cog_modules_add_directory(NULL);
GList *item = g_list_first(g_io_extension_point_get_extensions(extension_point));
while (item) {
(*callback)(item->data, userdata);
item = g_list_next(item);
}
}
void
cog_modules_add_directory(const char *directory_path)
{
g_autoptr(GFile) path_file = NULL;
if (directory_path) {
path_file = g_file_new_for_path(directory_path);
GFileType path_type = g_file_query_file_type(path_file, G_FILE_QUERY_INFO_NONE, NULL);
g_return_if_fail(path_type == G_FILE_TYPE_DIRECTORY);
directory_path = g_file_peek_path(path_file);
}
ensure_extension_points();
G_LOCK_DEFINE_STATIC(module_scan);
G_LOCK(module_scan);
static gboolean default_path_added = FALSE;
if (default_path_added) {
g_debug("%s: Default path already added, skipping.", G_STRFUNC);
goto out; }
if (directory_path) {
default_path_added = !strcmp(COG_MODULEDIR, directory_path);
} else {
g_assert(!path_file);
directory_path = COG_MODULEDIR;
default_path_added = TRUE;
static gboolean env_path_checked = FALSE;
if (!env_path_checked) {
env_path_checked = TRUE;
const char *env_value = g_getenv("COG_MODULEDIR");
if (env_value) {
path_file = g_file_new_for_path(env_value);
if (g_file_query_file_type(path_file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) {
directory_path = g_file_peek_path(path_file);
} else {
g_warning("Path '%s' is not a directory.", env_value);
}
}
}
}
static GIOModuleScope *scope = NULL;
if (!scope)
scope = g_io_module_scope_new(G_IO_MODULE_SCOPE_BLOCK_DUPLICATES);
g_debug("%s: Scanning '%s'", G_STRFUNC, directory_path);
g_io_modules_scan_all_in_directory_with_scope(directory_path, scope);
out:
G_UNLOCK(module_scan);
}