logdiag/liblogdiag/ld-library.c

421 lines
9.4 KiB
C
Raw Permalink Normal View History

2010-09-13 19:24:53 +02:00
/*
2010-09-25 16:14:09 +02:00
* ld-library.c
2010-09-13 19:24:53 +02:00
*
* This file is a part of logdiag.
* Copyright 2010, 2011, 2012 Přemysl Eric Janouch
2010-09-13 19:24:53 +02:00
*
* See the file LICENSE for licensing information.
*
*/
2010-12-05 15:21:00 +01:00
#include <string.h>
#include "liblogdiag.h"
2010-09-13 19:24:53 +02:00
#include "config.h"
/**
2010-09-25 16:14:09 +02:00
* SECTION:ld-library
* @short_description: A symbol library
2012-08-29 18:44:47 +02:00
* @see_also: #LdSymbol, #LdCategory
2010-09-13 19:24:53 +02:00
*
* #LdLibrary is used for loading symbols from their files. The library object
* itself is a container for categories, which in turn contain other
* subcategories and the actual symbols.
2010-09-13 19:24:53 +02:00
*/
/*
2010-09-25 16:14:09 +02:00
* LdLibraryPrivate:
* @lua: state of the scripting language.
* @children: categories in the library.
2010-09-13 19:24:53 +02:00
*/
2010-09-25 16:14:09 +02:00
struct _LdLibraryPrivate
2010-09-13 19:24:53 +02:00
{
2010-09-25 16:55:07 +02:00
LdLua *lua;
2012-08-29 18:44:47 +02:00
LdCategory *root;
2010-09-13 19:24:53 +02:00
};
static void ld_library_finalize (GObject *gobject);
2010-09-13 19:24:53 +02:00
2012-08-29 18:44:47 +02:00
static LdCategory *load_category (LdLibrary *self,
const gchar *path, const gchar *name);
static gboolean load_category_cb (const gchar *base,
2012-08-29 17:07:26 +02:00
const gchar *path, gpointer userdata);
static void load_category_symbol_cb (LdSymbol *symbol, gpointer user_data);
static gchar *read_human_name_from_file (const gchar *filename);
static gboolean foreach_dir (const gchar *path,
gboolean (*callback) (const gchar *, const gchar *, gpointer),
gpointer userdata, GError **error);
static LdSymbol *traverse_path (LdCategory *category, gchar **path);
2010-09-13 19:24:53 +02:00
G_DEFINE_TYPE (LdLibrary, ld_library, G_TYPE_OBJECT)
2010-09-13 19:24:53 +02:00
static void
2010-09-25 16:14:09 +02:00
ld_library_class_init (LdLibraryClass *klass)
2010-09-13 19:24:53 +02:00
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
2010-09-25 16:14:09 +02:00
object_class->finalize = ld_library_finalize;
2010-09-13 19:24:53 +02:00
/**
2010-09-25 16:14:09 +02:00
* LdLibrary::changed:
* @self: an #LdLibrary object.
2010-09-13 19:24:53 +02:00
*
* Contents of the library have changed.
*/
klass->changed_signal = g_signal_new
2010-12-05 15:21:00 +01:00
("changed", G_TYPE_FROM_CLASS (klass),
2010-12-12 18:17:13 +01:00
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
2010-09-13 19:24:53 +02:00
2010-09-25 16:14:09 +02:00
g_type_class_add_private (klass, sizeof (LdLibraryPrivate));
2010-09-13 19:24:53 +02:00
}
static void
2010-09-25 16:14:09 +02:00
ld_library_init (LdLibrary *self)
2010-09-13 19:24:53 +02:00
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE
2010-09-25 16:14:09 +02:00
(self, LD_TYPE_LIBRARY, LdLibraryPrivate);
2010-09-13 19:24:53 +02:00
2010-09-25 16:55:07 +02:00
self->priv->lua = ld_lua_new ();
self->priv->root = ld_category_new (LD_LIBRARY_IDENTIFIER_SEPARATOR, "/");
}
2010-09-13 19:24:53 +02:00
static void
2010-09-25 16:14:09 +02:00
ld_library_finalize (GObject *gobject)
2010-09-13 19:24:53 +02:00
{
2010-09-25 16:14:09 +02:00
LdLibrary *self;
2010-09-13 19:24:53 +02:00
2010-09-25 16:14:09 +02:00
self = LD_LIBRARY (gobject);
2010-09-13 19:24:53 +02:00
2010-09-25 16:55:07 +02:00
g_object_unref (self->priv->lua);
g_object_unref (self->priv->root);
2010-09-13 19:24:53 +02:00
/* Chain up to the parent class. */
2010-09-25 16:14:09 +02:00
G_OBJECT_CLASS (ld_library_parent_class)->finalize (gobject);
2010-09-13 19:24:53 +02:00
}
/**
2010-09-25 16:14:09 +02:00
* ld_library_new:
2010-09-13 19:24:53 +02:00
*
* Create an instance.
*/
2010-09-25 16:14:09 +02:00
LdLibrary *
ld_library_new (void)
2010-09-13 19:24:53 +02:00
{
2010-09-25 16:14:09 +02:00
return g_object_new (LD_TYPE_LIBRARY, NULL);
2010-09-13 19:24:53 +02:00
}
/*
* foreach_dir:
*
* Call a user-defined function for each file within a directory.
*/
static gboolean
foreach_dir (const gchar *path,
gboolean (*callback) (const gchar *, const gchar *, gpointer),
gpointer userdata, GError **error)
{
GDir *dir;
const gchar *item;
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (callback != NULL, FALSE);
dir = g_dir_open (path, 0, error);
if (!dir)
return FALSE;
while ((item = g_dir_read_name (dir)))
{
gchar *filename;
filename = g_build_filename (path, item, NULL);
if (!callback (item, filename, userdata))
break;
g_free (filename);
}
g_dir_close (dir);
return TRUE;
}
/*
* LoadCategoryData:
*
* Data shared between load_category() and load_category_cb().
*/
typedef struct
{
LdLibrary *self;
2012-08-29 18:44:47 +02:00
LdCategory *cat;
2012-08-29 17:07:26 +02:00
guint changed : 1;
guint load_symbols : 1;
}
LoadCategoryData;
2010-09-13 19:24:53 +02:00
/*
* load_category:
* @self: an #LdLibrary object.
* @path: the path to the category.
* @name: the default name of the category.
2010-09-13 19:24:53 +02:00
*
* Loads a category into the library.
*/
2012-08-29 18:44:47 +02:00
static LdCategory *
load_category (LdLibrary *self, const gchar *path, const gchar *name)
2010-09-13 19:24:53 +02:00
{
2012-08-29 15:46:20 +02:00
gchar *category_file, *human_name;
LoadCategoryData data;
2010-09-13 19:24:53 +02:00
2010-09-25 16:14:09 +02:00
g_return_val_if_fail (LD_IS_LIBRARY (self), NULL);
2010-09-13 19:24:53 +02:00
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
if (!g_file_test (path, G_FILE_TEST_IS_DIR))
2012-08-29 15:46:20 +02:00
return NULL;
2010-09-13 19:24:53 +02:00
category_file = g_build_filename (path, "category.json", NULL);
human_name = read_human_name_from_file (category_file);
if (!human_name)
human_name = g_strdup (name);
2010-09-13 19:24:53 +02:00
data.self = self;
2012-08-29 18:44:47 +02:00
data.cat = ld_category_new (name, human_name);
2012-08-29 17:07:26 +02:00
data.load_symbols = TRUE;
data.changed = FALSE;
foreach_dir (path, load_category_cb, &data, NULL);
g_free (human_name);
g_free (category_file);
2012-08-29 15:46:20 +02:00
return data.cat;
2010-09-13 19:24:53 +02:00
}
/*
* load_category_cb:
*
2012-08-29 17:07:26 +02:00
* Load contents of a directory into a symbol category.
*/
static gboolean
2012-08-29 17:07:26 +02:00
load_category_cb (const gchar *base, const gchar *path, gpointer userdata)
{
LoadCategoryData *data;
g_return_val_if_fail (base != NULL, FALSE);
2012-08-29 17:07:26 +02:00
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (userdata != NULL, FALSE);
data = (LoadCategoryData *) userdata;
2012-08-29 17:07:26 +02:00
if (g_file_test (path, G_FILE_TEST_IS_DIR))
{
2012-08-29 18:44:47 +02:00
LdCategory *cat;
2012-08-29 17:07:26 +02:00
cat = load_category (data->self, path, base);
if (cat)
{
2012-08-29 18:44:47 +02:00
ld_category_add_child (data->cat, cat);
2012-08-29 17:07:26 +02:00
g_object_unref (cat);
}
}
else if (data->load_symbols
&& ld_lua_check_file (data->self->priv->lua, path))
{
ld_lua_load_file (data->self->priv->lua, path,
load_category_symbol_cb, data->cat);
2012-08-29 17:07:26 +02:00
}
data->changed = TRUE;
return TRUE;
}
/*
* load_category_symbol_cb:
*
* Insert newly registered symbols into the category.
*/
static void
load_category_symbol_cb (LdSymbol *symbol, gpointer user_data)
{
2012-08-29 18:44:47 +02:00
LdCategory *cat;
g_return_if_fail (LD_IS_SYMBOL (symbol));
2012-08-29 18:44:47 +02:00
g_return_if_fail (LD_IS_CATEGORY (user_data));
2012-08-29 18:44:47 +02:00
cat = LD_CATEGORY (user_data);
ld_category_insert_symbol (cat, symbol, -1);
}
/*
* read_human_name_from_file:
* @filename: location of the JSON file.
*
* Read the human name of the processed category.
*/
static gchar *
read_human_name_from_file (const gchar *filename)
{
const gchar *const *lang;
JsonParser *parser;
JsonNode *root;
JsonObject *object;
GError *error;
g_return_val_if_fail (filename != NULL, NULL);
parser = json_parser_new ();
error = NULL;
if (!json_parser_load_from_file (parser, filename, &error))
{
g_warning ("%s", error->message);
g_error_free (error);
goto read_human_name_from_file_end;
}
root = json_parser_get_root (parser);
if (!JSON_NODE_HOLDS_OBJECT (root))
{
g_warning ("failed to parse `%s': %s", filename,
"The root node is not an object.");
goto read_human_name_from_file_end;
}
object = json_node_get_object (root);
for (lang = g_get_language_names (); *lang; lang++)
{
const gchar *member;
if (!json_object_has_member (object, *lang))
continue;
member = json_object_get_string_member (object, *lang);
if (member != NULL)
{
gchar *result;
result = g_strdup (member);
g_object_unref (parser);
return result;
}
}
read_human_name_from_file_end:
g_object_unref (parser);
return NULL;
}
2010-09-13 19:24:53 +02:00
/**
2010-09-25 16:14:09 +02:00
* ld_library_load:
* @self: an #LdLibrary object.
* @directory: a directory to be loaded.
2010-09-13 19:24:53 +02:00
*
* Load the contents of a directory into the library.
*/
gboolean
ld_library_load (LdLibrary *self, const gchar *directory)
2010-09-13 19:24:53 +02:00
{
2012-08-29 17:07:26 +02:00
LoadCategoryData data;
2010-09-13 19:24:53 +02:00
2010-09-25 16:14:09 +02:00
g_return_val_if_fail (LD_IS_LIBRARY (self), FALSE);
g_return_val_if_fail (directory != NULL, FALSE);
2010-09-13 19:24:53 +02:00
2012-08-29 17:07:26 +02:00
/* Almost like load_category(). */
data.self = self;
2012-08-29 17:07:26 +02:00
data.cat = self->priv->root;
data.load_symbols = FALSE;
data.changed = FALSE;
2012-08-29 17:07:26 +02:00
foreach_dir (directory, load_category_cb, &data, NULL);
2010-09-13 19:24:53 +02:00
2012-08-29 21:56:59 +02:00
/* XXX: It might also make sense to just forward the "children-changed"
* signal of the root category but we'd have to block it here anyway,
* so that we don't unnecessarily fire events for every single change.
*
* The user code isn't supposed to make changes to / and it's its own
* problem if it keeps reloading something a hundred times in a row.
*
* That said, it'd be possible to add change grouping methods to
* LdCategory and so delay the signal emission until an `unblock'.
*/
if (data.changed)
g_signal_emit (self, LD_LIBRARY_GET_CLASS (self)->changed_signal, 0);
2010-09-13 19:24:53 +02:00
return TRUE;
}
/**
* ld_library_find_symbol:
* @self: an #LdLibrary object.
* @identifier: an identifier of the symbol to be searched for.
*
* Search for a symbol in the library.
*
* Return value: a symbol object if found, %NULL otherwise.
*/
LdSymbol *
ld_library_find_symbol (LdLibrary *self, const gchar *identifier)
{
gchar **path;
LdSymbol *symbol = NULL;
g_return_val_if_fail (LD_IS_LIBRARY (self), NULL);
g_return_val_if_fail (identifier != NULL, NULL);
path = g_strsplit (identifier, LD_LIBRARY_IDENTIFIER_SEPARATOR, 0);
if (path)
{
symbol = traverse_path (self->priv->root, path);
g_strfreev (path);
}
return symbol;
}
static LdSymbol *
traverse_path (LdCategory *category, gchar **path)
{
const GSList *list, *iter;
LdSymbol *symbol;
g_return_val_if_fail (*path != NULL, NULL);
/* Walk the category tree to where the symbol is supposed to be. */
for (; path[1]; path++)
{
list = ld_category_get_children (category);
for (iter = list; iter; iter = g_slist_next (iter))
{
category = LD_CATEGORY (iter->data);
if (!strcmp (*path, ld_category_get_name (category)))
break;
}
if (!iter)
return NULL;
}
/* And look up the actual symbol at the leaf. */
list = ld_category_get_symbols (category);
for (iter = list; iter; iter = g_slist_next (iter))
{
symbol = LD_SYMBOL (iter->data);
if (!strcmp (*path, ld_symbol_get_name (symbol)))
return symbol;
}
return NULL;
}
2010-09-13 19:24:53 +02:00
/**
* ld_library_get_root:
* @self: an #LdLibrary object.
*
* Return value: (transfer none): the root category. Do not modify.
*/
2012-08-29 18:44:47 +02:00
LdCategory *
ld_library_get_root (LdLibrary *self)
{
g_return_val_if_fail (LD_IS_LIBRARY (self), NULL);
return self->priv->root;
}