652 lines
20 KiB
C
652 lines
20 KiB
C
|
/* PIKA - Photo and Image Kooker Application
|
||
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||
|
*
|
||
|
* Original copyright, applying to most contents (license remains unchanged):
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* pikatagcache.c
|
||
|
* Copyright (C) 2008 Aurimas Juška <aurisj@svn.gnome.org>
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program 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 General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
#include <gegl.h>
|
||
|
|
||
|
#include "libpikabase/pikabase.h"
|
||
|
#include "libpikamath/pikamath.h"
|
||
|
#include "libpikaconfig/pikaconfig.h"
|
||
|
|
||
|
#include "core-types.h"
|
||
|
|
||
|
#include "config/pikaxmlparser.h"
|
||
|
|
||
|
#include "pika-memsize.h"
|
||
|
#include "pikacontext.h"
|
||
|
#include "pikadata.h"
|
||
|
#include "pikalist.h"
|
||
|
#include "pikatag.h"
|
||
|
#include "pikatagcache.h"
|
||
|
#include "pikatagged.h"
|
||
|
|
||
|
#include "pika-intl.h"
|
||
|
|
||
|
|
||
|
#define PIKA_TAG_CACHE_FILE "tags.xml"
|
||
|
|
||
|
/* #define DEBUG_PIKA_TAG_CACHE 1 */
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_PIKA
|
||
|
};
|
||
|
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
GQuark identifier;
|
||
|
GQuark checksum;
|
||
|
GList *tags;
|
||
|
guint referenced : 1;
|
||
|
} PikaTagCacheRecord;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
GArray *records;
|
||
|
PikaTagCacheRecord current_record;
|
||
|
} PikaTagCacheParseData;
|
||
|
|
||
|
struct _PikaTagCachePrivate
|
||
|
{
|
||
|
GArray *records;
|
||
|
GList *containers;
|
||
|
};
|
||
|
|
||
|
|
||
|
static void pika_tag_cache_finalize (GObject *object);
|
||
|
|
||
|
static gint64 pika_tag_cache_get_memsize (PikaObject *object,
|
||
|
gint64 *gui_size);
|
||
|
static void pika_tag_cache_object_initialize (PikaTagged *tagged,
|
||
|
PikaTagCache *cache);
|
||
|
static void pika_tag_cache_add_object (PikaTagCache *cache,
|
||
|
PikaTagged *tagged);
|
||
|
|
||
|
static void pika_tag_cache_load_start_element (GMarkupParseContext *context,
|
||
|
const gchar *element_name,
|
||
|
const gchar **attribute_names,
|
||
|
const gchar **attribute_values,
|
||
|
gpointer user_data,
|
||
|
GError **error);
|
||
|
static void pika_tag_cache_load_end_element (GMarkupParseContext *context,
|
||
|
const gchar *element_name,
|
||
|
gpointer user_data,
|
||
|
GError **error);
|
||
|
static void pika_tag_cache_load_text (GMarkupParseContext *context,
|
||
|
const gchar *text,
|
||
|
gsize text_len,
|
||
|
gpointer user_data,
|
||
|
GError **error);
|
||
|
static void pika_tag_cache_load_error (GMarkupParseContext *context,
|
||
|
GError *error,
|
||
|
gpointer user_data);
|
||
|
static const gchar * pika_tag_cache_attribute_name_to_value
|
||
|
(const gchar **attribute_names,
|
||
|
const gchar **attribute_values,
|
||
|
const gchar *name);
|
||
|
|
||
|
static GQuark pika_tag_cache_get_error_domain (void);
|
||
|
|
||
|
|
||
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaTagCache, pika_tag_cache, PIKA_TYPE_OBJECT)
|
||
|
|
||
|
#define parent_class pika_tag_cache_parent_class
|
||
|
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_class_init (PikaTagCacheClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
|
||
|
|
||
|
object_class->finalize = pika_tag_cache_finalize;
|
||
|
|
||
|
pika_object_class->get_memsize = pika_tag_cache_get_memsize;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_init (PikaTagCache *cache)
|
||
|
{
|
||
|
cache->priv = pika_tag_cache_get_instance_private (cache);
|
||
|
|
||
|
cache->priv->records = g_array_new (FALSE, FALSE,
|
||
|
sizeof (PikaTagCacheRecord));
|
||
|
cache->priv->containers = NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_finalize (GObject *object)
|
||
|
{
|
||
|
PikaTagCache *cache = PIKA_TAG_CACHE (object);
|
||
|
|
||
|
if (cache->priv->records)
|
||
|
{
|
||
|
gint i;
|
||
|
|
||
|
for (i = 0; i < cache->priv->records->len; i++)
|
||
|
{
|
||
|
PikaTagCacheRecord *rec = &g_array_index (cache->priv->records,
|
||
|
PikaTagCacheRecord, i);
|
||
|
|
||
|
g_list_free_full (rec->tags, (GDestroyNotify) g_object_unref);
|
||
|
}
|
||
|
|
||
|
g_array_free (cache->priv->records, TRUE);
|
||
|
cache->priv->records = NULL;
|
||
|
}
|
||
|
|
||
|
if (cache->priv->containers)
|
||
|
{
|
||
|
g_list_free (cache->priv->containers);
|
||
|
cache->priv->containers = NULL;
|
||
|
}
|
||
|
|
||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static gint64
|
||
|
pika_tag_cache_get_memsize (PikaObject *object,
|
||
|
gint64 *gui_size)
|
||
|
{
|
||
|
PikaTagCache *cache = PIKA_TAG_CACHE (object);
|
||
|
gint64 memsize = 0;
|
||
|
|
||
|
memsize += pika_g_list_get_memsize (cache->priv->containers, 0);
|
||
|
memsize += cache->priv->records->len * sizeof (PikaTagCacheRecord);
|
||
|
|
||
|
return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
|
||
|
gui_size);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_tag_cache_new:
|
||
|
*
|
||
|
* Returns: creates new PikaTagCache object.
|
||
|
**/
|
||
|
PikaTagCache *
|
||
|
pika_tag_cache_new (void)
|
||
|
{
|
||
|
return g_object_new (PIKA_TYPE_TAG_CACHE, NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_container_add_callback (PikaTagCache *cache,
|
||
|
PikaTagged *tagged,
|
||
|
PikaContainer *not_used)
|
||
|
{
|
||
|
pika_tag_cache_add_object (cache, tagged);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_tag_cache_add_container:
|
||
|
* @cache: a PikaTagCache object.
|
||
|
* @container: container containing PikaTagged objects.
|
||
|
*
|
||
|
* Adds container of PikaTagged objects to tag cache. Before calling this
|
||
|
* function tag cache must be loaded using pika_tag_cache_load(). When tag
|
||
|
* cache is saved to file, tags are collected from objects in priv->containers.
|
||
|
**/
|
||
|
void
|
||
|
pika_tag_cache_add_container (PikaTagCache *cache,
|
||
|
PikaContainer *container)
|
||
|
{
|
||
|
g_return_if_fail (PIKA_IS_TAG_CACHE (cache));
|
||
|
g_return_if_fail (PIKA_IS_CONTAINER (container));
|
||
|
|
||
|
cache->priv->containers = g_list_append (cache->priv->containers, container);
|
||
|
pika_container_foreach (container, (GFunc) pika_tag_cache_object_initialize,
|
||
|
cache);
|
||
|
|
||
|
g_signal_connect_swapped (container, "add",
|
||
|
G_CALLBACK (pika_tag_cache_container_add_callback),
|
||
|
cache);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_add_object (PikaTagCache *cache,
|
||
|
PikaTagged *tagged)
|
||
|
{
|
||
|
gchar *identifier;
|
||
|
GQuark identifier_quark = 0;
|
||
|
gchar *checksum;
|
||
|
GQuark checksum_quark = 0;
|
||
|
GList *list;
|
||
|
gint i;
|
||
|
|
||
|
identifier = pika_tagged_get_identifier (tagged);
|
||
|
|
||
|
if (identifier)
|
||
|
{
|
||
|
identifier_quark = g_quark_try_string (identifier);
|
||
|
g_free (identifier);
|
||
|
}
|
||
|
|
||
|
if (identifier_quark)
|
||
|
{
|
||
|
for (i = 0; i < cache->priv->records->len; i++)
|
||
|
{
|
||
|
PikaTagCacheRecord *rec = &g_array_index (cache->priv->records,
|
||
|
PikaTagCacheRecord, i);
|
||
|
|
||
|
if (rec->identifier == identifier_quark)
|
||
|
{
|
||
|
for (list = rec->tags; list; list = g_list_next (list))
|
||
|
{
|
||
|
pika_tagged_add_tag (tagged, PIKA_TAG (list->data));
|
||
|
}
|
||
|
|
||
|
rec->referenced = TRUE;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
checksum = pika_tagged_get_checksum (tagged);
|
||
|
|
||
|
if (checksum)
|
||
|
{
|
||
|
checksum_quark = g_quark_try_string (checksum);
|
||
|
g_free (checksum);
|
||
|
}
|
||
|
|
||
|
if (checksum_quark)
|
||
|
{
|
||
|
for (i = 0; i < cache->priv->records->len; i++)
|
||
|
{
|
||
|
PikaTagCacheRecord *rec = &g_array_index (cache->priv->records,
|
||
|
PikaTagCacheRecord, i);
|
||
|
|
||
|
if (rec->checksum == checksum_quark)
|
||
|
{
|
||
|
#if DEBUG_PIKA_TAG_CACHE
|
||
|
g_printerr ("remapping identifier: %s ==> %s\n",
|
||
|
rec->identifier ? g_quark_to_string (rec->identifier) : "(NULL)",
|
||
|
identifier_quark ? g_quark_to_string (identifier_quark) : "(NULL)");
|
||
|
#endif
|
||
|
|
||
|
rec->identifier = identifier_quark;
|
||
|
|
||
|
for (list = rec->tags; list; list = g_list_next (list))
|
||
|
{
|
||
|
pika_tagged_add_tag (tagged, PIKA_TAG (list->data));
|
||
|
}
|
||
|
|
||
|
rec->referenced = TRUE;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_object_initialize (PikaTagged *tagged,
|
||
|
PikaTagCache *cache)
|
||
|
{
|
||
|
pika_tag_cache_add_object (cache, tagged);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_tagged_to_cache_record_foreach (PikaTagged *tagged,
|
||
|
GList **cache_records)
|
||
|
{
|
||
|
gchar *identifier = pika_tagged_get_identifier (tagged);
|
||
|
|
||
|
if (identifier)
|
||
|
{
|
||
|
PikaTagCacheRecord *cache_rec = g_new (PikaTagCacheRecord, 1);
|
||
|
gchar *checksum;
|
||
|
|
||
|
checksum = pika_tagged_get_checksum (tagged);
|
||
|
|
||
|
cache_rec->identifier = g_quark_from_string (identifier);
|
||
|
cache_rec->checksum = g_quark_from_string (checksum);
|
||
|
cache_rec->tags = g_list_copy (pika_tagged_get_tags (tagged));
|
||
|
|
||
|
g_free (checksum);
|
||
|
|
||
|
*cache_records = g_list_prepend (*cache_records, cache_rec);
|
||
|
}
|
||
|
|
||
|
g_free (identifier);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_tag_cache_save:
|
||
|
* @cache: a PikaTagCache object.
|
||
|
*
|
||
|
* Saves tag cache to cache file.
|
||
|
**/
|
||
|
void
|
||
|
pika_tag_cache_save (PikaTagCache *cache)
|
||
|
{
|
||
|
GString *buf;
|
||
|
GList *saved_records;
|
||
|
GList *iterator;
|
||
|
GFile *file;
|
||
|
GOutputStream *output;
|
||
|
GError *error = NULL;
|
||
|
gint i;
|
||
|
|
||
|
g_return_if_fail (PIKA_IS_TAG_CACHE (cache));
|
||
|
|
||
|
saved_records = NULL;
|
||
|
for (i = 0; i < cache->priv->records->len; i++)
|
||
|
{
|
||
|
PikaTagCacheRecord *current_record = &g_array_index (cache->priv->records,
|
||
|
PikaTagCacheRecord, i);
|
||
|
|
||
|
if (! current_record->referenced && current_record->tags)
|
||
|
{
|
||
|
/* keep tagged objects which have tags assigned
|
||
|
* but were not loaded.
|
||
|
*/
|
||
|
PikaTagCacheRecord *record_copy = g_new (PikaTagCacheRecord, 1);
|
||
|
|
||
|
record_copy->identifier = current_record->identifier;
|
||
|
record_copy->checksum = current_record->checksum;
|
||
|
record_copy->tags = g_list_copy (current_record->tags);
|
||
|
|
||
|
saved_records = g_list_prepend (saved_records, record_copy);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (iterator = cache->priv->containers;
|
||
|
iterator;
|
||
|
iterator = g_list_next (iterator))
|
||
|
{
|
||
|
pika_container_foreach (PIKA_CONTAINER (iterator->data),
|
||
|
(GFunc) pika_tag_cache_tagged_to_cache_record_foreach,
|
||
|
&saved_records);
|
||
|
}
|
||
|
|
||
|
saved_records = g_list_reverse (saved_records);
|
||
|
|
||
|
buf = g_string_new ("");
|
||
|
g_string_append (buf, "<?xml version='1.0' encoding='UTF-8'?>\n");
|
||
|
g_string_append (buf, "<tags>\n");
|
||
|
|
||
|
for (iterator = saved_records; iterator; iterator = g_list_next (iterator))
|
||
|
{
|
||
|
PikaTagCacheRecord *cache_rec = iterator->data;
|
||
|
GList *tag_iterator;
|
||
|
gchar *identifier_string;
|
||
|
gchar *tag_string;
|
||
|
|
||
|
identifier_string = g_markup_escape_text (g_quark_to_string (cache_rec->identifier), -1);
|
||
|
g_string_append_printf (buf, "\n <resource identifier=\"%s\" checksum=\"%s\">\n",
|
||
|
identifier_string,
|
||
|
g_quark_to_string (cache_rec->checksum));
|
||
|
g_free (identifier_string);
|
||
|
|
||
|
for (tag_iterator = cache_rec->tags;
|
||
|
tag_iterator;
|
||
|
tag_iterator = g_list_next (tag_iterator))
|
||
|
{
|
||
|
PikaTag *tag = PIKA_TAG (tag_iterator->data);
|
||
|
|
||
|
if (! pika_tag_get_internal (tag))
|
||
|
{
|
||
|
tag_string = g_markup_escape_text (pika_tag_get_name (tag), -1);
|
||
|
g_string_append_printf (buf, " <tag>%s</tag>\n", tag_string);
|
||
|
g_free (tag_string);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_string_append (buf, " </resource>\n");
|
||
|
}
|
||
|
|
||
|
g_string_append (buf, "</tags>\n");
|
||
|
|
||
|
file = pika_directory_file (PIKA_TAG_CACHE_FILE, NULL);
|
||
|
|
||
|
output = G_OUTPUT_STREAM (g_file_replace (file,
|
||
|
NULL, FALSE, G_FILE_CREATE_NONE,
|
||
|
NULL, &error));
|
||
|
if (! output)
|
||
|
{
|
||
|
g_printerr ("%s\n", error->message);
|
||
|
}
|
||
|
else if (! g_output_stream_write_all (output, buf->str, buf->len,
|
||
|
NULL, NULL, &error))
|
||
|
{
|
||
|
GCancellable *cancellable = g_cancellable_new ();
|
||
|
|
||
|
g_printerr (_("Error writing '%s': %s\n"),
|
||
|
pika_file_get_utf8_name (file), error->message);
|
||
|
|
||
|
/* Cancel the overwrite initiated by g_file_replace(). */
|
||
|
g_cancellable_cancel (cancellable);
|
||
|
g_output_stream_close (output, cancellable, NULL);
|
||
|
g_object_unref (cancellable);
|
||
|
}
|
||
|
else if (! g_output_stream_close (output, NULL, &error))
|
||
|
{
|
||
|
g_printerr (_("Error closing '%s': %s\n"),
|
||
|
pika_file_get_utf8_name (file), error->message);
|
||
|
}
|
||
|
|
||
|
if (output)
|
||
|
g_object_unref (output);
|
||
|
|
||
|
g_clear_error (&error);
|
||
|
g_object_unref (file);
|
||
|
g_string_free (buf, TRUE);
|
||
|
|
||
|
for (iterator = saved_records;
|
||
|
iterator;
|
||
|
iterator = g_list_next (iterator))
|
||
|
{
|
||
|
PikaTagCacheRecord *cache_rec = iterator->data;
|
||
|
|
||
|
g_list_free (cache_rec->tags);
|
||
|
g_free (cache_rec);
|
||
|
}
|
||
|
|
||
|
g_list_free (saved_records);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* pika_tag_cache_load:
|
||
|
* @cache: a PikaTagCache object.
|
||
|
*
|
||
|
* Loads tag cache from file.
|
||
|
**/
|
||
|
void
|
||
|
pika_tag_cache_load (PikaTagCache *cache)
|
||
|
{
|
||
|
GFile *file;
|
||
|
GMarkupParser markup_parser;
|
||
|
PikaXmlParser *xml_parser;
|
||
|
PikaTagCacheParseData parse_data;
|
||
|
GError *error = NULL;
|
||
|
|
||
|
g_return_if_fail (PIKA_IS_TAG_CACHE (cache));
|
||
|
|
||
|
/* clear any previous priv->records */
|
||
|
cache->priv->records = g_array_set_size (cache->priv->records, 0);
|
||
|
|
||
|
parse_data.records = g_array_new (FALSE, FALSE, sizeof (PikaTagCacheRecord));
|
||
|
memset (&parse_data.current_record, 0, sizeof (PikaTagCacheRecord));
|
||
|
|
||
|
markup_parser.start_element = pika_tag_cache_load_start_element;
|
||
|
markup_parser.end_element = pika_tag_cache_load_end_element;
|
||
|
markup_parser.text = pika_tag_cache_load_text;
|
||
|
markup_parser.passthrough = NULL;
|
||
|
markup_parser.error = pika_tag_cache_load_error;
|
||
|
|
||
|
xml_parser = pika_xml_parser_new (&markup_parser, &parse_data);
|
||
|
|
||
|
file = pika_directory_file (PIKA_TAG_CACHE_FILE, NULL);
|
||
|
|
||
|
if (pika_xml_parser_parse_gfile (xml_parser, file, &error))
|
||
|
{
|
||
|
cache->priv->records = g_array_append_vals (cache->priv->records,
|
||
|
parse_data.records->data,
|
||
|
parse_data.records->len);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_printerr ("Failed to parse tag cache: %s\n",
|
||
|
error ? error->message : "WTF unknown error");
|
||
|
g_clear_error (&error);
|
||
|
}
|
||
|
|
||
|
g_object_unref (file);
|
||
|
pika_xml_parser_free (xml_parser);
|
||
|
g_array_free (parse_data.records, TRUE);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_load_start_element (GMarkupParseContext *context,
|
||
|
const gchar *element_name,
|
||
|
const gchar **attribute_names,
|
||
|
const gchar **attribute_values,
|
||
|
gpointer user_data,
|
||
|
GError **error)
|
||
|
{
|
||
|
PikaTagCacheParseData *parse_data = user_data;
|
||
|
|
||
|
if (! strcmp (element_name, "resource"))
|
||
|
{
|
||
|
const gchar *identifier;
|
||
|
const gchar *checksum;
|
||
|
|
||
|
identifier = pika_tag_cache_attribute_name_to_value (attribute_names,
|
||
|
attribute_values,
|
||
|
"identifier");
|
||
|
checksum = pika_tag_cache_attribute_name_to_value (attribute_names,
|
||
|
attribute_values,
|
||
|
"checksum");
|
||
|
|
||
|
if (! identifier)
|
||
|
{
|
||
|
g_set_error (error,
|
||
|
pika_tag_cache_get_error_domain (),
|
||
|
1001,
|
||
|
"Resource tag does not contain required attribute identifier.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memset (&parse_data->current_record, 0, sizeof (PikaTagCacheRecord));
|
||
|
|
||
|
parse_data->current_record.identifier = g_quark_from_string (identifier);
|
||
|
parse_data->current_record.checksum = g_quark_from_string (checksum);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_load_end_element (GMarkupParseContext *context,
|
||
|
const gchar *element_name,
|
||
|
gpointer user_data,
|
||
|
GError **error)
|
||
|
{
|
||
|
PikaTagCacheParseData *parse_data = user_data;
|
||
|
|
||
|
if (strcmp (element_name, "resource") == 0)
|
||
|
{
|
||
|
parse_data->records = g_array_append_val (parse_data->records,
|
||
|
parse_data->current_record);
|
||
|
memset (&parse_data->current_record, 0, sizeof (PikaTagCacheRecord));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_load_text (GMarkupParseContext *context,
|
||
|
const gchar *text,
|
||
|
gsize text_len,
|
||
|
gpointer user_data,
|
||
|
GError **error)
|
||
|
{
|
||
|
PikaTagCacheParseData *parse_data = user_data;
|
||
|
const gchar *current_element;
|
||
|
gchar buffer[2048];
|
||
|
PikaTag *tag;
|
||
|
|
||
|
current_element = g_markup_parse_context_get_element (context);
|
||
|
|
||
|
if (g_strcmp0 (current_element, "tag") == 0)
|
||
|
{
|
||
|
if (text_len >= sizeof (buffer))
|
||
|
{
|
||
|
g_set_error (error, pika_tag_cache_get_error_domain (), 1002,
|
||
|
"Tag value is too long.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memcpy (buffer, text, text_len);
|
||
|
buffer[text_len] = '\0';
|
||
|
|
||
|
tag = pika_tag_new (buffer);
|
||
|
if (tag)
|
||
|
{
|
||
|
parse_data->current_record.tags = g_list_append (parse_data->current_record.tags,
|
||
|
tag);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_warning ("dropping invalid tag '%s' from '%s'\n", buffer,
|
||
|
g_quark_to_string (parse_data->current_record.identifier));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pika_tag_cache_load_error (GMarkupParseContext *context,
|
||
|
GError *error,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
g_printerr ("Tag cache parse error: %s\n", error->message);
|
||
|
}
|
||
|
|
||
|
static const gchar*
|
||
|
pika_tag_cache_attribute_name_to_value (const gchar **attribute_names,
|
||
|
const gchar **attribute_values,
|
||
|
const gchar *name)
|
||
|
{
|
||
|
while (*attribute_names)
|
||
|
{
|
||
|
if (! strcmp (*attribute_names, name))
|
||
|
{
|
||
|
return *attribute_values;
|
||
|
}
|
||
|
|
||
|
attribute_names++;
|
||
|
attribute_values++;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static GQuark
|
||
|
pika_tag_cache_get_error_domain (void)
|
||
|
{
|
||
|
return g_quark_from_static_string ("pika-tag-cache-error-quark");
|
||
|
}
|