/* 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 "core-types.h" #include "pika.h" #include "pika-batch.h" #include "pikaparamspecs.h" #include "pdb/pikapdb.h" #include "pdb/pikaprocedure.h" #include "plug-in/pikapluginmanager.h" #include "plug-in/pikapluginprocedure.h" #include "pika-intl.h" static void pika_batch_exit_after_callback (Pika *pika) G_GNUC_NORETURN; static gint pika_batch_run_cmd (Pika *pika, const gchar *proc_name, PikaProcedure *procedure, PikaRunMode run_mode, const gchar *cmd); gint pika_batch_run (Pika *pika, const gchar *batch_interpreter, const gchar **batch_commands) { PikaProcedure *eval_proc; GSList *batch_procedures; GSList *iter; gulong exit_id; gint retval = EXIT_SUCCESS; if (! batch_commands || ! batch_commands[0]) return retval; batch_procedures = pika_plug_in_manager_get_batch_procedures (pika->plug_in_manager); if (g_slist_length (batch_procedures) == 0) { g_message (_("No batch interpreters are available. " "Batch mode disabled.")); retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */ return retval; } if (! batch_interpreter) { batch_interpreter = g_getenv ("PIKA_BATCH_INTERPRETER"); if (! batch_interpreter) { if (g_slist_length (batch_procedures) == 1) { batch_interpreter = pika_object_get_name (batch_procedures->data);; if (pika->be_verbose) g_printerr (_("No batch interpreter specified, using " "'%s'.\n"), batch_interpreter); } else { retval = 64; /* EX_USAGE - command line usage error */ g_print ("%s\n\n%s\n", _("No batch interpreter specified."), _("Available interpreters are:")); for (iter = batch_procedures; iter; iter = iter->next) { PikaPlugInProcedure *proc = iter->data; gchar *locale_name; locale_name = g_locale_from_utf8 (proc->batch_interpreter_name, -1, NULL, NULL, NULL); g_print ("- %s (%s)\n", pika_object_get_name (iter->data), locale_name ? locale_name : proc->batch_interpreter_name); g_free (locale_name); } g_print ("\n%s\n", _("Specify one of these interpreters as --batch-interpreter option.")); return retval; } } } for (iter = batch_procedures; iter; iter = iter->next) { if (g_strcmp0 (pika_object_get_name (iter->data), batch_interpreter) == 0) break; } if (iter == NULL) { retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */ g_print (_("The procedure '%s' is not a valid batch interpreter."), batch_interpreter); g_print ("\n%s\n\n%s\n", _("Batch mode disabled."), _("Available interpreters are:")); for (iter = batch_procedures; iter; iter = iter->next) { PikaPlugInProcedure *proc = iter->data; gchar *locale_name; locale_name = g_locale_from_utf8 (proc->batch_interpreter_name, -1, NULL, NULL, NULL); g_print ("- %s (%s)\n", pika_object_get_name (iter->data), locale_name ? locale_name : proc->batch_interpreter_name); g_free (locale_name); } g_print ("\n%s\n", _("Specify one of these interpreters as --batch-interpreter option.")); return retval; } exit_id = g_signal_connect_after (pika, "exit", G_CALLBACK (pika_batch_exit_after_callback), NULL); eval_proc = pika_pdb_lookup_procedure (pika->pdb, batch_interpreter); if (eval_proc) { gint i; retval = EXIT_SUCCESS; for (i = 0; batch_commands[i]; i++) { retval = pika_batch_run_cmd (pika, batch_interpreter, eval_proc, PIKA_RUN_NONINTERACTIVE, batch_commands[i]); /* In case of several commands, stop and return last * failed command. */ if (retval != EXIT_SUCCESS) { g_printerr ("Stopping at failing batch command [%d]: %s\n", i, batch_commands[i]); break; } } } else { retval = 69; /* EX_UNAVAILABLE - service unavailable (sysexits.h) */ g_message (_("The batch interpreter '%s' is not available. " "Batch mode disabled."), batch_interpreter); } g_signal_handler_disconnect (pika, exit_id); return retval; } /* * The purpose of this handler is to exit PIKA cleanly when the batch * procedure calls the pika-exit procedure. Without this callback, the * message "batch command experienced an execution error" would appear * and pika would hang forever. */ static void pika_batch_exit_after_callback (Pika *pika) { if (pika->be_verbose) g_print ("EXIT: %s\n", G_STRFUNC); gegl_exit (); exit (EXIT_SUCCESS); } static inline gboolean PIKA_IS_PARAM_SPEC_RUN_MODE (GParamSpec *pspec) { return (G_IS_PARAM_SPEC_ENUM (pspec) && pspec->value_type == PIKA_TYPE_RUN_MODE); } static gint pika_batch_run_cmd (Pika *pika, const gchar *proc_name, PikaProcedure *procedure, PikaRunMode run_mode, const gchar *cmd) { PikaValueArray *args; PikaValueArray *return_vals; GError *error = NULL; gint i = 0; gint retval = EXIT_SUCCESS; args = pika_procedure_get_arguments (procedure); if (procedure->num_args > i && PIKA_IS_PARAM_SPEC_RUN_MODE (procedure->args[i])) { g_value_set_enum (pika_value_array_index (args, i++), run_mode); } if (procedure->num_args > i && G_IS_PARAM_SPEC_STRING (procedure->args[i])) { g_value_set_static_string (pika_value_array_index (args, i++), cmd); } return_vals = pika_pdb_execute_procedure_by_name_args (pika->pdb, pika_get_user_context (pika), NULL, &error, proc_name, args); switch (g_value_get_enum (pika_value_array_index (return_vals, 0))) { case PIKA_PDB_EXECUTION_ERROR: /* Using Linux's standard exit code as found in /usr/include/sysexits.h * Since other platforms may not have the header, I simply * hardcode the few cases. */ retval = 70; /* EX_SOFTWARE - internal software error */ if (error) { g_printerr ("batch command experienced an execution error:\n" "%s\n", error->message); } else { g_printerr ("batch command experienced an execution error\n"); } break; case PIKA_PDB_CALLING_ERROR: retval = 64; /* EX_USAGE - command line usage error */ if (error) { g_printerr ("batch command experienced a calling error:\n" "%s\n", error->message); } else { g_printerr ("batch command experienced a calling error\n"); } break; case PIKA_PDB_SUCCESS: retval = EXIT_SUCCESS; g_printerr ("batch command executed successfully\n"); break; case PIKA_PDB_CANCEL: /* Not in sysexits.h, but usually used for 'Script terminated by * Control-C'. See: https://tldp.org/LDP/abs/html/exitcodes.html */ retval = 130; break; case PIKA_PDB_PASS_THROUGH: retval = EXIT_FAILURE; /* Catchall. */ break; } pika_value_array_unref (return_vals); pika_value_array_unref (args); if (error) g_error_free (error); return retval; }