PIKApp/libpikamodule/pikamoduledb.c

485 lines
12 KiB
C

/* 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
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gio/gio.h>
#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");
}
}