mirror of https://github.com/GNOME/gimp.git
ScriptFu: fix #10307 plugin call display not effective
This benefits script authors and testers of ScriptFu. Now a call to (display "foo") in a plugin goes to the terminal where GIMP started. Whether interactive or in batch mode. Make TS errors go to an error port instead of the output port. Tool plugins: Console, Eval, Server get error messages from the error port. TextConsole not changed. Tools behave per new doc "ScriptFu Tools" at dev web site. Driveby fix of SF Server: send whole message instead of byte by byte. Driveby comments and more semantic checking of set-output-port in TS. Add test plugin test-display.scm
This commit is contained in:
parent
1247c8ba2e
commit
20b19b960e
|
@ -496,10 +496,19 @@ script_fu_editor_key_function (GtkWidget *widget,
|
||||||
|
|
||||||
is_error = script_fu_interpret_string (command);
|
is_error = script_fu_interpret_string (command);
|
||||||
|
|
||||||
script_fu_output_to_console (is_error,
|
/* Send captured stdout to console, w/o emphasis. */
|
||||||
output->str,
|
script_fu_output_to_console (FALSE, output->str, output->len, console);
|
||||||
output->len,
|
/* Assert the output had a trailing newline, possibly an empty line. */
|
||||||
console);
|
|
||||||
|
if (is_error)
|
||||||
|
{
|
||||||
|
/* Send error text to console, w emphasis. */
|
||||||
|
const gchar *text = script_fu_get_error_msg ();
|
||||||
|
|
||||||
|
script_fu_output_to_console (TRUE, text, strlen (text), console);
|
||||||
|
|
||||||
|
g_free ( (gpointer) text);
|
||||||
|
}
|
||||||
|
|
||||||
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
||||||
GIMP_PDB_ERROR_HANDLER_INTERNAL);
|
GIMP_PDB_ERROR_HANDLER_INTERNAL);
|
||||||
|
|
|
@ -328,6 +328,15 @@ ts_get_success_msg (void)
|
||||||
return "Success";
|
return "Success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Delegate. The caller doesn't know the scheme instance,
|
||||||
|
* and here we don't know TS internals.
|
||||||
|
*/
|
||||||
|
const gchar*
|
||||||
|
ts_get_error_msg (void)
|
||||||
|
{
|
||||||
|
return ts_get_error_string (&sc);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ts_stdout_output_func (TsOutputType type,
|
ts_stdout_output_func (TsOutputType type,
|
||||||
const char *string,
|
const char *string,
|
||||||
|
|
|
@ -33,6 +33,7 @@ void ts_set_print_flag (gint print_flag);
|
||||||
void ts_print_welcome (void);
|
void ts_print_welcome (void);
|
||||||
|
|
||||||
const gchar * ts_get_success_msg (void);
|
const gchar * ts_get_success_msg (void);
|
||||||
|
const gchar * ts_get_error_msg (void);
|
||||||
|
|
||||||
void ts_interpret_stdin (void);
|
void ts_interpret_stdin (void);
|
||||||
|
|
||||||
|
|
|
@ -43,14 +43,20 @@
|
||||||
|
|
||||||
|
|
||||||
/* Interpret a command.
|
/* Interpret a command.
|
||||||
|
* A command is a string for a Scheme call to a script plugin's run function.
|
||||||
*
|
*
|
||||||
* When errors during interpretation:
|
* When errors during interpretation:
|
||||||
* 1) set the error message from tinyscheme into GError at given handle.
|
* 1) set the error message from tinyscheme into GError at given handle.
|
||||||
* 2) return FALSE
|
* 2) return FALSE
|
||||||
* otherwise, return TRUE and discard any result of interpretation
|
* Otherwise, return TRUE and discard any result of interpretation.
|
||||||
* ScriptFu return values only have a GimpPDBStatus,
|
* ScriptFu return values only have a GimpPDBStatus,
|
||||||
* since ScriptFu plugin scripts can only be declared returning void.
|
* since ScriptFu plugin scripts can only be declared returning void.
|
||||||
*
|
*
|
||||||
|
* In v2, we captured output from a script (calls to Scheme:display)
|
||||||
|
* and they were a prefix of any error message.
|
||||||
|
* In v3, output from a script is shown in any stdout/terminal in which Gimp was started.
|
||||||
|
* And any error msg is retrieved from the inner interpreter.
|
||||||
|
*
|
||||||
* While interpreting, any errors from further calls to the PDB
|
* While interpreting, any errors from further calls to the PDB
|
||||||
* can show error dialogs in any GIMP gui,
|
* can show error dialogs in any GIMP gui,
|
||||||
* unless the caller has taken responsibility with a prior call to
|
* unless the caller has taken responsibility with a prior call to
|
||||||
|
@ -63,25 +69,17 @@ gboolean
|
||||||
script_fu_run_command (const gchar *command,
|
script_fu_run_command (const gchar *command,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GString *output;
|
|
||||||
gboolean success = FALSE;
|
|
||||||
|
|
||||||
g_debug ("script_fu_run_command: %s", command);
|
g_debug ("script_fu_run_command: %s", command);
|
||||||
output = g_string_new (NULL);
|
|
||||||
script_fu_redirect_output_to_gstr (output);
|
|
||||||
|
|
||||||
if (script_fu_interpret_string (command))
|
if (script_fu_interpret_string (command))
|
||||||
{
|
{
|
||||||
g_set_error (error, GIMP_PLUG_IN_ERROR, 0, "%s", output->str);
|
*error = script_fu_get_gerror ();
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
success = TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_string_free (output, TRUE);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include "scheme-wrapper.h"
|
#include "scheme-wrapper.h"
|
||||||
|
|
||||||
|
#include "script-fu-lib.h"
|
||||||
#include "script-fu-types.h"
|
#include "script-fu-types.h"
|
||||||
|
|
||||||
#include "script-fu-interface.h"
|
#include "script-fu-interface.h"
|
||||||
|
@ -773,11 +774,16 @@ script_fu_update_models (SFScript *script)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Handler for event: OK button clicked. */
|
/* Handler for event: OK button clicked.
|
||||||
|
*
|
||||||
|
* Run the scripts with values from the dialog.
|
||||||
|
*
|
||||||
|
* Sets a global status of the PDB call to this plugin,
|
||||||
|
* which is returned later by interface_dialog.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
script_fu_ok (SFScript *script)
|
script_fu_ok (SFScript *script)
|
||||||
{
|
{
|
||||||
GString *output;
|
|
||||||
gchar *command;
|
gchar *command;
|
||||||
|
|
||||||
script_fu_update_models (script);
|
script_fu_update_models (script);
|
||||||
|
@ -785,26 +791,29 @@ script_fu_ok (SFScript *script)
|
||||||
command = script_fu_script_get_command (script);
|
command = script_fu_script_get_command (script);
|
||||||
|
|
||||||
/* run the command through the interpreter */
|
/* run the command through the interpreter */
|
||||||
output = g_string_new (NULL);
|
|
||||||
ts_register_output_func (ts_gstring_output_func, output);
|
|
||||||
|
|
||||||
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
||||||
GIMP_PDB_ERROR_HANDLER_PLUGIN);
|
GIMP_PDB_ERROR_HANDLER_PLUGIN);
|
||||||
|
|
||||||
|
script_fu_redirect_output_to_stdout ();
|
||||||
|
|
||||||
|
/* Returns non-zero error code on failure. */
|
||||||
if (ts_interpret_string (command))
|
if (ts_interpret_string (command))
|
||||||
{
|
{
|
||||||
gchar *message = g_strdup_printf (_("Error while executing %s:"),
|
gchar *message;
|
||||||
script->name);
|
|
||||||
|
|
||||||
g_message ("%s\n\n%s", message, output->str);
|
/* Log to stdout. Later to Gimp. */
|
||||||
|
message = g_strdup_printf (_("Error while executing %s:"), script->name);
|
||||||
|
g_message ("%s\n", message);
|
||||||
g_free (message);
|
g_free (message);
|
||||||
|
|
||||||
|
/* Set global to be returned by script-fu-interface-dialog. */
|
||||||
|
sf_status = GIMP_PDB_EXECUTION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
|
||||||
GIMP_PDB_ERROR_HANDLER_INTERNAL);
|
GIMP_PDB_ERROR_HANDLER_INTERNAL);
|
||||||
|
|
||||||
g_string_free (output, TRUE);
|
|
||||||
|
|
||||||
g_free (command);
|
g_free (command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,41 @@ script_fu_get_success_msg (void)
|
||||||
return ts_get_success_msg ();
|
return ts_get_success_msg ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return an error message string for recent failure of script.
|
||||||
|
*
|
||||||
|
* Requires an interpretation just returned an error, else returns "Unknown".
|
||||||
|
* Should be called exactly once per error, else second calls return "Unknown".
|
||||||
|
*
|
||||||
|
* Transfer ownership to caller, the string must be freed.
|
||||||
|
*/
|
||||||
|
const gchar *
|
||||||
|
script_fu_get_error_msg (void)
|
||||||
|
{
|
||||||
|
return ts_get_error_msg ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return a GError for recent failure of script.
|
||||||
|
*
|
||||||
|
* Requires an interpretation just returned an error,
|
||||||
|
* else returns a GError with message "Unknown".
|
||||||
|
* Should be called exactly once per error
|
||||||
|
*
|
||||||
|
* You should call either get_error_msg, or get_gerror, but not both.
|
||||||
|
*
|
||||||
|
* Transfers ownership, caller must free the GError.
|
||||||
|
*/
|
||||||
|
GError *
|
||||||
|
script_fu_get_gerror (void)
|
||||||
|
{
|
||||||
|
const gchar *error_message;
|
||||||
|
GError *result;
|
||||||
|
|
||||||
|
error_message = script_fu_get_error_msg ();
|
||||||
|
result = g_error_new_literal (g_quark_from_string ("scriptfu"), 0, error_message);
|
||||||
|
g_free ((gpointer) error_message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
script_fu_run_read_eval_print_loop (void)
|
script_fu_run_read_eval_print_loop (void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,8 @@ void script_fu_print_welcome (void);
|
||||||
|
|
||||||
gboolean script_fu_interpret_string (const gchar *text);
|
gboolean script_fu_interpret_string (const gchar *text);
|
||||||
const gchar *script_fu_get_success_msg (void);
|
const gchar *script_fu_get_success_msg (void);
|
||||||
|
const gchar *script_fu_get_error_msg (void);
|
||||||
|
GError *script_fu_get_gerror (void);
|
||||||
|
|
||||||
void script_fu_run_read_eval_print_loop (void);
|
void script_fu_run_read_eval_print_loop (void);
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "scheme-wrapper.h" /* type "pointer" */
|
#include "scheme-wrapper.h" /* type "pointer" */
|
||||||
|
|
||||||
|
#include "script-fu-lib.h"
|
||||||
#include "script-fu-types.h"
|
#include "script-fu-types.h"
|
||||||
#include "script-fu-interface.h" /* ScriptFu's GUI implementation. */
|
#include "script-fu-interface.h" /* ScriptFu's GUI implementation. */
|
||||||
#include "script-fu-dialog.h" /* Gimp's GUI implementation. */
|
#include "script-fu-dialog.h" /* Gimp's GUI implementation. */
|
||||||
|
@ -185,15 +186,23 @@ script_fu_run_procedure (GimpProcedure *procedure,
|
||||||
/* First, try to collect the standard script arguments... */
|
/* First, try to collect the standard script arguments... */
|
||||||
min_args = script_fu_script_collect_standard_args (script, pspecs, n_pspecs, config);
|
min_args = script_fu_script_collect_standard_args (script, pspecs, n_pspecs, config);
|
||||||
|
|
||||||
/* ...then acquire the rest of arguments (if any) with a dialog */
|
/* If plugin has more than the standard args. */
|
||||||
if (script->n_args > min_args)
|
if (script->n_args > min_args)
|
||||||
{
|
{
|
||||||
|
/* Get the rest of arguments with a dialog, and run the command. */
|
||||||
status = script_fu_interface_dialog (script, min_args);
|
status = script_fu_interface_dialog (script, min_args);
|
||||||
|
|
||||||
|
if (status == GIMP_PDB_EXECUTION_ERROR)
|
||||||
|
return gimp_procedure_new_return_values (procedure, status,
|
||||||
|
script_fu_get_gerror ());
|
||||||
|
|
||||||
|
/* Else no error, or GIMP_PDB_CANCEL.
|
||||||
|
* GIMP_PDB_CALLING_ERROR is emitted prior to this.
|
||||||
|
* Break and return without an error message.
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* otherwise (if the script takes no more arguments), skip
|
/* Else fallthrough to next case and run the script without dialog. */
|
||||||
* this part and run the script directly (fallthrough)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case GIMP_RUN_NONINTERACTIVE:
|
case GIMP_RUN_NONINTERACTIVE:
|
||||||
|
|
|
@ -9,6 +9,8 @@ EXPORTS
|
||||||
script_fu_print_welcome
|
script_fu_print_welcome
|
||||||
script_fu_interpret_string
|
script_fu_interpret_string
|
||||||
script_fu_get_success_msg
|
script_fu_get_success_msg
|
||||||
|
script_fu_get_error_msg
|
||||||
|
script_fu_get_gerror
|
||||||
script_fu_run_read_eval_print_loop
|
script_fu_run_read_eval_print_loop
|
||||||
script_fu_register_quit_callback
|
script_fu_register_quit_callback
|
||||||
script_fu_register_post_command_callback
|
script_fu_register_post_command_callback
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
/* GIMP - The GNU Image Manipulation Program
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "scheme-private.h"
|
||||||
|
#include "string-port.h"
|
||||||
|
|
||||||
|
#include "error-port.h"
|
||||||
|
|
||||||
|
/* Error port.
|
||||||
|
*
|
||||||
|
* TinyScheme now writes all errors to a separate output string-port
|
||||||
|
* (instead of to the output port.)
|
||||||
|
* Similar to the way UNIX has distinct streams stdout and stderr.
|
||||||
|
*
|
||||||
|
* A program (e.g. ScriptFu) that wraps the inner TinyScheme interpreter
|
||||||
|
* can retrieve error messages.
|
||||||
|
*
|
||||||
|
* A wrapping program can also redirect the inner interpreter's non-error output
|
||||||
|
* but differently, using ts_set_output_func.
|
||||||
|
*
|
||||||
|
* The events:
|
||||||
|
* When the inner interpreter declares an error
|
||||||
|
* it redirects its output to the error port,
|
||||||
|
* writes the error message, and then returns -1 retcode.
|
||||||
|
* Output means internal calls to putbytes.
|
||||||
|
* Interpreter is aborting so won't interpret any more writes in the script.
|
||||||
|
* The wrapping program then gets the error message,
|
||||||
|
* which also clears the error port, ready for the next interpretation.
|
||||||
|
*
|
||||||
|
* When interpretation proceeds without error,
|
||||||
|
* no error port is created.
|
||||||
|
* Existence of error port means:
|
||||||
|
* - interpreter is in error state, after a final call to internal error
|
||||||
|
* Note that exception handling occurs before this,
|
||||||
|
* so a script can still intercept errors using *error-hook*.
|
||||||
|
* - subsequent writes by putbytes are to the error port,
|
||||||
|
* until the error port is retrieved and destroyed,
|
||||||
|
* by the outer interpreter.
|
||||||
|
* Subsequent writes by putbytes are all writing error message
|
||||||
|
* and args to (error ... )
|
||||||
|
*
|
||||||
|
* Unlike other fields in scheme struct,
|
||||||
|
* errport is not type pointer i.e. does not point to a cell.
|
||||||
|
* The error port is internal to the interpreter. Scripts cannot access it.
|
||||||
|
* It is a singleton.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialize so there is no error port.
|
||||||
|
*
|
||||||
|
* Ensure the next interpretation is w/o redirected errors.
|
||||||
|
* Note the string-port API has no way to empty an output port.
|
||||||
|
* We create/destroy the error port.
|
||||||
|
*
|
||||||
|
* Any existing error port should already be disposed, else leaks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
error_port_init (scheme *sc)
|
||||||
|
{
|
||||||
|
sc->errport = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is interpreter in error state and redirecting to the error port? */
|
||||||
|
gboolean
|
||||||
|
error_port_is_redirect_output (scheme *sc)
|
||||||
|
{
|
||||||
|
return sc->errport != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the error port so subsequent putbytes (which every IO write uses)
|
||||||
|
* is to the error port.
|
||||||
|
* Even writes passing a port: (write <foo> <port>)
|
||||||
|
* will write to the error port instead of the passed port.
|
||||||
|
*
|
||||||
|
* Requires no error port exist already. When it does, memory can leak.
|
||||||
|
*
|
||||||
|
* Ensure the port is kind string, direction output.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
error_port_redirect_output (scheme *sc)
|
||||||
|
{
|
||||||
|
g_debug ("%s", G_STRFUNC);
|
||||||
|
|
||||||
|
if (sc->errport != NULL)
|
||||||
|
g_warning ("%s error port exists already", G_STRFUNC);
|
||||||
|
|
||||||
|
sc->errport = string_port_open_output_port (sc);
|
||||||
|
|
||||||
|
g_assert (sc->errport->kind & (port_output | port_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the errport or NULL.
|
||||||
|
* When non-null, interpreter is in an error state, is aborting.
|
||||||
|
*/
|
||||||
|
port*
|
||||||
|
error_port_get_port_rep (scheme *sc)
|
||||||
|
{
|
||||||
|
return sc->errport;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the content string of the error port and close the port.
|
||||||
|
*
|
||||||
|
* The returned string is owned by the caller and must be freed.
|
||||||
|
*
|
||||||
|
* This must be called exactly once per inner interpretation.
|
||||||
|
* Destroys the error port so next interpretation is not writing to error port.
|
||||||
|
*
|
||||||
|
* Require the inner interpreter just returned to the caller
|
||||||
|
* (the wrapping interpreter) with an error status.
|
||||||
|
* Else the returned string is "Unknown" and not the actual error.
|
||||||
|
*/
|
||||||
|
const gchar *error_port_take_string_and_close (scheme *sc)
|
||||||
|
{
|
||||||
|
gchar *result;
|
||||||
|
port *port = error_port_get_port_rep (sc);
|
||||||
|
|
||||||
|
if (port != NULL)
|
||||||
|
{
|
||||||
|
result = g_strdup (port->rep.string.start);
|
||||||
|
|
||||||
|
string_port_dispose_struct (sc, port);
|
||||||
|
|
||||||
|
error_port_init (sc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Not expected to happen. Untranslated. */
|
||||||
|
result = g_strdup ("Unknown error");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* GIMP - The GNU Image Manipulation Program
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ERROR_PORT_H__
|
||||||
|
#define __ERROR_PORT_H__
|
||||||
|
|
||||||
|
void error_port_init (scheme *sc);
|
||||||
|
void error_port_redirect_output (scheme *sc);
|
||||||
|
gboolean error_port_is_redirect_output (scheme *sc);
|
||||||
|
|
||||||
|
port *error_port_get_port_rep (scheme *sc);
|
||||||
|
const gchar *error_port_take_string_and_close (scheme *sc);
|
||||||
|
|
||||||
|
#endif /* __ERROR_PORT_H__ */
|
|
@ -4,6 +4,7 @@
|
||||||
scriptfu_tinyscheme = static_library('scriptfu-tinyscheme',
|
scriptfu_tinyscheme = static_library('scriptfu-tinyscheme',
|
||||||
'scheme.c',
|
'scheme.c',
|
||||||
'string-port.c',
|
'string-port.c',
|
||||||
|
'error-port.c',
|
||||||
include_directories: [ rootInclude, ],
|
include_directories: [ rootInclude, ],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
glib,
|
glib,
|
||||||
|
|
|
@ -131,6 +131,7 @@ pointer inport;
|
||||||
pointer outport;
|
pointer outport;
|
||||||
pointer save_inport;
|
pointer save_inport;
|
||||||
pointer loadport;
|
pointer loadport;
|
||||||
|
port *errport; /* Not a cell, a port struct. */
|
||||||
|
|
||||||
#ifndef MAXFIL
|
#ifndef MAXFIL
|
||||||
#define MAXFIL 64
|
#define MAXFIL 64
|
||||||
|
|
|
@ -45,11 +45,15 @@
|
||||||
|
|
||||||
#include "scheme-private.h"
|
#include "scheme-private.h"
|
||||||
#include "string-port.h"
|
#include "string-port.h"
|
||||||
|
#include "error-port.h"
|
||||||
|
|
||||||
#if !STANDALONE
|
#if !STANDALONE
|
||||||
static ts_output_func ts_output_handler = NULL;
|
static ts_output_func ts_output_handler = NULL;
|
||||||
static gpointer ts_output_data = NULL;
|
static gpointer ts_output_data = NULL;
|
||||||
|
|
||||||
|
/* Register an output func from a wrapping interpeter.
|
||||||
|
* Typically the output func writes to a string, or to stdout.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
ts_register_output_func (ts_output_func func,
|
ts_register_output_func (ts_output_func func,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
|
@ -70,6 +74,28 @@ ts_output_string (TsOutputType type,
|
||||||
if (ts_output_handler && len > 0)
|
if (ts_output_handler && len > 0)
|
||||||
(* ts_output_handler) (type, string, len, ts_output_data);
|
(* ts_output_handler) (type, string, len, ts_output_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ts_is_output_redirected (void)
|
||||||
|
{
|
||||||
|
return ts_output_handler != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns string of errors declared by interpreter or script.
|
||||||
|
*
|
||||||
|
* You must call when a script has returned an error flag.
|
||||||
|
* Side effect is to clear: you should only call once.
|
||||||
|
*
|
||||||
|
* When called when the script did not return an error flag,
|
||||||
|
* returns "Unknown error"
|
||||||
|
*
|
||||||
|
* Returned string is transfered, owned by the caller and must be freed.
|
||||||
|
*/
|
||||||
|
const gchar*
|
||||||
|
ts_get_error_string (scheme *sc)
|
||||||
|
{
|
||||||
|
return error_port_take_string_and_close (sc);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Used for documentation purposes, to signal functions in 'interface' */
|
/* Used for documentation purposes, to signal functions in 'interface' */
|
||||||
|
@ -103,6 +129,19 @@ ts_output_string (TsOutputType type,
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Set current outport with checks for validity. */
|
||||||
|
void
|
||||||
|
set_outport (scheme * sc, pointer arg)
|
||||||
|
{
|
||||||
|
if (! is_port (arg))
|
||||||
|
g_warning ("%s arg not a port", G_STRFUNC);
|
||||||
|
|
||||||
|
if ( ! is_outport (arg) )
|
||||||
|
g_warning ("%s port not an output port, or closed", G_STRFUNC);
|
||||||
|
|
||||||
|
sc->outport = arg;
|
||||||
|
}
|
||||||
|
|
||||||
#define stricmp utf8_stricmp
|
#define stricmp utf8_stricmp
|
||||||
|
|
||||||
static int utf8_stricmp(const char *s1, const char *s2)
|
static int utf8_stricmp(const char *s1, const char *s2)
|
||||||
|
@ -1526,8 +1565,10 @@ static void file_pop(scheme *sc) {
|
||||||
if(sc->file_i != 0) {
|
if(sc->file_i != 0) {
|
||||||
sc->nesting=sc->nesting_stack[sc->file_i];
|
sc->nesting=sc->nesting_stack[sc->file_i];
|
||||||
port_close(sc,sc->loadport,port_input);
|
port_close(sc,sc->loadport,port_input);
|
||||||
|
/* Pop load stack, discarding port soon to be gc. */
|
||||||
sc->file_i--;
|
sc->file_i--;
|
||||||
sc->loadport->_object._port=sc->load_stack+sc->file_i;
|
/* Top of stack into current load port. */
|
||||||
|
sc->loadport->_object._port = sc->load_stack + sc->file_i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1599,7 +1640,11 @@ static pointer port_from_file(scheme *sc, FILE *f, int prop) {
|
||||||
|
|
||||||
static void port_close(scheme *sc, pointer p, int flag) {
|
static void port_close(scheme *sc, pointer p, int flag) {
|
||||||
port *pt=p->_object._port;
|
port *pt=p->_object._port;
|
||||||
pt->kind&=~flag;
|
|
||||||
|
/* Clear the direction that is closing. */
|
||||||
|
pt->kind &= ~flag;
|
||||||
|
|
||||||
|
/* If there are no directions remaining. */
|
||||||
if((pt->kind & (port_input|port_output))==0) {
|
if((pt->kind & (port_input|port_output))==0) {
|
||||||
if(pt->kind&port_file) {
|
if(pt->kind&port_file) {
|
||||||
|
|
||||||
|
@ -1613,6 +1658,7 @@ static void port_close(scheme *sc, pointer p, int flag) {
|
||||||
|
|
||||||
fclose(pt->rep.stdio.file);
|
fclose(pt->rep.stdio.file);
|
||||||
}
|
}
|
||||||
|
/* Clear port direction, kind, and saw_EOF. */
|
||||||
pt->kind=port_free;
|
pt->kind=port_free;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1729,30 +1775,45 @@ backbyte (scheme *sc, gint b)
|
||||||
static void
|
static void
|
||||||
putbytes (scheme *sc, const char *bytes, int byte_count)
|
putbytes (scheme *sc, const char *bytes, int byte_count)
|
||||||
{
|
{
|
||||||
port *pt=sc->outport->_object._port;
|
port *pt;
|
||||||
|
|
||||||
|
if (error_port_is_redirect_output (sc))
|
||||||
|
pt = error_port_get_port_rep (sc);
|
||||||
|
else
|
||||||
|
pt = sc->outport->_object._port;
|
||||||
|
|
||||||
if(pt->kind&port_file) {
|
if(pt->kind&port_file) {
|
||||||
#if STANDALONE
|
#if STANDALONE
|
||||||
fwrite (bytes, 1, byte_count, pt->rep.stdio.file);
|
fwrite (bytes, 1, byte_count, pt->rep.stdio.file);
|
||||||
fflush(pt->rep.stdio.file);
|
fflush(pt->rep.stdio.file);
|
||||||
#else
|
#else
|
||||||
/* If output is still directed to stdout (the default) it should be */
|
/* If output is still directed to stdout (the default) try
|
||||||
/* safe to redirect it to the registered output routine. */
|
* redirect it to any registered output routine.
|
||||||
|
* Currently, we require outer wrapper to set_output_func.
|
||||||
|
*/
|
||||||
if (pt->rep.stdio.file == stdout)
|
if (pt->rep.stdio.file == stdout)
|
||||||
{
|
{
|
||||||
ts_output_string (TS_OUTPUT_NORMAL, bytes, byte_count);
|
if (ts_is_output_redirected ())
|
||||||
|
ts_output_string (TS_OUTPUT_NORMAL, bytes, byte_count);
|
||||||
|
else
|
||||||
|
g_warning ("%s Output disappears since outer wrapper did not redirect.", G_STRFUNC);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* Otherwise, the script has set the output port, write to it. */
|
||||||
fwrite (bytes, 1, byte_count, pt->rep.stdio.file);
|
fwrite (bytes, 1, byte_count, pt->rep.stdio.file);
|
||||||
fflush (pt->rep.stdio.file);
|
fflush (pt->rep.stdio.file);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else if (pt->kind & port_string)
|
||||||
{
|
{
|
||||||
string_port_put_bytes (sc, pt, bytes, byte_count);
|
string_port_put_bytes (sc, pt, bytes, byte_count);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("%s closed or unknown port kind", G_STRFUNC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -4135,7 +4196,7 @@ static pointer opexe_4(scheme *sc, enum scheme_opcodes op) {
|
||||||
if(cadr(sc->args)!=sc->outport) {
|
if(cadr(sc->args)!=sc->outport) {
|
||||||
x=cons(sc,sc->outport,sc->NIL);
|
x=cons(sc,sc->outport,sc->NIL);
|
||||||
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
|
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
|
||||||
sc->outport=cadr(sc->args);
|
set_outport (sc, cadr (sc->args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sc->args = car(sc->args);
|
sc->args = car(sc->args);
|
||||||
|
@ -4151,14 +4212,20 @@ static pointer opexe_4(scheme *sc, enum scheme_opcodes op) {
|
||||||
if(car(sc->args)!=sc->outport) {
|
if(car(sc->args)!=sc->outport) {
|
||||||
x=cons(sc,sc->outport,sc->NIL);
|
x=cons(sc,sc->outport,sc->NIL);
|
||||||
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
|
s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
|
||||||
sc->outport=car(sc->args);
|
set_outport (sc, car (sc->args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
putstr(sc, "\n");
|
putstr(sc, "\n");
|
||||||
s_return(sc,sc->T);
|
s_return(sc,sc->T);
|
||||||
|
|
||||||
case OP_ERR0: /* error */
|
case OP_ERR0: /* error */
|
||||||
|
/* Subsequently, the current interpretation will abort. */
|
||||||
sc->retcode=-1;
|
sc->retcode=-1;
|
||||||
|
|
||||||
|
/* Subsequently, putbytes will write to error_port*/
|
||||||
|
error_port_redirect_output (sc);
|
||||||
|
|
||||||
|
/* Print prefix: "Error: <reason>" OR "Error: --" */
|
||||||
if (!is_string(car(sc->args))) {
|
if (!is_string(car(sc->args))) {
|
||||||
sc->args=cons(sc,mk_string(sc," -- "),sc->args);
|
sc->args=cons(sc,mk_string(sc," -- "),sc->args);
|
||||||
setimmutable(car(sc->args));
|
setimmutable(car(sc->args));
|
||||||
|
@ -4171,17 +4238,30 @@ static pointer opexe_4(scheme *sc, enum scheme_opcodes op) {
|
||||||
case OP_ERR1: /* error */
|
case OP_ERR1: /* error */
|
||||||
putstr(sc, " ");
|
putstr(sc, " ");
|
||||||
if (sc->args != sc->NIL) {
|
if (sc->args != sc->NIL) {
|
||||||
|
/* Print other args.*/
|
||||||
s_save(sc,OP_ERR1, cdr(sc->args), sc->NIL);
|
s_save(sc,OP_ERR1, cdr(sc->args), sc->NIL);
|
||||||
sc->args = car(sc->args);
|
sc->args = car(sc->args);
|
||||||
sc->print_flag = 1;
|
sc->print_flag = 1;
|
||||||
s_goto(sc,OP_P0LIST);
|
s_goto(sc,OP_P0LIST);
|
||||||
|
/* Continues at saved OP_ERR1. */
|
||||||
} else {
|
} else {
|
||||||
putstr(sc, "\n");
|
if (sc->interactive_repl)
|
||||||
if(sc->interactive_repl) {
|
{
|
||||||
s_goto(sc,OP_T0LVL);
|
/* Case is SF Text Console, not SF Console (w GUI).
|
||||||
} else {
|
* Simple write to stdout.
|
||||||
return sc->NIL;
|
*/
|
||||||
}
|
gchar *error_message = (gchar*) error_port_take_string_and_close (sc);
|
||||||
|
g_printf ("%s", error_message);
|
||||||
|
g_free (error_message);
|
||||||
|
|
||||||
|
/* Continue to read stdin and eval. */
|
||||||
|
s_goto (sc, OP_T0LVL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* ScriptFu wrapper will retrieve error message. */
|
||||||
|
return sc->NIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case OP_REVERSE: /* reverse */
|
case OP_REVERSE: /* reverse */
|
||||||
|
@ -4463,7 +4543,7 @@ static pointer opexe_5(scheme *sc, enum scheme_opcodes op) {
|
||||||
s_return(sc,sc->value);
|
s_return(sc,sc->value);
|
||||||
|
|
||||||
case OP_SET_OUTPORT: /* set-output-port */
|
case OP_SET_OUTPORT: /* set-output-port */
|
||||||
sc->outport=car(sc->args);
|
set_outport (sc, car (sc->args));
|
||||||
s_return(sc,sc->value);
|
s_return(sc,sc->value);
|
||||||
|
|
||||||
case OP_RDSEXPR:
|
case OP_RDSEXPR:
|
||||||
|
@ -5109,6 +5189,7 @@ int scheme_init_custom_alloc(scheme *sc, func_alloc malloc, func_dealloc free) {
|
||||||
sc->outport=sc->NIL;
|
sc->outport=sc->NIL;
|
||||||
sc->save_inport=sc->NIL;
|
sc->save_inport=sc->NIL;
|
||||||
sc->loadport=sc->NIL;
|
sc->loadport=sc->NIL;
|
||||||
|
error_port_init (sc);
|
||||||
sc->nesting=0;
|
sc->nesting=0;
|
||||||
sc->interactive_repl=0;
|
sc->interactive_repl=0;
|
||||||
sc->print_output=0;
|
sc->print_output=0;
|
||||||
|
@ -5222,6 +5303,7 @@ void scheme_deinit(scheme *sc) {
|
||||||
typeflag(sc->loadport) = T_ATOM;
|
typeflag(sc->loadport) = T_ATOM;
|
||||||
}
|
}
|
||||||
sc->loadport=sc->NIL;
|
sc->loadport=sc->NIL;
|
||||||
|
error_port_init (sc);
|
||||||
sc->gc_verbose=0;
|
sc->gc_verbose=0;
|
||||||
gc(sc,sc->NIL,sc->NIL);
|
gc(sc,sc->NIL,sc->NIL);
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,9 @@ typedef struct num {
|
||||||
|
|
||||||
#if !STANDALONE
|
#if !STANDALONE
|
||||||
|
|
||||||
|
/* Functions to capture and retrieve output i.e. writes using display.
|
||||||
|
* SF Tools: Console, Eval, Server uses.
|
||||||
|
*/
|
||||||
typedef enum { TS_OUTPUT_NORMAL, TS_OUTPUT_ERROR } TsOutputType;
|
typedef enum { TS_OUTPUT_NORMAL, TS_OUTPUT_ERROR } TsOutputType;
|
||||||
|
|
||||||
typedef void (* ts_output_func) (TsOutputType type,
|
typedef void (* ts_output_func) (TsOutputType type,
|
||||||
|
@ -137,6 +140,10 @@ SCHEME_EXPORT void ts_register_output_func (ts_output_func func,
|
||||||
SCHEME_EXPORT void ts_output_string (TsOutputType type,
|
SCHEME_EXPORT void ts_output_string (TsOutputType type,
|
||||||
const char *string,
|
const char *string,
|
||||||
int len);
|
int len);
|
||||||
|
|
||||||
|
/* Functions to retrieve error messages. */
|
||||||
|
SCHEME_EXPORT const gchar *ts_get_error_string (scheme *sc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SCHEME_EXPORT scheme *scheme_init_new(void);
|
SCHEME_EXPORT scheme *scheme_init_new(void);
|
||||||
|
|
|
@ -188,11 +188,18 @@ output_port_struct_from_scratch (scheme *sc)
|
||||||
|
|
||||||
pt = (port*) sc->malloc (sizeof (port));
|
pt = (port*) sc->malloc (sizeof (port));
|
||||||
if (pt == NULL)
|
if (pt == NULL)
|
||||||
return NULL;
|
{
|
||||||
|
no_memory (sc, "output port struct");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
start = sc->malloc (STRING_PORT_MIN_ALLOCATION);
|
start = sc->malloc (STRING_PORT_MIN_ALLOCATION);
|
||||||
if (start == NULL)
|
if (start == NULL)
|
||||||
return NULL;
|
{
|
||||||
|
no_memory (sc, "output port bytes");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
memset (start, '\0', STRING_PORT_MIN_ALLOCATION);
|
memset (start, '\0', STRING_PORT_MIN_ALLOCATION);
|
||||||
|
|
||||||
init_port_struct (pt, port_string|port_output, start, STRING_PORT_MIN_ALLOCATION);
|
init_port_struct (pt, port_string|port_output, start, STRING_PORT_MIN_ALLOCATION);
|
||||||
|
@ -212,7 +219,7 @@ output_port_from_scratch (scheme *sc)
|
||||||
port *pt = output_port_struct_from_scratch (sc);
|
port *pt = output_port_struct_from_scratch (sc);
|
||||||
if (pt == NULL)
|
if (pt == NULL)
|
||||||
{
|
{
|
||||||
no_memory (sc, "output port struct");
|
/* no-memory already called. */
|
||||||
return sc->NIL;
|
return sc->NIL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -273,6 +280,7 @@ output_port_expand_by_at_least (scheme *sc, port *p, size_t byte_count)
|
||||||
/* GLib MAX */
|
/* GLib MAX */
|
||||||
size_t new_size = current_content_size_bytes + MAX (byte_count, STRING_PORT_MIN_ALLOCATION) + 1;
|
size_t new_size = current_content_size_bytes + MAX (byte_count, STRING_PORT_MIN_ALLOCATION) + 1;
|
||||||
|
|
||||||
|
g_debug ("%s byte_count %" G_GSIZE_FORMAT, G_STRFUNC, byte_count);
|
||||||
g_debug ("%s current contents %" G_GSIZE_FORMAT " new size %" G_GSIZE_FORMAT, G_STRFUNC, current_content_size_bytes, new_size);
|
g_debug ("%s current contents %" G_GSIZE_FORMAT " new size %" G_GSIZE_FORMAT, G_STRFUNC, current_content_size_bytes, new_size);
|
||||||
|
|
||||||
new_buffer = sc->malloc (new_size);
|
new_buffer = sc->malloc (new_size);
|
||||||
|
@ -378,6 +386,17 @@ string_port_open_output_string (scheme *sc, pointer scheme_string)
|
||||||
return output_port_from_scratch (sc);
|
return output_port_from_scratch (sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns C pointer to a port struct, or NULL.
|
||||||
|
*
|
||||||
|
* Used for internal ports of the interpreter
|
||||||
|
* (not for port objects known by a script.)
|
||||||
|
*/
|
||||||
|
port*
|
||||||
|
string_port_open_output_port (scheme *sc)
|
||||||
|
{
|
||||||
|
return output_port_struct_from_scratch (sc);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a string-port of kind input from a Scheme string.
|
/* Create a string-port of kind input from a Scheme string.
|
||||||
*
|
*
|
||||||
* Ensures the port contents do not depend on lifetime of Scheme string.
|
* Ensures the port contents do not depend on lifetime of Scheme string.
|
||||||
|
@ -398,6 +417,24 @@ string_port_open_input_string (scheme *sc, pointer scheme_string, int prop)
|
||||||
return input_port_from_string (sc, c_string);
|
return input_port_from_string (sc, c_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Free heap allocation of a port struct.
|
||||||
|
*
|
||||||
|
* Require port is-a struct port.
|
||||||
|
* The port* must not be used again.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
string_port_dispose_struct (scheme *sc, port *port)
|
||||||
|
{
|
||||||
|
g_debug ("%s content size %ld", G_STRFUNC, strlen (port->rep.string.start) + 1);
|
||||||
|
|
||||||
|
/* Free allocated buffer. */
|
||||||
|
sc->free (port->rep.string.start);
|
||||||
|
|
||||||
|
/* Free the allocated struct itself. */
|
||||||
|
sc->free (port);
|
||||||
|
}
|
||||||
|
|
||||||
/* Free heap allocation of the Scheme object.
|
/* Free heap allocation of the Scheme object.
|
||||||
* Called during garbage collection, the cell itself is being reclaimed.
|
* Called during garbage collection, the cell itself is being reclaimed.
|
||||||
*
|
*
|
||||||
|
@ -406,13 +443,8 @@ string_port_open_input_string (scheme *sc, pointer scheme_string, int prop)
|
||||||
void
|
void
|
||||||
string_port_dispose (scheme *sc, pointer port_cell)
|
string_port_dispose (scheme *sc, pointer port_cell)
|
||||||
{
|
{
|
||||||
g_debug ("%s content size %ld", G_STRFUNC, strlen (port_cell->_object._port->rep.string.start) + 1);
|
string_port_dispose_struct (sc, port_cell->_object._port);
|
||||||
|
/* The cell still has a reference, but it is invalid. */
|
||||||
/* Free allocated buffer. */
|
|
||||||
sc->free (port_cell->_object._port->rep.string.start);
|
|
||||||
|
|
||||||
/* Free the allocated struct itself. */
|
|
||||||
sc->free (port_cell->_object._port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of Scheme GET-OUTPUT-STRING.
|
/* Implementation of Scheme GET-OUTPUT-STRING.
|
||||||
|
|
|
@ -25,7 +25,8 @@ pointer string_port_open_input_string (scheme *sc,
|
||||||
int prop);
|
int prop);
|
||||||
void string_port_dispose (scheme *sc,
|
void string_port_dispose (scheme *sc,
|
||||||
pointer port);
|
pointer port);
|
||||||
|
void string_port_dispose_struct (scheme *sc,
|
||||||
|
port *port);
|
||||||
void string_port_init_static_port (port *port,
|
void string_port_init_static_port (port *port,
|
||||||
const gchar *command);
|
const gchar *command);
|
||||||
|
|
||||||
|
@ -39,4 +40,6 @@ void string_port_put_bytes (scheme *sc,
|
||||||
pointer string_port_get_output_string (scheme *sc,
|
pointer string_port_get_output_string (scheme *sc,
|
||||||
port *port);
|
port *port);
|
||||||
|
|
||||||
|
port *string_port_open_output_port (scheme *sc);
|
||||||
|
|
||||||
#endif /* __STRING_PORT_H__ */
|
#endif /* __STRING_PORT_H__ */
|
|
@ -31,39 +31,27 @@ script_fu_eval_run (GimpProcedure *procedure,
|
||||||
const gchar *code,
|
const gchar *code,
|
||||||
GimpProcedureConfig *config)
|
GimpProcedureConfig *config)
|
||||||
{
|
{
|
||||||
GString *output = g_string_new (NULL);
|
|
||||||
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
|
||||||
|
|
||||||
script_fu_set_run_mode (run_mode);
|
script_fu_set_run_mode (run_mode);
|
||||||
script_fu_redirect_output_to_gstr (output);
|
|
||||||
|
/* IO writes by script go to stdout. */
|
||||||
|
script_fu_redirect_output_to_stdout ();
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case GIMP_RUN_NONINTERACTIVE:
|
case GIMP_RUN_NONINTERACTIVE:
|
||||||
if (script_fu_interpret_string (code) != 0)
|
if (script_fu_interpret_string (code) != 0)
|
||||||
status = GIMP_PDB_EXECUTION_ERROR;
|
return gimp_procedure_new_return_values (procedure,
|
||||||
break;
|
GIMP_PDB_EXECUTION_ERROR,
|
||||||
|
script_fu_get_gerror ());
|
||||||
case GIMP_RUN_INTERACTIVE:
|
else
|
||||||
case GIMP_RUN_WITH_LAST_VALS:
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
|
||||||
status = GIMP_PDB_CALLING_ERROR;
|
|
||||||
g_string_assign (output, _("Script-Fu evaluation mode only allows "
|
|
||||||
"non-interactive invocation"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
{
|
||||||
|
GError *error = g_error_new_literal (g_quark_from_string ("scriptfu"), 0,
|
||||||
|
_("Script-Fu evaluation mode only allows non-interactive invocation"));
|
||||||
|
|
||||||
|
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CALLING_ERROR, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != GIMP_PDB_SUCCESS && output->len > 0)
|
|
||||||
{
|
|
||||||
GError *error = g_error_new_literal (g_quark_from_string("scriptfu"), 0,
|
|
||||||
g_string_free (output, FALSE));
|
|
||||||
|
|
||||||
return gimp_procedure_new_return_values (procedure, status, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_string_free (output, TRUE);
|
|
||||||
|
|
||||||
return gimp_procedure_new_return_values (procedure, status, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ install_data(
|
||||||
|
|
||||||
scripts_independent = [
|
scripts_independent = [
|
||||||
{ 'name': 'test-bytes' },
|
{ 'name': 'test-bytes' },
|
||||||
|
{ 'name': 'test-display' },
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach plugin : scripts_independent
|
foreach plugin : scripts_independent
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env gimp-script-fu-interpreter-3.0
|
||||||
|
|
||||||
|
; Calls scheme (display ...)
|
||||||
|
; Tests it prints to any stdout console where Gimp was started.
|
||||||
|
|
||||||
|
; Then calls (error ...)
|
||||||
|
; Tests Gimp declares an error.
|
||||||
|
|
||||||
|
; in v2, (display ...) did not go to the terminal,
|
||||||
|
; but was prepended to any error message,
|
||||||
|
; or disappeared when there was no error.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define (script-fu-test-display)
|
||||||
|
|
||||||
|
; test display function
|
||||||
|
|
||||||
|
; display shows a passed string
|
||||||
|
(display "foo")
|
||||||
|
|
||||||
|
; display shows repr of any atom or list
|
||||||
|
(display '(1 2 "bar"))
|
||||||
|
|
||||||
|
; print is same as display but adds newline
|
||||||
|
; shows repr of a function #<CLOSURE>
|
||||||
|
(print gimp-message)
|
||||||
|
|
||||||
|
(gimp-message "Called display: expect foo(1 2 bar)#<CLOSURE> in terminal")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; test error function
|
||||||
|
|
||||||
|
; Call to error yields:
|
||||||
|
; dialog when Gimp Error Console not open
|
||||||
|
; else text in the open Gimp Error Console
|
||||||
|
(gimp-message "Called error: expect Gimp dialog, OR error in Gimp Error Console.")
|
||||||
|
; Scheme objects print their representation.
|
||||||
|
; Here gimp-message should print as #<CLOSURE>
|
||||||
|
(error "Reason" gimp-message)
|
||||||
|
|
||||||
|
; Call to error returns to Gimp, this should not be reached.
|
||||||
|
(gimp-message "Test failed: unreachable statement was reached.")
|
||||||
|
)
|
||||||
|
|
||||||
|
(script-fu-register "script-fu-test-display"
|
||||||
|
"Test scheme display and error functions"
|
||||||
|
"Test (display ...) to console, and (error ...) returns err to Gimp"
|
||||||
|
"lkk"
|
||||||
|
"lkk"
|
||||||
|
"2024"
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
(script-fu-menu-register "script-fu-test-display"
|
||||||
|
"<Image>/Filters/Development/Demos")
|
|
@ -19,6 +19,7 @@
|
||||||
* Testing
|
* Testing
|
||||||
*
|
*
|
||||||
* Use a scriptfu server client such as https://github.com/vit1-irk/gimp-exec.
|
* Use a scriptfu server client such as https://github.com/vit1-irk/gimp-exec.
|
||||||
|
* OR servertest.py in the repo, >python2 servertest.py
|
||||||
*
|
*
|
||||||
* In a console, export G_MESSAGES_DEBUG=scriptfu (to see more logging from scriptfu)
|
* In a console, export G_MESSAGES_DEBUG=scriptfu (to see more logging from scriptfu)
|
||||||
* (script-fu-server does not use g_logging but rolls its own.)
|
* (script-fu-server does not use g_logging but rolls its own.)
|
||||||
|
@ -180,7 +181,7 @@ typedef union
|
||||||
static void server_start (const gchar *listen_ip,
|
static void server_start (const gchar *listen_ip,
|
||||||
gint port,
|
gint port,
|
||||||
const gchar *logfile);
|
const gchar *logfile);
|
||||||
static gboolean execute_command (SFCommand *cmd);
|
static void execute_command (SFCommand *cmd);
|
||||||
static gint read_from_client (gint filedes);
|
static gint read_from_client (gint filedes);
|
||||||
static gint make_socket (const struct addrinfo
|
static gint make_socket (const struct addrinfo
|
||||||
*ai);
|
*ai);
|
||||||
|
@ -563,7 +564,6 @@ server_start (const gchar *listen_ip,
|
||||||
{
|
{
|
||||||
SFCommand *cmd = (SFCommand *) command_queue->data;
|
SFCommand *cmd = (SFCommand *) command_queue->data;
|
||||||
|
|
||||||
/* Process the command */
|
|
||||||
execute_command (cmd);
|
execute_command (cmd);
|
||||||
|
|
||||||
/* Remove the command from the list */
|
/* Remove the command from the list */
|
||||||
|
@ -582,69 +582,113 @@ server_start (const gchar *listen_ip,
|
||||||
server_quit ();
|
server_quit ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interpret command on the ScriptFu interpreter.
|
||||||
|
* Returns whether the script had an error.
|
||||||
|
*
|
||||||
|
* Also creates a GString at the handle script_stdout.
|
||||||
|
* Ownership is transfered, and caller must free.
|
||||||
|
*
|
||||||
|
* The returned script_stdout is either:
|
||||||
|
* - when no error, what the script writes to stdout
|
||||||
|
* - an error message from the interpreter,
|
||||||
|
* or from the script calling Scheme:error or Scheme:quit
|
||||||
|
*
|
||||||
|
* Scheme scripts yield only the value of the final expression.
|
||||||
|
* The yielded value is NOT written to stdout.
|
||||||
|
* Scripts that are calls to Gimp PDB procedure never yield a useful value,
|
||||||
|
* since they are functions returning void.
|
||||||
|
* Most scripts are for their side effects,
|
||||||
|
* and not for what they write to stdout.
|
||||||
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
|
get_interpretation_result (SFCommand *cmd, GString **script_stdout)
|
||||||
|
{
|
||||||
|
gboolean is_script_error = FALSE;
|
||||||
|
|
||||||
|
*script_stdout = g_string_new (NULL);
|
||||||
|
|
||||||
|
script_fu_redirect_output_to_gstr (*script_stdout);
|
||||||
|
|
||||||
|
/* Returns non-zero on error. */
|
||||||
|
if (script_fu_interpret_string (cmd->command) != 0)
|
||||||
|
{
|
||||||
|
/* Substitute error message for output in script_stdout.
|
||||||
|
* What the script wrote to stdout before error is lost.
|
||||||
|
*/
|
||||||
|
g_string_assign (*script_stdout, script_fu_get_error_msg ());
|
||||||
|
is_script_error = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_script_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interpret command, then relay result to client.
|
||||||
|
* Side effect: log start, response, ending time.
|
||||||
|
*
|
||||||
|
* !!! Does not return a value indicating errors.
|
||||||
|
* Neither IO errors on the socket nor errors interpreting the script.
|
||||||
|
*
|
||||||
|
* Does write an error byte to the client indicating error interpreting script.
|
||||||
|
* On script error, substitutes an error msg for earlier output from the script.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
execute_command (SFCommand *cmd)
|
execute_command (SFCommand *cmd)
|
||||||
{
|
{
|
||||||
guchar buffer[RESPONSE_HEADER];
|
guchar buffer[RESPONSE_HEADER];
|
||||||
GString *response;
|
GString *response = NULL;
|
||||||
time_t clocknow;
|
time_t clocknow;
|
||||||
gboolean error;
|
|
||||||
gint i;
|
|
||||||
gdouble total_time;
|
gdouble total_time;
|
||||||
GTimer *timer;
|
GTimer *timer;
|
||||||
|
gboolean is_script_error;
|
||||||
|
|
||||||
server_log ("Processing request #%d\n", cmd->request_no);
|
server_log ("Processing request #%d\n", cmd->request_no);
|
||||||
|
|
||||||
timer = g_timer_new ();
|
timer = g_timer_new ();
|
||||||
|
|
||||||
response = g_string_new (NULL);
|
is_script_error = get_interpretation_result (cmd, &response);
|
||||||
script_fu_redirect_output_to_gstr (response);
|
/* Require interpretation set response to a valid GString. */
|
||||||
|
if (response == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
/* run the command */
|
server_log ("%s\n", response->str);
|
||||||
if (script_fu_interpret_string (cmd->command) != 0)
|
|
||||||
{
|
|
||||||
error = TRUE;
|
|
||||||
|
|
||||||
server_log ("%s\n", response->str);
|
total_time = g_timer_elapsed (timer, NULL);
|
||||||
}
|
time (&clocknow);
|
||||||
else
|
server_log ("Request #%d processed in %.3f seconds, finishing on %s",
|
||||||
{
|
cmd->request_no, total_time, ctime (&clocknow));
|
||||||
error = FALSE;
|
|
||||||
|
|
||||||
if (response->len == 0)
|
|
||||||
g_string_assign (response, script_fu_get_success_msg ());
|
|
||||||
|
|
||||||
total_time = g_timer_elapsed (timer, NULL);
|
|
||||||
time (&clocknow);
|
|
||||||
server_log ("Request #%d processed in %.3f seconds, finishing on %s",
|
|
||||||
cmd->request_no, total_time, ctime (&clocknow));
|
|
||||||
}
|
|
||||||
g_timer_destroy (timer);
|
g_timer_destroy (timer);
|
||||||
|
|
||||||
buffer[MAGIC_BYTE] = MAGIC;
|
buffer[MAGIC_BYTE] = MAGIC;
|
||||||
buffer[ERROR_BYTE] = error ? TRUE : FALSE;
|
buffer[ERROR_BYTE] = is_script_error ? TRUE : FALSE;
|
||||||
buffer[RSP_LEN_H_BYTE] = (guchar) (response->len >> 8);
|
buffer[RSP_LEN_H_BYTE] = (guchar) (response->len >> 8);
|
||||||
buffer[RSP_LEN_L_BYTE] = (guchar) (response->len & 0xFF);
|
buffer[RSP_LEN_L_BYTE] = (guchar) (response->len & 0xFF);
|
||||||
|
|
||||||
/* Write the response to the client */
|
/* Write a header to the client, as one message. */
|
||||||
for (i = 0; i < RESPONSE_HEADER; i++)
|
if (cmd->filedes > 0 &&
|
||||||
if (cmd->filedes > 0 && send (cmd->filedes, (const void *) (buffer + i), 1, 0) < 0)
|
send (cmd->filedes, (const void *) (buffer), RESPONSE_HEADER, 0) < 0)
|
||||||
{
|
{
|
||||||
/* Write error */
|
/* Write error */
|
||||||
print_socket_api_error ("send");
|
g_debug ("%s error sending header", G_STRFUNC);
|
||||||
return FALSE;
|
print_socket_api_error ("send");
|
||||||
}
|
g_string_free (response, TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < response->len; i++)
|
/* Write the script response to the client, as one message. */
|
||||||
if (cmd->filedes > 0 && send (cmd->filedes, response->str + i, 1, 0) < 0)
|
if (cmd->filedes > 0 &&
|
||||||
{
|
send (cmd->filedes, response->str, response->len, 0) < 0)
|
||||||
/* Write error */
|
{
|
||||||
print_socket_api_error ("send");
|
/* Write error. A client may have closed before taking all bytes. */
|
||||||
return FALSE;
|
g_debug ("%s error sending response", G_STRFUNC);
|
||||||
}
|
print_socket_api_error ("send");
|
||||||
|
g_string_free (response, TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_string_free (response, TRUE);
|
g_string_free (response, TRUE);
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
|
|
Loading…
Reference in New Issue