/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2009 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, see . */ #include #include "gth-script-task.h" struct _GthScriptTaskPrivate { GthScript *script; GtkWindow *parent; GList *file_list; GList *current; int n_files; int n_current; GPid pid; guint script_watch; }; G_DEFINE_TYPE_WITH_CODE (GthScriptTask, gth_script_task, GTH_TYPE_TASK, G_ADD_PRIVATE (GthScriptTask)) static void gth_script_task_finalize (GObject *object) { GthScriptTask *self; self = GTH_SCRIPT_TASK (object); g_object_unref (self->priv->script); _g_object_list_unref (self->priv->file_list); G_OBJECT_CLASS (gth_script_task_parent_class)->finalize (object); } static void _gth_script_task_exec (GthScriptTask *self); static void _gth_script_task_exec_next_file (GthScriptTask *self) { self->priv->current = self->priv->current->next; self->priv->n_current++; if (self->priv->current == NULL) gth_task_completed (GTH_TASK (self), NULL); else _gth_script_task_exec (self); } static void watch_script_cb (GPid pid, int status, gpointer data) { GthScriptTask *self = data; GError *error; g_spawn_close_pid (self->priv->pid); self->priv->pid = 0; self->priv->script_watch = 0; if (status != 0) { error = g_error_new (GTH_TASK_ERROR, GTH_TASK_ERROR_FAILED, _("Command exited abnormally with status %d"), status); gth_task_completed (GTH_TASK (self), error); return; } if (gth_script_for_each_file (self->priv->script)) _gth_script_task_exec_next_file (self); else gth_task_completed (GTH_TASK (self), NULL); } static void child_setup (gpointer user_data) { /* detach from the tty */ setsid (); /* create a process group to kill all the child processes when * canceling the operation. */ setpgid (0, 0); } static void get_command_line_ready_cb (GObject *source, GAsyncResult *result, gpointer user_data) { GthScriptTask *self = user_data; char *command_line; GError *error = NULL; gboolean retval = FALSE; command_line = gth_script_get_command_line_finish (GTH_SCRIPT (source), result, &error); if (command_line != NULL) { char **argv; int argc; if (gth_script_is_shell_script (self->priv->script)) { argv = g_new (char *, 4); argv[0] = "sh"; argv[1] = "-c"; argv[2] = command_line; argv[3] = NULL; } else g_shell_parse_argv (command_line, &argc, &argv, &error); if (error == NULL) { if (gth_script_wait_command (self->priv->script)) { if (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, child_setup, NULL, &self->priv->pid, &error)) { self->priv->script_watch = g_child_watch_add (self->priv->pid, watch_script_cb, self); retval = TRUE; } } else { if (g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) { retval = TRUE; } } } g_free (argv); } g_free (command_line); if (g_error_matches (error, GTH_TASK_ERROR, GTH_TASK_ERROR_SKIP_TO_NEXT_FILE)) { _gth_script_task_exec_next_file (self); return; } if (! retval) { gth_task_completed (GTH_TASK (self), error); return; } if (gth_script_wait_command (self->priv->script)) return; if (gth_script_for_each_file (self->priv->script)) { _gth_script_task_exec_next_file (self); return; } gth_task_completed (GTH_TASK (self), NULL); } static void get_command_line_dialog_cb (GtkWidget *dialog, gpointer user_data) { gth_task_dialog (GTH_TASK (user_data), (dialog != NULL), dialog); } static void _gth_script_task_exec (GthScriptTask *self) { if (gth_script_for_each_file (self->priv->script)) { GthFileData *file_data = self->priv->current->data; GList *list; gth_task_progress (GTH_TASK (self), gth_script_get_display_name (self->priv->script), g_file_info_get_display_name (file_data->info), FALSE, (double) self->priv->n_current / (self->priv->n_files + 1)); list = g_list_prepend (NULL, file_data); gth_script_get_command_line_async (self->priv->script, self->priv->parent, list, (self->priv->file_list->next != NULL), gth_task_get_cancellable (GTH_TASK (self)), get_command_line_dialog_cb, get_command_line_ready_cb, self); g_list_free (list); } else { gth_task_progress (GTH_TASK (self), gth_script_get_display_name (self->priv->script), NULL, TRUE, 0.0); gth_script_get_command_line_async (self->priv->script, self->priv->parent, self->priv->file_list, FALSE, gth_task_get_cancellable (GTH_TASK (self)), get_command_line_dialog_cb, get_command_line_ready_cb, self); } } static void file_info_ready_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GthScriptTask *self = user_data; GError *error = NULL; _g_query_metadata_finish (result, &error); if (error != NULL) { gth_task_completed (GTH_TASK (self), error); return; } _gth_script_task_exec (self); } static void gth_script_task_exec (GthTask *task) { GthScriptTask *self; char *attributes; g_return_if_fail (GTH_IS_SCRIPT_TASK (task)); self = GTH_SCRIPT_TASK (task); attributes = gth_script_get_requested_attributes (self->priv->script); if (attributes != NULL) { _g_query_metadata_async (self->priv->file_list, attributes, gth_task_get_cancellable (task), file_info_ready_cb, self); g_free (attributes); } else _gth_script_task_exec (self); } static void gth_script_task_cancelled (GthTask *task) { GthScriptTask *self; g_return_if_fail (GTH_IS_SCRIPT_TASK (task)); self = GTH_SCRIPT_TASK (task); if (self->priv->pid != 0) killpg (self->priv->pid, SIGTERM); } static void gth_script_task_class_init (GthScriptTaskClass *klass) { GObjectClass *object_class; GthTaskClass *task_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = gth_script_task_finalize; task_class = GTH_TASK_CLASS (klass); task_class->exec = gth_script_task_exec; task_class->cancelled = gth_script_task_cancelled; } static void gth_script_task_init (GthScriptTask *self) { self->priv = gth_script_task_get_instance_private (self); self->priv->pid = 0; } GthTask * gth_script_task_new (GtkWindow *parent, GthScript *script, GList *file_list) { GthScriptTask *self; self = GTH_SCRIPT_TASK (g_object_new (GTH_TYPE_SCRIPT_TASK, NULL)); self->priv->parent = parent; self->priv->script = g_object_ref (script); self->priv->file_list = _g_object_list_ref (file_list); self->priv->current = self->priv->file_list; self->priv->n_files = g_list_length (file_list); self->priv->n_current = 1; return (GthTask *) self; }