summaryrefslogtreecommitdiff
path: root/src/fr-process.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fr-process.c')
-rw-r--r--src/fr-process.c1028
1 files changed, 1028 insertions, 0 deletions
diff --git a/src/fr-process.c b/src/fr-process.c
new file mode 100644
index 0000000..4750f59
--- /dev/null
+++ b/src/fr-process.c
@@ -0,0 +1,1028 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2008 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <glib.h>
+#include "fr-process.h"
+#include "fr-marshal.h"
+#include "glib-utils.h"
+
+#define REFRESH_RATE 20
+#define BUFFER_SIZE 16384
+
+enum {
+ START,
+ DONE,
+ STICKY_ONLY,
+ LAST_SIGNAL
+};
+
+static GObjectClass *parent_class;
+static guint fr_process_signals[LAST_SIGNAL] = { 0 };
+
+static void fr_process_class_init (FrProcessClass *class);
+static void fr_process_init (FrProcess *process);
+static void fr_process_finalize (GObject *object);
+
+
+typedef struct {
+ GList *args; /* command to execute */
+ char *dir; /* working directory */
+ guint sticky : 1; /* whether the command must be
+ * executed even if a previous
+ * command has failed. */
+ guint ignore_error : 1; /* whether to continue to execute
+ * other commands if this command
+ * fails. */
+ ContinueFunc continue_func;
+ gpointer continue_data;
+ ProcFunc begin_func;
+ gpointer begin_data;
+ ProcFunc end_func;
+ gpointer end_data;
+} FrCommandInfo;
+
+
+static FrCommandInfo *
+fr_command_info_new (void)
+{
+ FrCommandInfo *info;
+
+ info = g_new0 (FrCommandInfo, 1);
+ info->args = NULL;
+ info->dir = NULL;
+ info->sticky = FALSE;
+ info->ignore_error = FALSE;
+
+ return info;
+}
+
+
+static void
+fr_command_info_free (FrCommandInfo *info)
+{
+ if (info == NULL)
+ return;
+
+ if (info->args != NULL) {
+ g_list_foreach (info->args, (GFunc) g_free, NULL);
+ g_list_free (info->args);
+ info->args = NULL;
+ }
+
+ if (info->dir != NULL) {
+ g_free (info->dir);
+ info->dir = NULL;
+ }
+
+ g_free (info);
+}
+
+
+static void
+fr_channel_data_init (FrChannelData *channel)
+{
+ channel->source = NULL;
+ channel->raw = NULL;
+ channel->status = G_IO_STATUS_NORMAL;
+ channel->error = NULL;
+}
+
+
+static void
+fr_channel_data_close_source (FrChannelData *channel)
+{
+ if (channel->source != NULL) {
+ g_io_channel_shutdown (channel->source, FALSE, NULL);
+ g_io_channel_unref (channel->source);
+ channel->source = NULL;
+ }
+}
+
+
+static GIOStatus
+fr_channel_data_read (FrChannelData *channel)
+{
+ char *line;
+ gsize length;
+ gsize terminator_pos;
+
+ channel->status = G_IO_STATUS_NORMAL;
+ g_clear_error (&channel->error);
+
+ while ((channel->status = g_io_channel_read_line (channel->source,
+ &line,
+ &length,
+ &terminator_pos,
+ &channel->error)) == G_IO_STATUS_NORMAL)
+ {
+ line[terminator_pos] = 0;
+ channel->raw = g_list_prepend (channel->raw, line);
+ if (channel->line_func != NULL)
+ (*channel->line_func) (line, channel->line_data);
+ }
+
+ return channel->status;
+}
+
+
+static GIOStatus
+fr_channel_data_flush (FrChannelData *channel)
+{
+ GIOStatus status;
+
+ while (((status = fr_channel_data_read (channel)) != G_IO_STATUS_ERROR) && (status != G_IO_STATUS_EOF))
+ /* void */;
+ fr_channel_data_close_source (channel);
+
+ return status;
+}
+
+
+static void
+fr_channel_data_reset (FrChannelData *channel)
+{
+ fr_channel_data_close_source (channel);
+
+ if (channel->raw != NULL) {
+ g_list_foreach (channel->raw, (GFunc) g_free, NULL);
+ g_list_free (channel->raw);
+ channel->raw = NULL;
+ }
+}
+
+
+static void
+fr_channel_data_free (FrChannelData *channel)
+{
+ fr_channel_data_reset (channel);
+}
+
+
+static void
+fr_channel_data_set_fd (FrChannelData *channel,
+ int fd,
+ const char *charset)
+{
+ fr_channel_data_reset (channel);
+
+ channel->source = g_io_channel_unix_new (fd);
+ g_io_channel_set_flags (channel->source, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_buffer_size (channel->source, BUFFER_SIZE);
+ if (charset != NULL)
+ g_io_channel_set_encoding (channel->source, charset, NULL);
+}
+
+
+const char *try_charsets[] = { "UTF-8", "ISO-8859-1", "WINDOW-1252" };
+int n_charsets = G_N_ELEMENTS (try_charsets);
+
+
+struct _FrProcessPrivate {
+ GPtrArray *comm; /* FrCommandInfo elements. */
+ gint n_comm; /* total number of commands */
+ gint current_comm; /* currenlty editing command. */
+
+ GPid command_pid;
+ guint check_timeout;
+
+ FrProcError first_error;
+
+ gboolean running;
+ gboolean stopping;
+ gint current_command;
+ gint error_command; /* command that coused an error. */
+
+ gboolean use_standard_locale;
+ gboolean sticky_only; /* whether to execute only sticky
+ * commands. */
+ int current_charset;
+};
+
+
+GType
+fr_process_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrProcessClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_process_class_init,
+ NULL,
+ NULL,
+ sizeof (FrProcess),
+ 0,
+ (GInstanceInitFunc) fr_process_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "FRProcess",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+fr_process_class_init (FrProcessClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ fr_process_signals[START] =
+ g_signal_new ("start",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, start),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ fr_process_signals[DONE] =
+ g_signal_new ("done",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, done),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ fr_process_signals[STICKY_ONLY] =
+ g_signal_new ("sticky_only",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, sticky_only),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gobject_class->finalize = fr_process_finalize;
+
+ class->start = NULL;
+ class->done = NULL;
+}
+
+
+static void
+fr_process_init (FrProcess *process)
+{
+ process->priv = g_new0 (FrProcessPrivate, 1);
+
+ process->term_on_stop = TRUE;
+
+ process->priv->comm = g_ptr_array_new ();
+ process->priv->n_comm = -1;
+ process->priv->current_comm = -1;
+
+ process->priv->command_pid = 0;
+ fr_channel_data_init (&process->out);
+ fr_channel_data_init (&process->err);
+
+ process->error.gerror = NULL;
+ process->priv->first_error.gerror = NULL;
+
+ process->priv->check_timeout = 0;
+ process->priv->running = FALSE;
+ process->priv->stopping = FALSE;
+ process->restart = FALSE;
+
+ process->priv->current_charset = -1;
+
+ process->priv->use_standard_locale = FALSE;
+}
+
+
+FrProcess *
+fr_process_new (void)
+{
+ return FR_PROCESS (g_object_new (FR_TYPE_PROCESS, NULL));
+}
+
+
+static void fr_process_stop_priv (FrProcess *process, gboolean emit_signal);
+
+
+static void
+fr_process_finalize (GObject *object)
+{
+ FrProcess *process;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_PROCESS (object));
+
+ process = FR_PROCESS (object);
+
+ fr_process_stop_priv (process, FALSE);
+ fr_process_clear (process);
+
+ g_ptr_array_free (process->priv->comm, FALSE);
+
+ fr_channel_data_free (&process->out);
+ fr_channel_data_free (&process->err);
+
+ g_clear_error (&process->error.gerror);
+ g_clear_error (&process->priv->first_error.gerror);
+
+ g_free (process->priv);
+
+ /* Chain up */
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+void
+fr_process_begin_command (FrProcess *process,
+ const char *arg)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = fr_command_info_new ();
+ info->args = g_list_prepend (NULL, g_strdup (arg));
+
+ g_ptr_array_add (process->priv->comm, info);
+
+ process->priv->n_comm++;
+ process->priv->current_comm = process->priv->n_comm;
+}
+
+
+void
+fr_process_begin_command_at (FrProcess *process,
+ const char *arg,
+ int index)
+{
+ FrCommandInfo *info, *old_c_info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (index >= 0 && index <= process->priv->n_comm);
+
+ process->priv->current_comm = index;
+
+ old_c_info = g_ptr_array_index (process->priv->comm, index);
+
+ if (old_c_info != NULL)
+ fr_command_info_free (old_c_info);
+
+ info = fr_command_info_new ();
+ info->args = g_list_prepend (NULL, g_strdup (arg));
+
+ g_ptr_array_index (process->priv->comm, index) = info;
+}
+
+
+void
+fr_process_set_working_dir (FrProcess *process,
+ const char *dir)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ if (info->dir != NULL)
+ g_free (info->dir);
+ info->dir = g_strdup (dir);
+}
+
+
+void
+fr_process_set_sticky (FrProcess *process,
+ gboolean sticky)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->sticky = sticky;
+}
+
+
+void
+fr_process_set_ignore_error (FrProcess *process,
+ gboolean ignore_error)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->ignore_error = ignore_error;
+}
+
+
+void
+fr_process_add_arg (FrProcess *process,
+ const char *arg)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->args = g_list_prepend (info->args, g_strdup (arg));
+}
+
+
+void
+fr_process_add_arg_concat (FrProcess *process,
+ const char *arg1,
+ ...)
+{
+ GString *arg;
+ va_list args;
+ char *s;
+
+ arg = g_string_new (arg1);
+
+ va_start (args, arg1);
+ while ((s = va_arg (args, char*)) != NULL)
+ g_string_append (arg, s);
+ va_end (args);
+
+ fr_process_add_arg (process, arg->str);
+ g_string_free (arg, TRUE);
+}
+
+
+void
+fr_process_add_arg_printf (FrProcess *fr_proc,
+ const char *format,
+ ...)
+{
+ va_list args;
+ char *arg;
+
+ va_start (args, format);
+ arg = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ fr_process_add_arg (fr_proc, arg);
+
+ g_free (arg);
+}
+
+
+void
+fr_process_set_arg_at (FrProcess *process,
+ int n_comm,
+ int n_arg,
+ const char *arg_value)
+{
+ FrCommandInfo *info;
+ GList *arg;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, n_comm);
+ arg = g_list_nth (info->args, n_arg);
+ g_return_if_fail (arg != NULL);
+
+ g_free (arg->data);
+ arg->data = g_strdup (arg_value);
+}
+
+
+void
+fr_process_set_begin_func (FrProcess *process,
+ ProcFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->begin_func = func;
+ info->begin_data = func_data;
+}
+
+
+void
+fr_process_set_end_func (FrProcess *process,
+ ProcFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->end_func = func;
+ info->end_data = func_data;
+}
+
+
+void
+fr_process_set_continue_func (FrProcess *process,
+ ContinueFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ if (process->priv->current_comm < 0)
+ return;
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->continue_func = func;
+ info->continue_data = func_data;
+}
+
+
+void
+fr_process_end_command (FrProcess *process)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->args = g_list_reverse (info->args);
+}
+
+
+void
+fr_process_clear (FrProcess *process)
+{
+ gint i;
+
+ g_return_if_fail (process != NULL);
+
+ for (i = 0; i <= process->priv->n_comm; i++) {
+ FrCommandInfo *info;
+
+ info = g_ptr_array_index (process->priv->comm, i);
+ fr_command_info_free (info);
+ g_ptr_array_index (process->priv->comm, i) = NULL;
+ }
+
+ for (i = 0; i <= process->priv->n_comm; i++)
+ g_ptr_array_remove_index_fast (process->priv->comm, 0);
+
+ process->priv->n_comm = -1;
+ process->priv->current_comm = -1;
+}
+
+
+void
+fr_process_set_out_line_func (FrProcess *process,
+ LineFunc func,
+ gpointer data)
+{
+ g_return_if_fail (process != NULL);
+
+ process->out.line_func = func;
+ process->out.line_data = data;
+}
+
+
+void
+fr_process_set_err_line_func (FrProcess *process,
+ LineFunc func,
+ gpointer data)
+{
+ g_return_if_fail (process != NULL);
+
+ process->err.line_func = func;
+ process->err.line_data = data;
+}
+
+
+static gboolean check_child (gpointer data);
+
+
+static void
+child_setup (gpointer user_data)
+{
+ FrProcess *process = user_data;
+
+ if (process->priv->use_standard_locale)
+ putenv ("LC_MESSAGES=C");
+
+ /* detach from the tty */
+
+ setsid ();
+}
+
+
+static const char *
+fr_process_get_charset (FrProcess *process)
+{
+ const char *charset = NULL;
+
+ if (process->priv->current_charset >= 0)
+ charset = try_charsets[process->priv->current_charset];
+ else if (g_get_charset (&charset))
+ charset = NULL;
+
+ return charset;
+}
+
+
+static void
+start_current_command (FrProcess *process)
+{
+ FrCommandInfo *info;
+ GList *scan;
+ char **argv;
+ int out_fd, err_fd;
+ int i = 0;
+
+ debug (DEBUG_INFO, "%d/%d) ", process->priv->current_command, process->priv->n_comm);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
+
+ argv = g_new (char *, g_list_length (info->args) + 1);
+ for (scan = info->args; scan; scan = scan->next)
+ argv[i++] = scan->data;
+ argv[i] = NULL;
+
+#ifdef DEBUG
+ {
+ int j;
+
+ if (process->priv->use_standard_locale)
+ g_print ("\tLC_MESSAGES=C\n");
+
+ if (info->dir != NULL)
+ g_print ("\tcd %s\n", info->dir);
+
+ g_print ("\t");
+ for (j = 0; j < i; j++)
+ g_print ("%s ", argv[j]);
+ g_print ("\n");
+ }
+#endif
+
+ if (info->begin_func != NULL)
+ (*info->begin_func) (info->begin_data);
+
+ if (! g_spawn_async_with_pipes (info->dir,
+ argv,
+ NULL,
+ (G_SPAWN_LEAVE_DESCRIPTORS_OPEN
+ | G_SPAWN_SEARCH_PATH
+ | G_SPAWN_DO_NOT_REAP_CHILD),
+ child_setup,
+ process,
+ &process->priv->command_pid,
+ NULL,
+ &out_fd,
+ &err_fd,
+ &process->error.gerror))
+ {
+ process->error.type = FR_PROC_ERROR_SPAWN;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ g_free (argv);
+ return;
+ }
+
+ g_free (argv);
+
+ fr_channel_data_set_fd (&process->out, out_fd, fr_process_get_charset (process));
+ fr_channel_data_set_fd (&process->err, err_fd, fr_process_get_charset (process));
+
+ process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
+ check_child,
+ process);
+}
+
+
+static gboolean
+command_is_sticky (FrProcess *process,
+ int i)
+{
+ FrCommandInfo *info;
+
+ info = g_ptr_array_index (process->priv->comm, i);
+ return info->sticky;
+}
+
+
+static void
+allow_sticky_processes_only (FrProcess *process,
+ gboolean emit_signal)
+{
+ if (! process->priv->sticky_only) {
+ /* Remember the first error. */
+ process->priv->error_command = process->priv->current_command;
+ process->priv->first_error.type = process->error.type;
+ process->priv->first_error.status = process->error.status;
+ g_clear_error (&process->priv->first_error.gerror);
+ if (process->error.gerror != NULL)
+ process->priv->first_error.gerror = g_error_copy (process->error.gerror);
+ }
+
+ process->priv->sticky_only = TRUE;
+ if (emit_signal)
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[STICKY_ONLY],
+ 0);
+}
+
+
+static void
+fr_process_set_error (FrProcess *process,
+ FrProcErrorType type,
+ int status,
+ GError *gerror)
+{
+ process->error.type = type;
+ process->error.status = status;
+ if (gerror != process->error.gerror) {
+ g_clear_error (&process->error.gerror);
+ if (gerror != NULL)
+ process->error.gerror = g_error_copy (gerror);
+ }
+}
+
+
+static gint
+check_child (gpointer data)
+{
+ FrProcess *process = data;
+ FrCommandInfo *info;
+ pid_t pid;
+ int status;
+ gboolean continue_process;
+ gboolean channel_error = FALSE;
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
+
+ /* Remove check. */
+
+ g_source_remove (process->priv->check_timeout);
+ process->priv->check_timeout = 0;
+
+ if (fr_channel_data_read (&process->out) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error);
+ channel_error = TRUE;
+ }
+ else if (fr_channel_data_read (&process->err) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error);
+ channel_error = TRUE;
+ }
+ else {
+ pid = waitpid (process->priv->command_pid, &status, WNOHANG);
+ if (pid != process->priv->command_pid) {
+ /* Add check again. */
+ process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
+ check_child,
+ process);
+ return FALSE;
+ }
+ }
+
+ if (info->ignore_error) {
+ process->error.type = FR_PROC_ERROR_NONE;
+ debug (DEBUG_INFO, "[ignore error]\n");
+ }
+ else if (! channel_error && (process->error.type != FR_PROC_ERROR_STOPPED)) {
+ if (WIFEXITED (status)) {
+ if (WEXITSTATUS (status) == 0)
+ process->error.type = FR_PROC_ERROR_NONE;
+ else if (WEXITSTATUS (status) == 255)
+ process->error.type = FR_PROC_ERROR_COMMAND_NOT_FOUND;
+ else {
+ process->error.type = FR_PROC_ERROR_COMMAND_ERROR;
+ process->error.status = WEXITSTATUS (status);
+ }
+ }
+ else {
+ process->error.type = FR_PROC_ERROR_EXITED_ABNORMALLY;
+ process->error.status = 255;
+ }
+ }
+
+ process->priv->command_pid = 0;
+
+ if (fr_channel_data_flush (&process->out) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error);
+ channel_error = TRUE;
+ }
+ else if (fr_channel_data_flush (&process->err) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error);
+ channel_error = TRUE;
+ }
+
+ if (info->end_func != NULL)
+ (*info->end_func) (info->end_data);
+
+ /**/
+
+ if (channel_error
+ && (process->error.type == FR_PROC_ERROR_IO_CHANNEL)
+ && g_error_matches (process->error.gerror, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+ {
+ if (process->priv->current_charset < n_charsets - 1) {
+ /* try with another charset */
+ process->priv->current_charset++;
+ process->priv->running = FALSE;
+ process->restart = TRUE;
+ fr_process_start (process);
+ return FALSE;
+ }
+ /*fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL);*/
+ fr_process_set_error (process, FR_PROC_ERROR_BAD_CHARSET, 0, process->error.gerror);
+ }
+
+ /* Check whether to continue or stop the process */
+
+ continue_process = TRUE;
+ if (info->continue_func != NULL)
+ continue_process = (*info->continue_func) (info->continue_data);
+
+ /* Execute next command. */
+ if (continue_process) {
+ if (process->error.type != FR_PROC_ERROR_NONE) {
+ allow_sticky_processes_only (process, TRUE);
+#ifdef DEBUG
+ {
+ GList *scan;
+
+ g_print ("** ERROR **\n");
+ for (scan = process->err.raw; scan; scan = scan->next)
+ g_print ("%s\n", (char *)scan->data);
+ }
+#endif
+ }
+
+ if (process->priv->sticky_only) {
+ do {
+ process->priv->current_command++;
+ } while ((process->priv->current_command <= process->priv->n_comm)
+ && ! command_is_sticky (process, process->priv->current_command));
+ }
+ else
+ process->priv->current_command++;
+
+ if (process->priv->current_command <= process->priv->n_comm) {
+ start_current_command (process);
+ return FALSE;
+ }
+ }
+
+ /* Done */
+
+ process->priv->current_command = -1;
+ process->priv->use_standard_locale = FALSE;
+
+ if (process->out.raw != NULL)
+ process->out.raw = g_list_reverse (process->out.raw);
+ if (process->err.raw != NULL)
+ process->err.raw = g_list_reverse (process->err.raw);
+
+ process->priv->running = FALSE;
+ process->priv->stopping = FALSE;
+
+ if (process->priv->sticky_only) {
+ /* Restore the first error. */
+ fr_process_set_error (process,
+ process->priv->first_error.type,
+ process->priv->first_error.status,
+ process->priv->first_error.gerror);
+ }
+
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+
+ return FALSE;
+}
+
+
+void
+fr_process_use_standard_locale (FrProcess *process,
+ gboolean use_stand_locale)
+{
+ g_return_if_fail (process != NULL);
+ process->priv->use_standard_locale = use_stand_locale;
+}
+
+
+void
+fr_process_start (FrProcess *process)
+{
+ g_return_if_fail (process != NULL);
+
+ if (process->priv->running)
+ return;
+
+ fr_channel_data_reset (&process->out);
+ fr_channel_data_reset (&process->err);
+
+ process->priv->sticky_only = FALSE;
+ process->priv->current_command = 0;
+ fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL);
+
+ if (! process->restart) {
+ process->priv->current_charset = -1;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[START],
+ 0);
+ }
+
+ process->priv->stopping = FALSE;
+
+ if (process->priv->n_comm == -1) {
+ process->priv->running = FALSE;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ }
+ else {
+ process->priv->running = TRUE;
+ start_current_command (process);
+ }
+}
+
+
+static void
+fr_process_stop_priv (FrProcess *process,
+ gboolean emit_signal)
+{
+ g_return_if_fail (process != NULL);
+
+ if (! process->priv->running)
+ return;
+
+ if (process->priv->stopping)
+ return;
+
+ process->priv->stopping = TRUE;
+ process->error.type = FR_PROC_ERROR_STOPPED;
+
+ if (command_is_sticky (process, process->priv->current_command))
+ allow_sticky_processes_only (process, emit_signal);
+
+ else if (process->term_on_stop && (process->priv->command_pid > 0))
+ kill (process->priv->command_pid, SIGTERM);
+
+ else {
+ if (process->priv->check_timeout != 0) {
+ g_source_remove (process->priv->check_timeout);
+ process->priv->check_timeout = 0;
+ }
+
+ process->priv->command_pid = 0;
+ fr_channel_data_close_source (&process->out);
+ fr_channel_data_close_source (&process->err);
+
+ process->priv->running = FALSE;
+
+ if (emit_signal)
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ }
+}
+
+
+void
+fr_process_stop (FrProcess *process)
+{
+ fr_process_stop_priv (process, TRUE);
+}