/* 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 * * 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 . */ #include "config.h" #include #include #include #include #include "libpikabase/pikabase.h" #include "pdb-types.h" #include "core/pika.h" #include "core/pika-memsize.h" #include "core/pikachannel.h" #include "core/pikadisplay.h" #include "core/pikalayer.h" #include "core/pikaparamspecs.h" #include "core/pikaprogress.h" #include "vectors/pikavectors.h" #include "pikapdbcontext.h" #include "pikapdberror.h" #include "pikaprocedure.h" #include "pika-intl.h" static void pika_procedure_finalize (GObject *object); static gint64 pika_procedure_get_memsize (PikaObject *object, gint64 *gui_size); static const gchar * pika_procedure_real_get_label (PikaProcedure *procedure); static const gchar * pika_procedure_real_get_menu_label (PikaProcedure *procedure); static const gchar * pika_procedure_real_get_blurb (PikaProcedure *procedure); static const gchar * pika_procedure_real_get_help_id (PikaProcedure *procedure); static gboolean pika_procedure_real_get_sensitive (PikaProcedure *procedure, PikaObject *object, const gchar **tooltip); static PikaValueArray * pika_procedure_real_execute (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, GError **error); static void pika_procedure_real_execute_async (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, PikaDisplay *display); static void pika_procedure_free_help (PikaProcedure *procedure); static void pika_procedure_free_attribution (PikaProcedure *procedure); static gboolean pika_procedure_validate_args (PikaProcedure *procedure, GParamSpec **param_specs, gint n_param_specs, PikaValueArray *args, gboolean return_vals, GError **error); G_DEFINE_TYPE (PikaProcedure, pika_procedure, PIKA_TYPE_VIEWABLE) #define parent_class pika_procedure_parent_class static void pika_procedure_class_init (PikaProcedureClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass); object_class->finalize = pika_procedure_finalize; pika_object_class->get_memsize = pika_procedure_get_memsize; klass->get_label = pika_procedure_real_get_label; klass->get_menu_label = pika_procedure_real_get_menu_label; klass->get_blurb = pika_procedure_real_get_blurb; klass->get_help_id = pika_procedure_real_get_help_id; klass->get_sensitive = pika_procedure_real_get_sensitive; klass->execute = pika_procedure_real_execute; klass->execute_async = pika_procedure_real_execute_async; } static void pika_procedure_init (PikaProcedure *procedure) { procedure->proc_type = PIKA_PDB_PROC_TYPE_INTERNAL; } static void pika_procedure_finalize (GObject *object) { PikaProcedure *procedure = PIKA_PROCEDURE (object); gint i; pika_procedure_free_help (procedure); pika_procedure_free_attribution (procedure); g_clear_pointer (&procedure->deprecated, g_free); g_clear_pointer (&procedure->label, g_free); if (procedure->args) { for (i = 0; i < procedure->num_args; i++) g_param_spec_unref (procedure->args[i]); g_clear_pointer (&procedure->args, g_free); } if (procedure->values) { for (i = 0; i < procedure->num_values; i++) g_param_spec_unref (procedure->values[i]); g_clear_pointer (&procedure->values, g_free); } G_OBJECT_CLASS (parent_class)->finalize (object); } static gint64 pika_procedure_get_memsize (PikaObject *object, gint64 *gui_size) { PikaProcedure *procedure = PIKA_PROCEDURE (object); gint64 memsize = 0; gint i; if (! procedure->static_help) { memsize += pika_string_get_memsize (procedure->blurb); memsize += pika_string_get_memsize (procedure->help); memsize += pika_string_get_memsize (procedure->help_id); } if (! procedure->static_attribution) { memsize += pika_string_get_memsize (procedure->authors); memsize += pika_string_get_memsize (procedure->copyright); memsize += pika_string_get_memsize (procedure->date); } memsize += pika_string_get_memsize (procedure->deprecated); memsize += procedure->num_args * sizeof (GParamSpec *); for (i = 0; i < procedure->num_args; i++) memsize += pika_g_param_spec_get_memsize (procedure->args[i]); memsize += procedure->num_values * sizeof (GParamSpec *); for (i = 0; i < procedure->num_values; i++) memsize += pika_g_param_spec_get_memsize (procedure->values[i]); return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); } static const gchar * pika_procedure_real_get_label (PikaProcedure *procedure) { gchar *ellipsis; gchar *label; if (procedure->label) return procedure->label; label = pika_strip_uline (pika_procedure_get_menu_label (procedure)); ellipsis = strstr (label, "..."); if (! ellipsis) ellipsis = strstr (label, "\342\200\246" /* U+2026 HORIZONTAL ELLIPSIS */); if (ellipsis && ellipsis == (label + strlen (label) - 3)) *ellipsis = '\0'; procedure->label = label; return procedure->label; } static const gchar * pika_procedure_real_get_menu_label (PikaProcedure *procedure) { return pika_object_get_name (procedure); /* lame fallback */ } static const gchar * pika_procedure_real_get_blurb (PikaProcedure *procedure) { return procedure->blurb; } static const gchar * pika_procedure_real_get_help_id (PikaProcedure *procedure) { if (procedure->help_id) return procedure->help_id; return pika_object_get_name (procedure); } static gboolean pika_procedure_real_get_sensitive (PikaProcedure *procedure, PikaObject *object, const gchar **tooltip) { return TRUE /* random fallback */; } static PikaValueArray * pika_procedure_real_execute (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, GError **error) { g_return_val_if_fail (pika_value_array_length (args) >= procedure->num_args, NULL); return procedure->marshal_func (procedure, pika, context, progress, args, error); } static void pika_procedure_real_execute_async (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, PikaDisplay *display) { PikaValueArray *return_vals; GError *error = NULL; g_return_if_fail (pika_value_array_length (args) >= procedure->num_args); return_vals = PIKA_PROCEDURE_GET_CLASS (procedure)->execute (procedure, pika, context, progress, args, &error); pika_value_array_unref (return_vals); if (error) { pika_message_literal (pika, G_OBJECT (progress), PIKA_MESSAGE_ERROR, error->message); g_error_free (error); } } /* public functions */ PikaProcedure * pika_procedure_new (PikaMarshalFunc marshal_func) { PikaProcedure *procedure; g_return_val_if_fail (marshal_func != NULL, NULL); procedure = g_object_new (PIKA_TYPE_PROCEDURE, NULL); procedure->marshal_func = marshal_func; return procedure; } void pika_procedure_set_help (PikaProcedure *procedure, const gchar *blurb, const gchar *help, const gchar *help_id) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_help (procedure); procedure->blurb = g_strdup (blurb); procedure->help = g_strdup (help); procedure->help_id = g_strdup (help_id); procedure->static_help = FALSE; } void pika_procedure_set_static_help (PikaProcedure *procedure, const gchar *blurb, const gchar *help, const gchar *help_id) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_help (procedure); procedure->blurb = (gchar *) blurb; procedure->help = (gchar *) help; procedure->help_id = (gchar *) help_id; procedure->static_help = TRUE; } void pika_procedure_take_help (PikaProcedure *procedure, gchar *blurb, gchar *help, gchar *help_id) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_help (procedure); procedure->blurb = blurb; procedure->help = help; procedure->help_id = help_id; procedure->static_help = FALSE; } void pika_procedure_set_attribution (PikaProcedure *procedure, const gchar *authors, const gchar *copyright, const gchar *date) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_attribution (procedure); procedure->authors = g_strdup (authors); procedure->copyright = g_strdup (copyright); procedure->date = g_strdup (date); procedure->static_attribution = FALSE; } void pika_procedure_set_static_attribution (PikaProcedure *procedure, const gchar *authors, const gchar *copyright, const gchar *date) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_attribution (procedure); procedure->authors = (gchar *) authors; procedure->copyright = (gchar *) copyright; procedure->date = (gchar *) date; procedure->static_attribution = TRUE; } void pika_procedure_take_attribution (PikaProcedure *procedure, gchar *authors, gchar *copyright, gchar *date) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); pika_procedure_free_attribution (procedure); procedure->authors = authors; procedure->copyright = copyright; procedure->date = date; procedure->static_attribution = FALSE; } void pika_procedure_set_deprecated (PikaProcedure *procedure, const gchar *deprecated) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); g_free (procedure->deprecated); procedure->deprecated = g_strdup (deprecated); } const gchar * pika_procedure_get_label (PikaProcedure *procedure) { g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); return PIKA_PROCEDURE_GET_CLASS (procedure)->get_label (procedure); } const gchar * pika_procedure_get_menu_label (PikaProcedure *procedure) { g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); return PIKA_PROCEDURE_GET_CLASS (procedure)->get_menu_label (procedure); } const gchar * pika_procedure_get_blurb (PikaProcedure *procedure) { g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); return PIKA_PROCEDURE_GET_CLASS (procedure)->get_blurb (procedure); } const gchar * pika_procedure_get_help (PikaProcedure *procedure) { g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); return procedure->help; } const gchar * pika_procedure_get_help_id (PikaProcedure *procedure) { g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); return PIKA_PROCEDURE_GET_CLASS (procedure)->get_help_id (procedure); } gboolean pika_procedure_get_sensitive (PikaProcedure *procedure, PikaObject *object, const gchar **reason) { const gchar *my_reason = NULL; gboolean sensitive; g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), FALSE); g_return_val_if_fail (object == NULL || PIKA_IS_OBJECT (object), FALSE); sensitive = PIKA_PROCEDURE_GET_CLASS (procedure)->get_sensitive (procedure, object, &my_reason); if (reason) *reason = my_reason; return sensitive; } PikaValueArray * pika_procedure_execute (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, GError **error) { PikaValueArray *return_vals; GError *pdb_error = NULL; g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), NULL); g_return_val_if_fail (args != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (! pika_procedure_validate_args (procedure, procedure->args, procedure->num_args, args, FALSE, &pdb_error)) { return_vals = pika_procedure_get_return_values (procedure, FALSE, pdb_error); if (! error) /* If we can't properly propagate the error, at least print it * to standard error stream. This makes debugging easier. */ g_printerr ("%s failed to validate arguments: %s\n", G_STRFUNC, pdb_error->message); g_propagate_error (error, pdb_error); return return_vals; } if (PIKA_IS_PDB_CONTEXT (context)) context = g_object_ref (context); else context = pika_pdb_context_new (pika, context, TRUE); if (progress) g_object_ref (progress); /* call the procedure */ return_vals = PIKA_PROCEDURE_GET_CLASS (procedure)->execute (procedure, pika, context, progress, args, error); if (progress) g_object_unref (progress); g_object_unref (context); if (return_vals) { switch (g_value_get_enum (pika_value_array_index (return_vals, 0))) { case PIKA_PDB_CALLING_ERROR: case PIKA_PDB_EXECUTION_ERROR: /* If the error has not already been set, construct one * from the error message that is optionally passed with * the return values. */ if (error && *error == NULL && pika_value_array_length (return_vals) > 1 && G_VALUE_HOLDS_STRING (pika_value_array_index (return_vals, 1))) { GValue *value = pika_value_array_index (return_vals, 1); const gchar *message = g_value_get_string (value); if (message) g_set_error_literal (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_FAILED, message); } break; default: break; } } else { g_warning ("%s: no return values, shouldn't happen", G_STRFUNC); pdb_error = g_error_new (PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned no return values"), pika_object_get_name (procedure)); return_vals = pika_procedure_get_return_values (procedure, FALSE, pdb_error); if (error && *error == NULL) g_propagate_error (error, pdb_error); else g_error_free (pdb_error); } return return_vals; } void pika_procedure_execute_async (PikaProcedure *procedure, Pika *pika, PikaContext *context, PikaProgress *progress, PikaValueArray *args, PikaDisplay *display, GError **error) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); g_return_if_fail (PIKA_IS_PIKA (pika)); g_return_if_fail (PIKA_IS_CONTEXT (context)); g_return_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress)); g_return_if_fail (args != NULL); g_return_if_fail (display == NULL || PIKA_IS_DISPLAY (display)); g_return_if_fail (error == NULL || *error == NULL); if (pika_procedure_validate_args (procedure, procedure->args, procedure->num_args, args, FALSE, error)) { if (PIKA_IS_PDB_CONTEXT (context)) context = g_object_ref (context); else context = pika_pdb_context_new (pika, context, TRUE); if (progress) g_object_ref (progress); PIKA_PROCEDURE_GET_CLASS (procedure)->execute_async (procedure, pika, context, progress, args, display); if (progress) g_object_unref (progress); g_object_unref (context); } } PikaValueArray * pika_procedure_get_arguments (PikaProcedure *procedure) { PikaValueArray *args; GValue value = G_VALUE_INIT; gint i; g_return_val_if_fail (PIKA_IS_PROCEDURE (procedure), NULL); args = pika_value_array_new (procedure->num_args); for (i = 0; i < procedure->num_args; i++) { g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (procedure->args[i])); g_param_value_set_default (procedure->args[i], &value); pika_value_array_append (args, &value); g_value_unset (&value); } return args; } PikaValueArray * pika_procedure_get_return_values (PikaProcedure *procedure, gboolean success, const GError *error) { PikaValueArray *args; GValue value = G_VALUE_INIT; gint i; g_return_val_if_fail (success == FALSE || PIKA_IS_PROCEDURE (procedure), NULL); if (success) { args = pika_value_array_new (procedure->num_values + 1); g_value_init (&value, PIKA_TYPE_PDB_STATUS_TYPE); g_value_set_enum (&value, PIKA_PDB_SUCCESS); pika_value_array_append (args, &value); g_value_unset (&value); for (i = 0; i < procedure->num_values; i++) { g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (procedure->values[i])); g_param_value_set_default (procedure->values[i], &value); pika_value_array_append (args, &value); g_value_unset (&value); } } else { args = pika_value_array_new ((error && error->message) ? 2 : 1); g_value_init (&value, PIKA_TYPE_PDB_STATUS_TYPE); /* errors in the PIKA_PDB_ERROR domain are calling errors */ if (error && error->domain == PIKA_PDB_ERROR) { switch ((PikaPdbErrorCode) error->code) { case PIKA_PDB_ERROR_FAILED: case PIKA_PDB_ERROR_PROCEDURE_NOT_FOUND: case PIKA_PDB_ERROR_INVALID_ARGUMENT: case PIKA_PDB_ERROR_INVALID_RETURN_VALUE: case PIKA_PDB_ERROR_INTERNAL_ERROR: g_value_set_enum (&value, PIKA_PDB_CALLING_ERROR); break; case PIKA_PDB_ERROR_CANCELLED: g_value_set_enum (&value, PIKA_PDB_CANCEL); break; default: pika_assert_not_reached (); } } else { g_value_set_enum (&value, PIKA_PDB_EXECUTION_ERROR); } pika_value_array_append (args, &value); g_value_unset (&value); if (error && error->message) { g_value_init (&value, G_TYPE_STRING); g_value_set_string (&value, error->message); pika_value_array_append (args, &value); g_value_unset (&value); } } return args; } void pika_procedure_add_argument (PikaProcedure *procedure, GParamSpec *pspec) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); procedure->args = g_renew (GParamSpec *, procedure->args, procedure->num_args + 1); procedure->args[procedure->num_args] = pspec; g_param_spec_ref_sink (pspec); procedure->num_args++; } void pika_procedure_add_return_value (PikaProcedure *procedure, GParamSpec *pspec) { g_return_if_fail (PIKA_IS_PROCEDURE (procedure)); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); procedure->values = g_renew (GParamSpec *, procedure->values, procedure->num_values + 1); procedure->values[procedure->num_values] = pspec; g_param_spec_ref_sink (pspec); procedure->num_values++; } /** * pika_procedure_create_override: * @procedure: * @new_marshal_func: * * Creates a new PikaProcedure that can be used to override the * existing @procedure. * * Returns: The new #PikaProcedure. **/ PikaProcedure * pika_procedure_create_override (PikaProcedure *procedure, PikaMarshalFunc new_marshal_func) { PikaProcedure *new_procedure = NULL; const gchar *name = NULL; int i = 0; new_procedure = pika_procedure_new (new_marshal_func); name = pika_object_get_name (procedure); pika_object_set_static_name (PIKA_OBJECT (new_procedure), name); for (i = 0; i < procedure->num_args; i++) pika_procedure_add_argument (new_procedure, procedure->args[i]); for (i = 0; i < procedure->num_values; i++) pika_procedure_add_return_value (new_procedure, procedure->values[i]); return new_procedure; } gint pika_procedure_name_compare (PikaProcedure *proc1, PikaProcedure *proc2) { /* Assume there always is a name, don't bother with NULL checks */ return strcmp (pika_object_get_name (proc1), pika_object_get_name (proc2)); } /* private functions */ static void pika_procedure_free_help (PikaProcedure *procedure) { if (! procedure->static_help) { g_free (procedure->blurb); g_free (procedure->help); g_free (procedure->help_id); } procedure->blurb = NULL; procedure->help = NULL; procedure->help_id = NULL; procedure->static_help = FALSE; } static void pika_procedure_free_attribution (PikaProcedure *procedure) { if (! procedure->static_attribution) { g_free (procedure->authors); g_free (procedure->copyright); g_free (procedure->date); } procedure->authors = NULL; procedure->copyright = NULL; procedure->date = NULL; procedure->static_attribution = FALSE; } static gboolean pika_procedure_validate_args (PikaProcedure *procedure, GParamSpec **param_specs, gint n_param_specs, PikaValueArray *args, gboolean return_vals, GError **error) { gint i; for (i = 0; i < MIN (pika_value_array_length (args), n_param_specs); i++) { GValue *arg = pika_value_array_index (args, i); GParamSpec *pspec = param_specs[i]; GType arg_type = G_VALUE_TYPE (arg); GType spec_type = G_PARAM_SPEC_VALUE_TYPE (pspec); if (! g_type_is_a (arg_type, spec_type)) { if (return_vals) { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned a wrong value type " "for return value '%s' (#%d). " "Expected %s, got %s."), pika_object_get_name (procedure), g_param_spec_get_name (pspec), i + 1, g_type_name (spec_type), g_type_name (arg_type)); } else { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT, _("Procedure '%s' has been called with a " "wrong value type for argument '%s' (#%d). " "Expected %s, got %s."), pika_object_get_name (procedure), g_param_spec_get_name (pspec), i + 1, g_type_name (spec_type), g_type_name (arg_type)); } return FALSE; } else if (! (pspec->flags & PIKA_PARAM_NO_VALIDATE)) { GValue string_value = G_VALUE_INIT; g_value_init (&string_value, G_TYPE_STRING); if (g_value_type_transformable (arg_type, G_TYPE_STRING)) g_value_transform (arg, &string_value); else g_value_set_static_string (&string_value, ""); if (g_param_value_validate (pspec, arg)) { if (PIKA_IS_PARAM_SPEC_DRAWABLE (pspec) && g_value_get_object (arg) == NULL) { if (return_vals) { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned an " "invalid ID for argument '%s'. " "Most likely a plug-in is trying " "to work on a layer that doesn't " "exist any longer."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } else { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT, _("Procedure '%s' has been called with an " "invalid ID for argument '%s'. " "Most likely a plug-in is trying " "to work on a layer that doesn't " "exist any longer."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } } else if (PIKA_IS_PARAM_SPEC_IMAGE (pspec) && g_value_get_object (arg) == NULL) { if (return_vals) { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned an " "invalid ID for argument '%s'. " "Most likely a plug-in is trying " "to work on an image that doesn't " "exist any longer."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } else { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT, _("Procedure '%s' has been called with an " "invalid ID for argument '%s'. " "Most likely a plug-in is trying " "to work on an image that doesn't " "exist any longer."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } } else { const gchar *value = g_value_get_string (&string_value); if (value == NULL) value = "(null)"; if (return_vals) { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned " "'%s' as return value '%s' " "(#%d, type %s). " "This value is out of range."), pika_object_get_name (procedure), value, g_param_spec_get_name (pspec), i + 1, g_type_name (spec_type)); } else { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT, _("Procedure '%s' has been called with " "value '%s' for argument '%s' " "(#%d, type %s). " "This value is out of range."), pika_object_get_name (procedure), value, g_param_spec_get_name (pspec), i + 1, g_type_name (spec_type)); } } g_value_unset (&string_value); return FALSE; } /* UTT-8 validate all strings */ if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_STRING || (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_BOXED && G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_STRV)) { gboolean valid = TRUE; if (G_PARAM_SPEC_TYPE (pspec) == G_TYPE_PARAM_STRING) { const gchar *string = g_value_get_string (arg); if (string) valid = g_utf8_validate (string, -1, NULL); } else { const gchar **array = g_value_get_boxed (arg); if (array) { gint i; for (i = 0; array[i]; i++) { valid = g_utf8_validate (array[i], -1, NULL); if (! valid) break; } } } if (! valid) { if (return_vals) { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_RETURN_VALUE, _("Procedure '%s' returned an " "invalid UTF-8 string for argument '%s'."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } else { g_set_error (error, PIKA_PDB_ERROR, PIKA_PDB_ERROR_INVALID_ARGUMENT, _("Procedure '%s' has been called with an " "invalid UTF-8 string for argument '%s'."), pika_object_get_name (procedure), g_param_spec_get_name (pspec)); } g_value_unset (&string_value); return FALSE; } } g_value_unset (&string_value); } } return TRUE; }