1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 Copyright (C) 2008 Sébastien Granjoux
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * Run and Stop program
23 *---------------------------------------------------------------------------*/
29 #include <libanjuta/anjuta-debug.h>
30 #include <libanjuta/interfaces/ianjuta-terminal.h>
31 #include <libanjuta/interfaces/ianjuta-builder.h>
33 #include <libgnomevfs/gnome-vfs-utils.h>
38 *---------------------------------------------------------------------------*/
40 #define PREF_USE_SB "build.use_scratchbox"
41 #define PREF_SB_PATH "build.scratchbox.path"
43 #define RUN_BUILD_TARGET_QUARK_STRING "RUN_PLUGIN_BUILD_TARGET"
45 /*----------------------------------------------------------------------------
49 struct _RunProgramChild
58 *---------------------------------------------------------------------------*/
61 get_local_executable (GtkWindow
*parent
, const gchar
*uri
)
63 const gchar
*err_msg
= NULL
;
68 local
= gnome_vfs_get_local_path_from_uri (uri
);
71 /* Only local program are supported */
72 err_msg
= _("Program '%s' is not a local file");
76 if (g_file_test (local
, G_FILE_TEST_EXISTS
) == FALSE
)
78 err_msg
= _("Program '%s' does not exists");
80 else if (g_file_test (local
, G_FILE_TEST_IS_EXECUTABLE
) == FALSE
)
82 err_msg
= _("Program '%s' does not have execution permission");
89 anjuta_util_dialog_error (parent
, err_msg
, local
== NULL
? uri
: local
);
98 get_local_directory (GtkWindow
*parent
, const gchar
*uri
)
100 const gchar
*err_msg
= NULL
;
105 local
= gnome_vfs_get_local_path_from_uri (uri
);
108 /* Only local directory are supported */
109 err_msg
= _("Program directory '%s' is not local");
115 anjuta_util_dialog_error (parent
, err_msg
, uri
);
122 *---------------------------------------------------------------------------*/
125 on_child_terminated (GPid pid
, gint status
, gpointer user_data
);
128 run_plugin_child_free (RunProgramPlugin
*plugin
, GPid pid
)
132 for (child
= g_list_first (plugin
->child
); child
!= NULL
; child
= g_list_next (child
))
134 if (((RunProgramChild
*)child
->data
)->pid
== pid
)
136 if (((RunProgramChild
*)child
->data
)->use_signal
)
138 g_return_if_fail (plugin
->child_exited_connection
> 0);
139 plugin
->child_exited_connection
--;
140 if (plugin
->child_exited_connection
== 0)
142 IAnjutaTerminal
*term
;
144 term
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
145 IAnjutaTerminal
, NULL
);
146 g_signal_handlers_disconnect_by_func (term
, on_child_terminated
, plugin
);
149 else if (((RunProgramChild
*)child
->data
)->source
)
151 g_source_remove (((RunProgramChild
*)child
->data
)->source
);
153 g_free (child
->data
);
154 plugin
->child
= g_list_delete_link (plugin
->child
, child
);
159 run_plugin_update_menu_sensitivity (plugin
);
163 on_child_terminated (GPid pid
, gint status
, gpointer user_data
)
165 RunProgramPlugin
*plugin
= (RunProgramPlugin
*)user_data
;
167 run_plugin_child_free (plugin
, pid
);
171 on_child_terminated_signal (IAnjutaTerminal
*term
, GPid pid
, gint status
, gpointer user_data
)
173 on_child_terminated (pid
, status
, user_data
);
177 execute_with_terminal (RunProgramPlugin
*plugin
,
178 const gchar
*dir
, const gchar
*cmd
, gchar
**env
)
180 IAnjutaTerminal
*term
;
183 term
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
184 IAnjutaTerminal
, NULL
);
187 gchar
* launcher_path
= g_find_program_in_path("anjuta_launcher");
189 RunProgramChild
*child
;
191 if (launcher_path
!= NULL
)
193 new_cmd
= g_strconcat ("anjuta_launcher ", cmd
, NULL
);
194 g_free (launcher_path
);
198 DEBUG_PRINT("Missing anjuta_launcher");
199 new_cmd
= g_strdup (cmd
);
202 if (plugin
->child_exited_connection
== 0)
204 g_signal_connect (term
, "child-exited", G_CALLBACK (on_child_terminated_signal
), plugin
);
206 plugin
->child_exited_connection
++;
207 child
= g_new0 (RunProgramChild
, 1);
208 child
->use_signal
= TRUE
;
209 plugin
->child
= g_list_prepend (plugin
->child
, child
);
211 pid
= ianjuta_terminal_execute_command (term
, dir
, new_cmd
, env
, NULL
);
220 execute_without_terminal (RunProgramPlugin
*plugin
,
221 const gchar
*dir
, gchar
*cmd
, gchar
**env
)
226 RunProgramChild
*child
;
233 /* Create environment variable array */
234 old_env
= g_listenv();
235 len
= old_env
? g_strv_length (old_env
) : 0;
236 len
+= env
? g_strv_length (env
) : 0;
238 new_env
= g_new (char *, len
);
240 /* Remove some environment variables, Move other in new_env */
242 for (p
= old_env
; *p
; p
++)
246 value
= g_getenv (*p
);
251 for (q
= env
; *q
; q
++)
256 if ((strlen (*q
) > len
+ 1) &&
257 (strncmp (*q
, *p
, len
) == 0) &&
260 value
= *q
+ len
+ 1;
266 new_env
[i
++] = g_strconcat (*p
, "=", value
, NULL
);
268 g_strfreev (old_env
);
270 /* Add new user variable */
273 for (p
= env
; *p
; p
++)
275 new_env
[i
++] = g_strdup (*p
);
280 /* Run user program using in a shell */
281 user_shell
= gnome_util_user_shell ();
282 argv
[0] = user_shell
;
287 child
= g_new0 (RunProgramChild
, 1);
288 plugin
->child
= g_list_prepend (plugin
->child
, child
);
290 if (g_spawn_async_with_pipes (dir
, argv
, new_env
, G_SPAWN_DO_NOT_REAP_CHILD
,
291 NULL
, NULL
, &pid
, NULL
, NULL
, NULL
, NULL
))
294 child
->source
= g_child_watch_add (pid
, on_child_terminated
, plugin
);
302 g_strfreev (new_env
);
308 run_program (RunProgramPlugin
*plugin
)
313 gchar
*dir_uri
= NULL
;
317 gboolean run_in_terminal
= 0;
318 AnjutaPreferences
*prefs
;
321 target
= get_local_executable (GTK_WINDOW (ANJUTA_PLUGIN (plugin
)->shell
),
323 g_free (plugin
->build_uri
);
324 plugin
->build_uri
= NULL
;
325 if (target
== NULL
) return FALSE
;
327 /* Get directory from shell */
328 anjuta_shell_get (ANJUTA_PLUGIN (plugin
)->shell
,
329 RUN_PROGRAM_DIR
, G_TYPE_STRING
, &dir_uri
,
333 dir
= get_local_directory (GTK_WINDOW (ANJUTA_PLUGIN (plugin
)->shell
),
336 if (dir
== NULL
) return FALSE
;
340 dir
= g_path_get_dirname (target
);
343 /* Get other parameters from shell */
344 anjuta_shell_get (ANJUTA_PLUGIN (plugin
)->shell
,
345 RUN_PROGRAM_ARGS
, G_TYPE_STRING
, &args
,
346 RUN_PROGRAM_ENV
, G_TYPE_STRV
, &env
,
347 RUN_PROGRAM_NEED_TERM
, G_TYPE_BOOLEAN
, &run_in_terminal
,
350 /* Quote target name */
351 quote_target
= g_shell_quote (target
);
354 if (args
&& strlen (args
) > 0)
355 cmd
= g_strconcat (quote_target
, " ", args
, NULL
);
357 cmd
= g_strdup (quote_target
);
359 g_free (quote_target
);
361 /* Take care of scratchbox */
362 prefs
= anjuta_shell_get_preferences (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
363 if (anjuta_preferences_get_int (prefs
, PREF_USE_SB
))
365 const gchar
* sb_path
= anjuta_preferences_get(prefs
, PREF_SB_PATH
);
366 /* we need to skip the /scratchbox/users part, maybe could be done more clever */
367 const gchar
* real_dir
= strstr(dir
, "/home");
371 cmd
= g_strdup_printf("%s/login -d %s \"%s\"", sb_path
,
374 dir
= g_strdup(real_dir
);
380 pid
= execute_with_terminal (plugin
, dir
, cmd
, env
);
383 pid
= execute_without_terminal (plugin
, dir
, cmd
, env
);
388 pid
= execute_without_terminal (plugin
, dir
, cmd
, env
);
393 anjuta_util_dialog_error (GTK_WINDOW (ANJUTA_PLUGIN (plugin
)->shell
),
394 "Unable to execute %s", cmd
);
396 run_plugin_update_menu_sensitivity (plugin
);
406 on_build_finished (IAnjutaBuilder
*builder
, GError
*err
, gpointer user_data
)
408 RunProgramPlugin
*plugin
= (RunProgramPlugin
*)user_data
;
410 g_signal_handler_disconnect (builder
, plugin
->build_handle
);
414 /* Up to date, run program */
415 run_program (plugin
);
419 g_free (plugin
->build_uri
);
420 plugin
->build_uri
= NULL
;
425 on_is_built_finished (IAnjutaBuilder
*builder
, GError
*err
, gpointer user_data
)
427 RunProgramPlugin
*plugin
= (RunProgramPlugin
*)user_data
;
429 g_signal_handler_disconnect (builder
, plugin
->build_handle
);
433 /* Up to date, run program */
434 run_program (plugin
);
436 else if (err
->code
== IANJUTA_BUILDER_FAILED
)
438 /* Target is not up to date */
439 plugin
->build_handle
= g_signal_connect (builder
, "command-finished::" RUN_BUILD_TARGET_QUARK_STRING
, G_CALLBACK (on_build_finished
), plugin
);
442 ianjuta_builder_build (builder
, plugin
->build_uri
, plugin
->build_id
, NULL
);
446 g_free (plugin
->build_uri
);
447 plugin
->build_uri
= NULL
;
452 check_target (RunProgramPlugin
*plugin
)
454 IAnjutaBuilder
*builder
;
457 anjuta_shell_get (ANJUTA_PLUGIN (plugin
)->shell
,
458 RUN_PROGRAM_URI
, G_TYPE_STRING
, &prog_uri
, NULL
);
460 builder
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
, IAnjutaBuilder
, NULL
);
463 if (plugin
->build_uri
)
465 /* a build operation is currently running */
466 if (strcmp (plugin
->build_uri
, prog_uri
) == 0)
468 /* It is the same one, just ignore */
473 /* Cancel old operation */
474 ianjuta_builder_cancel (builder
, plugin
->build_id
, NULL
);
478 plugin
->build_uri
= prog_uri
;
479 if (plugin
->build_id
== 0)
480 plugin
->build_id
= g_quark_from_static_string(RUN_BUILD_TARGET_QUARK_STRING
);
482 plugin
->build_handle
= g_signal_connect (builder
, "command-finished::" RUN_BUILD_TARGET_QUARK_STRING
, G_CALLBACK (on_is_built_finished
), plugin
);
484 /* Check if target is up to date */
485 return ianjuta_builder_is_built (builder
, plugin
->build_uri
, plugin
->build_id
, NULL
);
489 plugin
->build_uri
= prog_uri
;
491 /* Unable to build target, just run it */
492 return run_program (plugin
);
497 *---------------------------------------------------------------------------*/
500 run_plugin_run_program (RunProgramPlugin
*plugin
)
502 /* Check if target is up to date */
503 return check_target (plugin
);
507 run_plugin_kill_program (RunProgramPlugin
*plugin
, gboolean terminate
)
509 if (plugin
->child
!= NULL
)
511 RunProgramChild
*child
= (RunProgramChild
*)plugin
->child
->data
;
513 if (!child
->terminated
&& terminate
)
515 kill (child
->pid
, SIGTERM
);
516 child
->terminated
= TRUE
;
520 kill (child
->pid
, SIGKILL
);
521 run_plugin_child_free (plugin
, child
->pid
);
529 run_free_all_children (RunProgramPlugin
*plugin
)
532 IAnjutaTerminal
*term
;
534 /* Remove terminal child-exited handle */
535 term
= anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin
)->shell
,
536 IAnjutaTerminal
, NULL
);
537 g_signal_handlers_disconnect_by_func (term
, on_child_terminated
, plugin
);
538 plugin
->child_exited_connection
= 0;
540 /* Remove all child-exited source */
541 for (child
= g_list_first (plugin
->child
); child
!= NULL
; child
= g_list_next (child
))
543 if (!((RunProgramChild
*)child
->data
)->use_signal
)
545 g_source_remove (((RunProgramChild
*)child
->data
)->source
);
547 g_free (child
->data
);
549 g_list_free (plugin
->child
);
550 plugin
->child
= NULL
;