/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * pikaparasite.c * Copyright (C) 1998 Jay Cox * * 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 * Library 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef G_OS_WIN32 #include /* For _getpid() */ #endif #include "pikabasetypes.h" #include "pikaparasite.h" /** * SECTION: pikaparasite * @title: PikaParasite * @short_description: Arbitrary pieces of data which can be attached * to various PIKA objects. * @see_also: pika_image_attach_parasite(), pika_item_attach_parasite(), * pika_attach_parasite() and their related functions. * * Arbitrary pieces of data which can be attached to various PIKA objects. **/ /* * PIKA_TYPE_PARASITE */ G_DEFINE_BOXED_TYPE (PikaParasite, pika_parasite, pika_parasite_copy, pika_parasite_free) /* * PIKA_TYPE_PARAM_PARASITE */ #define PIKA_PARAM_SPEC_PARASITE(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), PIKA_TYPE_PARAM_PARASITE, PikaParamSpecParasite)) struct _PikaParamSpecParasite { GParamSpecBoxed parent_instance; }; static void pika_param_parasite_class_init (GParamSpecClass *class); static void pika_param_parasite_init (GParamSpec *pspec); static gboolean pika_param_parasite_validate (GParamSpec *pspec, GValue *value); static gint pika_param_parasite_values_cmp (GParamSpec *pspec, const GValue *value1, const GValue *value2); GType pika_param_parasite_get_type (void) { static GType type = 0; if (! type) { const GTypeInfo type_info = { sizeof (GParamSpecClass), NULL, NULL, (GClassInitFunc) pika_param_parasite_class_init, NULL, NULL, sizeof (PikaParamSpecParasite), 0, (GInstanceInitFunc) pika_param_parasite_init }; type = g_type_register_static (G_TYPE_PARAM_BOXED, "PikaParamParasite", &type_info, 0); } return type; } static void pika_param_parasite_class_init (GParamSpecClass *class) { class->value_type = PIKA_TYPE_PARASITE; class->value_validate = pika_param_parasite_validate; class->values_cmp = pika_param_parasite_values_cmp; } static void pika_param_parasite_init (GParamSpec *pspec) { } static gboolean pika_param_parasite_validate (GParamSpec *pspec, GValue *value) { PikaParasite *parasite = value->data[0].v_pointer; if (! parasite) { return TRUE; } else if (parasite->name == NULL || *parasite->name == '\0' || ! g_utf8_validate (parasite->name, -1, NULL) || (parasite->size == 0 && parasite->data != NULL) || (parasite->size > 0 && parasite->data == NULL)) { g_value_set_boxed (value, NULL); return TRUE; } return FALSE; } static gint pika_param_parasite_values_cmp (GParamSpec *pspec, const GValue *value1, const GValue *value2) { PikaParasite *parasite1 = value1->data[0].v_pointer; PikaParasite *parasite2 = value2->data[0].v_pointer; /* try to return at least *something*, it's useless anyway... */ if (! parasite1) return parasite2 != NULL ? -1 : 0; else if (! parasite2) return parasite1 != NULL; else return pika_parasite_compare (parasite1, parasite2); } /** * pika_param_spec_parasite: * @name: Canonical name of the property specified. * @nick: Nick name of the property specified. * @blurb: Description of the property specified. * @flags: Flags for the property specified. * * Creates a new #PikaParamSpecParasite specifying a * [type@Parasite] property. * * See g_param_spec_internal() for details on property names. * * Returns: (transfer full): The newly created #PikaParamSpecParasite. * * Since: 2.4 **/ GParamSpec * pika_param_spec_parasite (const gchar *name, const gchar *nick, const gchar *blurb, GParamFlags flags) { PikaParamSpecParasite *parasite_spec; parasite_spec = g_param_spec_internal (PIKA_TYPE_PARAM_PARASITE, name, nick, blurb, flags); return G_PARAM_SPEC (parasite_spec); } #ifdef DEBUG static void pika_parasite_print (PikaParasite *parasite) { if (parasite == NULL) { g_print ("pid %d: attempt to print a null parasite\n", getpid ()); return; } g_print ("pid %d: parasite: %p\n", getpid (), parasite); if (parasite->name) g_print ("\tname: %s\n", parasite->name); else g_print ("\tname: NULL\n"); g_print ("\tflags: %d\n", parasite->flags); g_print ("\tsize: %d\n", parasite->size); if (parasite->size > 0) g_print ("\tdata: %p\n", parasite->data); } #endif /** * pika_parasite_new: * @name: the new #PikaParasite name. * @flags: see libpikabase/pikaparasite.h macros. * @size: the size of @data, including a terminal %NULL byte if needed. * @data: (nullable) (array length=size) (element-type char): the data to save in a parasite. * * Creates a new parasite and save @data which may be a proper text (in * which case you may want to set @size as strlen(@data) + 1) or not. * * Returns: (transfer full): a new #PikaParasite. */ PikaParasite * pika_parasite_new (const gchar *name, guint32 flags, guint32 size, gconstpointer data) { PikaParasite *parasite; if (! (name && *name)) return NULL; parasite = g_slice_new (PikaParasite); parasite->name = g_strdup (name); parasite->flags = (flags & 0xFF); parasite->size = size; if (size) parasite->data = g_memdup2 (data, size); else parasite->data = NULL; return parasite; } /** * pika_parasite_free: * @parasite: a #PikaParasite * * Free @parasite's dynamically allocated memory. */ void pika_parasite_free (PikaParasite *parasite) { if (parasite == NULL) return; if (parasite->name) g_free (parasite->name); if (parasite->data) g_free (parasite->data); g_slice_free (PikaParasite, parasite); } /** * pika_parasite_is_type: * @parasite: a #PikaParasite * @name: a parasite name. * * Compare parasite's names. * * Returns: %TRUE if @parasite is named @name, %FALSE otherwise. */ gboolean pika_parasite_is_type (const PikaParasite *parasite, const gchar *name) { if (!parasite || !parasite->name) return FALSE; return (strcmp (parasite->name, name) == 0); } /** * pika_parasite_copy: * @parasite: a #PikaParasite * * Create a new parasite with all the same values. * * Returns: (transfer full): a newly allocated #PikaParasite with same contents. */ PikaParasite * pika_parasite_copy (const PikaParasite *parasite) { if (parasite == NULL) return NULL; return pika_parasite_new (parasite->name, parasite->flags, parasite->size, parasite->data); } /** * pika_parasite_compare: * @a: a #PikaParasite * @b: a #PikaParasite * * Compare parasite's contents. * * Returns: %TRUE if @a and @b have same contents, %FALSE otherwise. */ gboolean pika_parasite_compare (const PikaParasite *a, const PikaParasite *b) { if (a && b && a->name && b->name && strcmp (a->name, b->name) == 0 && a->flags == b->flags && a->size == b->size) { if (a->data == NULL && b->data == NULL) return TRUE; else if (a->data && b->data && memcmp (a->data, b->data, a->size) == 0) return TRUE; } return FALSE; } /** * pika_parasite_get_flags: * @parasite: a #PikaParasite * * Returns: @parasite flags. */ gulong pika_parasite_get_flags (const PikaParasite *parasite) { if (parasite == NULL) return 0; return parasite->flags; } /** * pika_parasite_is_persistent: * @parasite: a #PikaParasite * * Returns: %TRUE if @parasite is persistent, %FALSE otherwise. */ gboolean pika_parasite_is_persistent (const PikaParasite *parasite) { if (parasite == NULL) return FALSE; return (parasite->flags & PIKA_PARASITE_PERSISTENT); } /** * pika_parasite_is_undoable: * @parasite: a #PikaParasite * * Returns: %TRUE if @parasite is undoable, %FALSE otherwise. */ gboolean pika_parasite_is_undoable (const PikaParasite *parasite) { if (parasite == NULL) return FALSE; return (parasite->flags & PIKA_PARASITE_UNDOABLE); } /** * pika_parasite_has_flag: * @parasite: a #PikaParasite * @flag: a parasite flag * * Returns: %TRUE if @parasite has @flag set, %FALSE otherwise. */ gboolean pika_parasite_has_flag (const PikaParasite *parasite, gulong flag) { if (parasite == NULL) return FALSE; return (parasite->flags & flag); } /** * pika_parasite_get_name: * @parasite: a #PikaParasite * * Returns: @parasite's name. */ const gchar * pika_parasite_get_name (const PikaParasite *parasite) { if (parasite) return parasite->name; return NULL; } /** * pika_parasite_get_data: * @parasite: a #PikaParasite * @num_bytes: (out) (nullable): size of the returned data. * * Gets the parasite's data. It may not necessarily be text, nor is it * guaranteed to be %NULL-terminated. It is your responsibility to know * how to deal with this data. * Even when you expect a nul-terminated string, it is advised not to * assume the returned data to be, as parasites can be edited by third * party scripts. You may end up reading out-of-bounds data. So you * should only ignore @num_bytes when you all you care about is checking * if the parasite has contents. * * Returns: (array length=num_bytes) (element-type char): parasite's data. */ gconstpointer pika_parasite_get_data (const PikaParasite *parasite, guint32 *num_bytes) { if (parasite) { if (num_bytes) *num_bytes = parasite->size; return parasite->data; } if (num_bytes) *num_bytes = 0; return NULL; }