/* 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 * * pikapdb.c * Copyright (C) 2019 Michael Natterer * * 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 "pika.h" #include "libpikabase/pikaprotocol.h" #include "libpikabase/pikawire.h" #include "pikagpparams.h" #include "pikapdb-private.h" #include "pikapdb_pdb.h" #include "pikapdbprocedure.h" #include "pikaplugin-private.h" #include "libpika-intl.h" /** * PikaPDB: * * Provides access to the Procedural DataBase (PDB). */ struct _PikaPDBPrivate { PikaPlugIn *plug_in; GHashTable *procedures; PikaPDBStatusType error_status; gchar *error_message; }; static void pika_pdb_dispose (GObject *object); static void pika_pdb_finalize (GObject *object); static void pika_pdb_set_error (PikaPDB *pdb, PikaValueArray *return_values); G_DEFINE_TYPE_WITH_PRIVATE (PikaPDB, pika_pdb, G_TYPE_OBJECT) #define parent_class pika_pdb_parent_class static void pika_pdb_class_init (PikaPDBClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = pika_pdb_dispose; object_class->finalize = pika_pdb_finalize; } static void pika_pdb_init (PikaPDB *pdb) { pdb->priv = pika_pdb_get_instance_private (pdb); pdb->priv->procedures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); pdb->priv->error_status = PIKA_PDB_SUCCESS; } static void pika_pdb_dispose (GObject *object) { PikaPDB *pdb = PIKA_PDB (object); g_clear_pointer (&pdb->priv->procedures, g_hash_table_unref); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_pdb_finalize (GObject *object) { PikaPDB *pdb = PIKA_PDB (object); g_clear_object (&pdb->priv->plug_in); g_clear_pointer (&pdb->priv->error_message, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } PikaPDB * _pika_pdb_new (PikaPlugIn *plug_in) { PikaPDB *pdb; g_return_val_if_fail (PIKA_IS_PLUG_IN (plug_in), NULL); pdb = g_object_new (PIKA_TYPE_PDB, NULL); pdb->priv->plug_in = g_object_ref (plug_in); return pdb; } PikaPlugIn * _pika_pdb_get_plug_in (PikaPDB *pdb) { g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); return pdb->priv->plug_in; } /** * pika_pdb_procedure_exists: * @pdb: A PDB instance. * @procedure_name: A procedure name * * This function checks if a procedure exists in the procedural * database. * * Return: %TRUE if the procedure exists, %FALSE otherwise. * * Since: 3.0 **/ gboolean pika_pdb_procedure_exists (PikaPDB *pdb, const gchar *procedure_name) { g_return_val_if_fail (PIKA_IS_PDB (pdb), FALSE); g_return_val_if_fail (pika_is_canonical_identifier (procedure_name), FALSE); return _pika_pdb_proc_exists (procedure_name); } /** * pika_pdb_lookup_procedure: * @pdb: A #PikaPDB instance. * @procedure_name: A procedure name * * This function returns the [class@Procedure] which is registered * with @procedure_name if it exists, or returns %NULL otherwise. * * The returned [class@Procedure] is owned by @pdb and must not be modified. * * Return: (nullable) (transfer none): A [class@Procedure], or %NULL. * * Since: 3.0 **/ PikaProcedure * pika_pdb_lookup_procedure (PikaPDB *pdb, const gchar *procedure_name) { PikaProcedure *procedure; g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); g_return_val_if_fail (pika_is_canonical_identifier (procedure_name), NULL); procedure = g_hash_table_lookup (pdb->priv->procedures, procedure_name); if (! procedure) { procedure = _pika_pdb_procedure_new (pdb, procedure_name); if (procedure) g_hash_table_insert (pdb->priv->procedures, g_strdup (procedure_name), procedure); } return procedure; } /** * pika_pdb_temp_procedure_name: * @pdb: the #PikaPDB object. * * Generates a unique temporary PDB name. * * This function generates a temporary PDB entry name that is * guaranteed to be unique. * * Returns: (transfer full): A unique temporary name for a temporary * PDB entry. The returned value must be freed with * g_free(). * * Since: 3.0 **/ gchar * pika_pdb_temp_procedure_name (PikaPDB *pdb) { g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); return _pika_pdb_temp_name (); } /** * pika_pdb_dump_to_file: * @pdb: A #PikaPDB. * @file: The dump file. * * Dumps the current contents of the procedural database * * This procedure dumps the contents of the procedural database to the * specified @file. The file will contain all of the information * provided for each registered procedure. * * Returns: TRUE on success. * * Since: 3.0 **/ gboolean pika_pdb_dump_to_file (PikaPDB *pdb, GFile *file) { g_return_val_if_fail (PIKA_IS_PDB (pdb), FALSE); g_return_val_if_fail (G_IS_FILE (file), FALSE); return _pika_pdb_dump (file); } /** * pika_pdb_query_procedures: * @pdb: A #PikaPDB. * @name: The regex for procedure name. * @blurb: The regex for procedure blurb. * @help: The regex for procedure help. * @help_id: The regex for procedure help-id. * @authors: The regex for procedure authors. * @copyright: The regex for procedure copyright. * @date: The regex for procedure date. * @proc_type: The regex for procedure type: { 'Internal PIKA procedure', 'PIKA Plug-in', 'PIKA Extension', 'Temporary Procedure' }. * * Queries the procedural database for its contents using regular * expression matching. * * This function queries the contents of the procedural database. It * is supplied with eight arguments matching procedures on * * { name, blurb, help, help-id, authors, copyright, date, procedure type}. * * This is accomplished using regular expression matching. For * instance, to find all procedures with "jpeg" listed in the blurb, * all seven arguments can be supplied as ".*", except for the second, * which can be supplied as ".*jpeg.*". There are two return arguments * for this procedure. The first is the number of procedures matching * the query. The second is a concatenated list of procedure names * corresponding to those matching the query. If no matching entries * are found, then the returned string is NULL and the number of * entries is 0. * * Returns: (array zero-terminated=1) (transfer full): The list * of procedure names. Free with g_strfreev(). * * Since: 3.0 **/ gchar ** pika_pdb_query_procedures (PikaPDB *pdb, const gchar *name, const gchar *blurb, const gchar *help, const gchar *help_id, const gchar *authors, const gchar *copyright, const gchar *date, const gchar *proc_type) { gchar **matches; g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); _pika_pdb_query (name, blurb, help, /* FIXME help_id */ authors, copyright, date, proc_type, &matches); return matches; } GQuark _pika_pdb_error_quark (void) { return g_quark_from_static_string ("pika-pdb-error-quark"); } /* Temporary API, to go away before 3.0 */ /** * pika_pdb_get_last_error: * @pdb: a #PikaPDB. * * Retrieves the error message from the last procedure call. * * If a procedure call fails, then it might pass an error message with * the return values. Plug-ins that are using the libpika C wrappers * don't access the procedure return values directly. Thus #PikaPDB * stores the error message and makes it available with this * function. The next procedure call unsets the error message again. * * The returned string is owned by @pdb and must not be freed or * modified. * * Returns: the error message * * Since: 3.0 **/ const gchar * pika_pdb_get_last_error (PikaPDB *pdb) { g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); if (pdb->priv->error_message && strlen (pdb->priv->error_message)) return pdb->priv->error_message; switch (pdb->priv->error_status) { case PIKA_PDB_SUCCESS: /* procedure executed successfully */ return _("success"); case PIKA_PDB_EXECUTION_ERROR: /* procedure execution failed */ return _("execution error"); case PIKA_PDB_CALLING_ERROR: /* procedure called incorrectly */ return _("calling error"); case PIKA_PDB_CANCEL: /* procedure execution cancelled */ return _("cancelled"); default: return "invalid return status"; } } /** * pika_pdb_get_last_status: * @pdb: a #PikaPDB. * * Retrieves the status from the last procedure call. * * Returns: the #PikaPDBStatusType. * * Since: 3.0 **/ PikaPDBStatusType pika_pdb_get_last_status (PikaPDB *pdb) { g_return_val_if_fail (PIKA_IS_PDB (pdb), PIKA_PDB_SUCCESS); return pdb->priv->error_status; } /* Cruft API */ /** * pika_pdb_get_data: * @identifier: The identifier associated with data. * @data: A byte array containing data. * * Returns data associated with the specified identifier. * * This procedure returns any data which may have been associated with * the specified identifier. The data is copied into the given memory * location. * * WARNING: this function is exported in the library so that it can be used by * libpikawidgets. Nevertheless it is considered internal, and is not declared * in any public header on purpose. It should not be considered part of the API * and therefore should not be used in plug-ins. It may disappear at any time. * * Returns: TRUE on success, FALSE if no data has been associated with * the identifier */ gboolean pika_pdb_get_data (const gchar *identifier, GBytes **data) { return _pika_pdb_get_data (identifier, data); } /** * pika_pdb_set_data: * @identifier: The identifier associated with data. * @data: (array length=data_len): A byte array containing data. * @data_len: The number of bytes in the data * * Associates the specified identifier with the supplied data. * * This procedure associates the supplied data with the provided * identifier. The data may be subsequently retrieved by a call to * 'procedural-db-get-data'. This storage is global within the session, even * shareable between plug-ins, though it won't survive a restart of PIKA. * * WARNING: this function is exported in the library so that it can be used by * libpikawidgets. Nevertheless it is considered internal, and is not declared * in any public header on purpose. It should not be considered part of the API * and therefore should not be used in plug-ins. It may disappear at any time. * Returns: TRUE on success. */ gboolean pika_pdb_set_data (const gchar *identifier, GBytes *data) { return _pika_pdb_set_data (identifier, data); } /** * _pika_pdb_run_procedure_array: * @pdb: the #PikaPDB object. * @procedure_name: the procedure registered name. * @arguments: the call arguments. * * Runs the procedure named @procedure_name with @arguments. * * Returns: (transfer full): the return values for the procedure call. * * Since: 3.0 */ PikaValueArray * _pika_pdb_run_procedure_array (PikaPDB *pdb, const gchar *procedure_name, const PikaValueArray *arguments) { GPProcRun proc_run; GPProcReturn *proc_return; PikaWireMessage msg; PikaValueArray *return_values; g_return_val_if_fail (PIKA_IS_PDB (pdb), NULL); g_return_val_if_fail (pika_is_canonical_identifier (procedure_name), NULL); g_return_val_if_fail (arguments != NULL, NULL); proc_run.name = (gchar *) procedure_name; proc_run.n_params = pika_value_array_length (arguments); proc_run.params = _pika_value_array_to_gp_params (arguments, FALSE); if (! gp_proc_run_write (_pika_plug_in_get_write_channel (pdb->priv->plug_in), &proc_run, pdb->priv->plug_in)) pika_quit (); _pika_gp_params_free (proc_run.params, proc_run.n_params, FALSE); _pika_plug_in_read_expect_msg (pdb->priv->plug_in, &msg, GP_PROC_RETURN); proc_return = msg.data; return_values = _pika_gp_params_to_value_array (NULL, NULL, 0, proc_return->params, proc_return->n_params, TRUE); pika_wire_destroy (&msg); pika_pdb_set_error (pdb, return_values); return return_values; } /* private functions */ static void pika_pdb_set_error (PikaPDB *pdb, PikaValueArray *return_values) { g_clear_pointer (&pdb->priv->error_message, g_free); pdb->priv->error_status = PIKA_PDB_SUCCESS; if (pika_value_array_length (return_values) > 0) { pdb->priv->error_status = PIKA_VALUES_GET_ENUM (return_values, 0); switch (pdb->priv->error_status) { case PIKA_PDB_SUCCESS: case PIKA_PDB_PASS_THROUGH: break; case PIKA_PDB_EXECUTION_ERROR: case PIKA_PDB_CALLING_ERROR: case PIKA_PDB_CANCEL: if (pika_value_array_length (return_values) > 1) { GValue *value = pika_value_array_index (return_values, 1); if (G_VALUE_HOLDS_STRING (value)) pdb->priv->error_message = g_value_dup_string (value); } break; } } }