/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikaconfig/pikaconfig.h" #include "pikamoduletypes.h" #include "pikamodule.h" #include "pikamoduledb.h" #include "libpika/libpika-intl.h" /** * SECTION: pikamoduledb * @title: PikaModuleDB * @short_description: Keeps a list of #PikaModule's found in a given * searchpath. * * Keeps a list of #PikaModule's found in a given searchpath. **/ struct _PikaModuleDB { GObject parent_instance; GPtrArray *modules; gchar *load_inhibit; gboolean verbose; }; static void pika_module_db_finalize (GObject *object); static void pika_module_db_load_directory (PikaModuleDB *db, GFile *directory); static void pika_module_db_load_module (PikaModuleDB *db, GFile *file); static PikaModule * pika_module_db_module_find_by_file (PikaModuleDB *db, GFile *file); static void pika_module_db_module_dump_func (gpointer data, gpointer user_data); static void pika_module_db_list_model_iface_init (GListModelInterface *iface); G_DEFINE_TYPE_WITH_CODE (PikaModuleDB, pika_module_db, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, pika_module_db_list_model_iface_init)) #define parent_class pika_module_db_parent_class static void pika_module_db_class_init (PikaModuleDBClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = pika_module_db_finalize; } static void pika_module_db_init (PikaModuleDB *db) { db->modules = g_ptr_array_new (); db->load_inhibit = NULL; db->verbose = FALSE; } static void pika_module_db_finalize (GObject *object) { PikaModuleDB *db = PIKA_MODULE_DB (object); g_clear_pointer (&db->modules, g_ptr_array_unref); g_clear_pointer (&db->load_inhibit, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } static GType pika_module_db_get_item_type (GListModel *list) { return PIKA_TYPE_MODULE; } static guint pika_module_db_get_n_items (GListModel *list) { PikaModuleDB *self = PIKA_MODULE_DB (list); return self->modules->len; } static void * pika_module_db_get_item (GListModel *list, guint index) { PikaModuleDB *self = PIKA_MODULE_DB (list); if (index >= self->modules->len) return NULL; return g_object_ref (g_ptr_array_index (self->modules, index)); } static void pika_module_db_list_model_iface_init (GListModelInterface *iface) { iface->get_item_type = pika_module_db_get_item_type; iface->get_n_items = pika_module_db_get_n_items; iface->get_item = pika_module_db_get_item; } /** * pika_module_db_new: * @verbose: Pass %TRUE to enable debugging output. * * Creates a new #PikaModuleDB instance. The @verbose parameter will be * passed to the created #PikaModule instances using pika_module_new(). * * Returns: The new #PikaModuleDB instance. **/ PikaModuleDB * pika_module_db_new (gboolean verbose) { PikaModuleDB *db; db = g_object_new (PIKA_TYPE_MODULE_DB, NULL); db->verbose = verbose ? TRUE : FALSE; return db; } /** * pika_module_db_set_verbose: * @db: A #PikaModuleDB. * @verbose: the new 'verbose' setting * * Sets the 'verbose' setting of @db. * * Since: 3.0 **/ void pika_module_db_set_verbose (PikaModuleDB *db, gboolean verbose) { g_return_if_fail (PIKA_IS_MODULE_DB (db)); db->verbose = verbose ? TRUE : FALSE; } /** * pika_module_db_get_verbose: * @db: A #PikaModuleDB. * * Returns the 'verbose' setting of @db. * * Returns: the 'verbose' setting. * * Since: 3.0 **/ gboolean pika_module_db_get_verbose (PikaModuleDB *db) { g_return_val_if_fail (PIKA_IS_MODULE_DB (db), FALSE); return db->verbose; } static gboolean is_in_inhibit_list (GFile *file, const gchar *inhibit_list) { gchar *filename; gchar *p; gint pathlen; const gchar *start; const gchar *end; if (! inhibit_list || ! strlen (inhibit_list)) return FALSE; filename = g_file_get_path (file); p = strstr (inhibit_list, filename); if (! p) { g_free (filename); return FALSE; } /* we have a substring, but check for colons either side */ start = p; while (start != inhibit_list && *start != G_SEARCHPATH_SEPARATOR) start--; if (*start == G_SEARCHPATH_SEPARATOR) start++; end = strchr (p, G_SEARCHPATH_SEPARATOR); if (! end) end = inhibit_list + strlen (inhibit_list); pathlen = strlen (filename); g_free (filename); if ((end - start) == pathlen) return TRUE; return FALSE; } /** * pika_module_db_set_load_inhibit: * @db: A #PikaModuleDB. * @load_inhibit: A #G_SEARCHPATH_SEPARATOR delimited list of module * filenames to exclude from auto-loading. * * Sets the @load_inhibit flag for all #PikaModule's which are kept * by @db (using pika_module_set_load_inhibit()). **/ void pika_module_db_set_load_inhibit (PikaModuleDB *db, const gchar *load_inhibit) { guint i; g_return_if_fail (PIKA_IS_MODULE_DB (db)); g_free (db->load_inhibit); db->load_inhibit = g_strdup (load_inhibit); for (i = 0; i < db->modules->len; i++) { PikaModule *module = g_ptr_array_index (db->modules, i); gboolean inhibit; inhibit = is_in_inhibit_list (pika_module_get_file (module), load_inhibit); pika_module_set_auto_load (module, ! inhibit); } } /** * pika_module_db_get_load_inhibit: * @db: A #PikaModuleDB. * * Return the #G_SEARCHPATH_SEPARATOR delimited list of module filenames * which are excluded from auto-loading. * * Returns: the @db's @load_inhibit string. **/ const gchar * pika_module_db_get_load_inhibit (PikaModuleDB *db) { g_return_val_if_fail (PIKA_IS_MODULE_DB (db), NULL); return db->load_inhibit; } /** * pika_module_db_load: * @db: A #PikaModuleDB. * @module_path: A #G_SEARCHPATH_SEPARATOR delimited list of directories * to load modules from. * * Scans the directories contained in @module_path and creates a * #PikaModule instance for every loadable module contained in the * directories. **/ void pika_module_db_load (PikaModuleDB *db, const gchar *module_path) { g_return_if_fail (PIKA_IS_MODULE_DB (db)); g_return_if_fail (module_path != NULL); if (g_module_supported ()) { GList *path; GList *list; path = pika_config_path_expand_to_files (module_path, NULL); for (list = path; list; list = g_list_next (list)) { pika_module_db_load_directory (db, list->data); } g_list_free_full (path, (GDestroyNotify) g_object_unref); } if (FALSE) g_ptr_array_foreach (db->modules, pika_module_db_module_dump_func, NULL); } /** * pika_module_db_refresh: * @db: A #PikaModuleDB. * @module_path: A #G_SEARCHPATH_SEPARATOR delimited list of directories * to load modules from. * * Does the same as pika_module_db_load(), plus removes all #PikaModule * instances whose modules have been deleted from disk. * * Note that the #PikaModule's will just be removed from the internal * list and not freed as this is not possible with #GTypeModule * instances which actually implement types. **/ void pika_module_db_refresh (PikaModuleDB *db, const gchar *module_path) { guint i; g_return_if_fail (PIKA_IS_MODULE_DB (db)); g_return_if_fail (module_path != NULL); for (i = 0; i < db->modules->len; i++) { PikaModule *module = g_ptr_array_index (db->modules, i); if (! pika_module_is_on_disk (module) && ! pika_module_is_loaded (module)) { g_ptr_array_remove_index (db->modules, i); g_list_model_items_changed (G_LIST_MODEL (db), i, 1, 0); i--; } } /* walk filesystem and add new things we find */ pika_module_db_load (db, module_path); } static void pika_module_db_load_directory (PikaModuleDB *db, GFile *directory) { GFileEnumerator *enumerator; enumerator = g_file_enumerate_children (directory, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (enumerator) { GFileInfo *info; while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL))) { GFileType file_type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE); if (file_type == G_FILE_TYPE_REGULAR && ! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) { GFile *child = g_file_enumerator_get_child (enumerator, info); pika_module_db_load_module (db, child); g_object_unref (child); } g_object_unref (info); } g_object_unref (enumerator); } } static void pika_module_db_load_module (PikaModuleDB *db, GFile *file) { PikaModule *module; gboolean load_inhibit; if (! pika_file_has_extension (file, "." G_MODULE_SUFFIX)) return; /* don't load if we already know about it */ if (pika_module_db_module_find_by_file (db, file)) return; load_inhibit = is_in_inhibit_list (file, db->load_inhibit); module = pika_module_new (file, ! load_inhibit, db->verbose); g_ptr_array_add (db->modules, module); g_list_model_items_changed (G_LIST_MODEL (db), db->modules->len - 1, 0, 1); } static PikaModule * pika_module_db_module_find_by_file (PikaModuleDB *db, GFile *file) { guint i; for (i = 0; i < db->modules->len; i++) { PikaModule *module = g_ptr_array_index (db->modules, i); if (g_file_equal (file, pika_module_get_file (module))) return module; } return NULL; } static void pika_module_db_module_dump_func (gpointer data, gpointer user_data) { static const gchar * const statenames[] = { N_("Module error"), N_("Loaded"), N_("Load failed"), N_("Not loaded") }; PikaModule *module = data; const PikaModuleInfo *info = pika_module_get_info (module); g_print ("\n%s: %s\n", pika_file_get_utf8_name (pika_module_get_file (module)), gettext (statenames[pika_module_get_state (module)])); #if 0 g_print (" module: %p lasterr: %s query: %p register: %p\n", module->module, module->last_module_error ? module->last_module_error : "NONE", module->query_module, module->register_module); #endif if (info) { g_print (" purpose: %s\n" " author: %s\n" " version: %s\n" " copyright: %s\n" " date: %s\n", info->purpose ? info->purpose : "NONE", info->author ? info->author : "NONE", info->version ? info->version : "NONE", info->copyright ? info->copyright : "NONE", info->date ? info->date : "NONE"); } }