* plugins/run-program/execute.c:
[anjuta-git-plugin.git] / plugins / run-program / execute.c
blobdb57ff9e97baf390922ee321276aff92ba5bc44d
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 execute.c
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 *---------------------------------------------------------------------------*/
25 #include <config.h>
27 #include "execute.h"
29 #include <libanjuta/anjuta-debug.h>
30 #include <libanjuta/interfaces/ianjuta-terminal.h>
32 #include <libgnomevfs/gnome-vfs-utils.h>
34 #include <signal.h>
36 /* Constants
37 *---------------------------------------------------------------------------*/
39 #define PREF_USE_SB "build.use_scratchbox"
40 #define PREF_SB_PATH "build.scratchbox.path"
43 /*----------------------------------------------------------------------------
44 * Type definitions
47 struct _RunProgramChild
49 GPid pid;
50 guint source;
51 gboolean use_signal;
52 gboolean terminated;
55 /* Private functions
56 *---------------------------------------------------------------------------*/
58 static void
59 on_child_terminated (GPid pid, gint status, gpointer user_data);
61 static void
62 run_plugin_child_free (RunProgramPlugin *plugin, GPid pid)
64 GList *child;
66 for (child = g_list_first (plugin->child); child != NULL; child = g_list_next (child))
68 if (((RunProgramChild *)child->data)->pid == pid)
70 if (((RunProgramChild *)child->data)->use_signal)
72 g_return_if_fail (plugin->child_exited_connection > 0);
73 plugin->child_exited_connection--;
74 if (plugin->child_exited_connection == 0)
76 IAnjutaTerminal *term;
78 term = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
79 IAnjutaTerminal, NULL);
80 g_signal_handlers_disconnect_by_func (term, on_child_terminated, plugin);
83 else if (((RunProgramChild *)child->data)->source)
85 g_source_remove (((RunProgramChild *)child->data)->source);
87 g_free (child->data);
88 plugin->child = g_list_delete_link (plugin->child, child);
89 break;
93 run_plugin_update_menu_sensitivity (plugin);
96 static void
97 on_child_terminated (GPid pid, gint status, gpointer user_data)
99 RunProgramPlugin *plugin = (RunProgramPlugin *)user_data;
101 run_plugin_child_free (plugin, pid);
105 static void
106 on_child_terminated_signal (IAnjutaTerminal *term, GPid pid, gint status, gpointer user_data)
108 on_child_terminated (pid, status, user_data);
111 static gboolean
112 get_local_executable_and_directory (RunProgramPlugin *plugin, gchar **local, gchar **dir)
114 gchar *prog_uri = NULL;
115 gchar *dir_uri = NULL;
116 const gchar *err_msg = NULL;
117 const gchar *err_target = NULL;
119 anjuta_shell_get (ANJUTA_PLUGIN (plugin)->shell,
120 RUN_PROGRAM_DIR, G_TYPE_STRING, &dir_uri,
121 RUN_PROGRAM_URI, G_TYPE_STRING, &prog_uri, NULL);
122 *local = NULL;
123 *dir = NULL;
125 if (prog_uri == NULL)
127 err_msg = ""; /* No error message, just do nothing */
129 else if ((*local = gnome_vfs_get_local_path_from_uri (prog_uri)) == NULL)
131 /* Only local program are supported */
132 err_msg = _("Program '%s' is not a local file");
133 err_target = prog_uri;
135 else if ((dir_uri != NULL) && ((*dir = gnome_vfs_get_local_path_from_uri (dir_uri)) == NULL))
137 /* Only local directory are supported */
138 err_msg = _("Program directory '%s' is not local");
139 err_target = dir_uri;
141 else
143 if (g_file_test (*local, G_FILE_TEST_EXISTS) == FALSE)
145 err_msg = _("Program '%s' does not exists");
147 else if (g_file_test (*local, G_FILE_TEST_IS_EXECUTABLE) == FALSE)
149 err_msg = _("Program '%s' does not have execution permission");
151 err_target = dir_uri;
154 if (err_msg && (*err_msg != '\0'))
156 anjuta_util_dialog_error (GTK_WINDOW (ANJUTA_PLUGIN (plugin)->shell), err_msg, err_target);
158 g_free (prog_uri);
159 g_free (dir_uri);
160 if (err_msg != NULL)
162 g_free (*local);
163 *local = NULL;
164 g_free (*dir);
165 *dir = NULL;
168 return err_msg == NULL;
171 static GPid
172 execute_with_terminal (RunProgramPlugin *plugin,
173 const gchar *dir, const gchar *cmd, gchar **env)
175 IAnjutaTerminal *term;
176 GPid pid = 0;
178 term = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
179 IAnjutaTerminal, NULL);
180 if (term)
182 gchar* launcher_path = g_find_program_in_path("anjuta_launcher");
183 gchar *new_cmd;
184 RunProgramChild *child;
186 if (launcher_path != NULL)
188 new_cmd = g_strconcat ("anjuta_launcher ", cmd, NULL);
189 g_free (launcher_path);
191 else
193 DEBUG_PRINT("Missing anjuta_launcher");
194 new_cmd = g_strdup (cmd);
197 if (plugin->child_exited_connection == 0)
199 g_signal_connect (term, "child-exited", G_CALLBACK (on_child_terminated_signal), plugin);
201 plugin->child_exited_connection++;
202 child = g_new0 (RunProgramChild, 1);
203 child->use_signal = TRUE;
204 plugin->child = g_list_prepend (plugin->child, child);
206 pid = ianjuta_terminal_execute_command (term, dir, new_cmd, env, NULL);
207 child->pid = pid;
208 g_free (new_cmd);
211 return pid;
214 static GPid
215 execute_without_terminal (RunProgramPlugin *plugin,
216 const gchar *dir, gchar *cmd, gchar **env)
218 char *user_shell;
219 char * argv[4];
220 GPid pid;
221 RunProgramChild *child;
222 gsize len;
223 gchar **new_env;
224 gchar **old_env;
225 gchar **p;
226 gint i;
228 /* Create environment variable array */
229 old_env = g_listenv();
230 len = old_env ? g_strv_length (old_env) : 0;
231 len += env ? g_strv_length (env) : 0;
232 len ++;
233 new_env = g_new (char *, len);
235 /* Remove some environment variables, Move other in new_env */
236 i = 0;
237 for (p = old_env; *p; p++)
239 const gchar *value;
241 value = g_getenv (*p);
242 if (env != NULL)
244 gchar **q;
246 for (q = env; *q; q++)
248 gsize len;
250 len = strlen (*p);
251 if ((strlen (*q) > len + 1) &&
252 (strncmp (*q, *p, len) == 0) &&
253 (*q[len] == '='))
255 value = *q + len + 1;
256 break;
261 new_env[i++] = g_strconcat (*p, "=", value, NULL);
263 g_strfreev (old_env);
265 /* Add new user variable */
266 if (env)
268 for (p = env; *p; p++)
270 new_env[i++] = g_strdup (*p);
272 new_env[i] = NULL;
275 /* Run user program using in a shell */
276 user_shell = gnome_util_user_shell ();
277 argv[0] = user_shell;
278 argv[1] = "-c";
279 argv[2] = cmd;
280 argv[3] = NULL;
282 child = g_new0 (RunProgramChild, 1);
283 plugin->child = g_list_prepend (plugin->child, child);
285 if (g_spawn_async_with_pipes (dir, argv, new_env, G_SPAWN_DO_NOT_REAP_CHILD,
286 NULL, NULL, &pid, NULL, NULL, NULL, NULL))
288 child->pid = pid;
289 child->source = g_child_watch_add (pid, on_child_terminated, plugin);
291 else
293 pid = 0;
296 g_free (user_shell);
297 g_strfreev (new_env);
299 return pid;
302 /* Public functions
303 *---------------------------------------------------------------------------*/
305 gboolean
306 run_plugin_run_program (RunProgramPlugin *plugin)
308 gchar *target;
309 gchar *quote_target;
310 gchar *dir = NULL;
311 gchar *args = NULL;
312 gchar **env = NULL;
313 gchar *cmd;
314 gboolean run_in_terminal = 0;
315 AnjutaPreferences *prefs;
316 GPid pid;
318 if (!get_local_executable_and_directory (plugin, &target, &dir))
319 return FALSE;
321 /* Get other parameters from shell */
322 anjuta_shell_get (ANJUTA_PLUGIN (plugin)->shell,
323 RUN_PROGRAM_ARGS, G_TYPE_STRING, &args,
324 RUN_PROGRAM_ENV, G_TYPE_STRV, &env,
325 RUN_PROGRAM_NEED_TERM, G_TYPE_BOOLEAN, &run_in_terminal,
326 NULL);
328 /* TODO: Check if target is up to date */
330 if (dir == NULL)
331 dir = g_path_get_dirname (target);
333 /* Quote target name */
334 quote_target = g_shell_quote (target);
335 g_free (target);
337 if (args && strlen (args) > 0)
338 cmd = g_strconcat (quote_target, " ", args, NULL);
339 else
340 cmd = g_strdup (quote_target);
341 g_free (args);
342 g_free (quote_target);
344 /* Take care of scratchbox */
345 prefs = anjuta_shell_get_preferences (ANJUTA_PLUGIN(plugin)->shell, NULL);
346 if (anjuta_preferences_get_int (prefs , PREF_USE_SB))
348 const gchar* sb_path = anjuta_preferences_get(prefs, PREF_SB_PATH);
349 /* we need to skip the /scratchbox/users part, maybe could be done more clever */
350 const gchar* real_dir = strstr(dir, "/home");
351 gchar* oldcmd = cmd;
352 gchar* olddir = dir;
354 cmd = g_strdup_printf("%s/login -d %s \"%s\"", sb_path,
355 real_dir, oldcmd);
356 g_free(oldcmd);
357 dir = g_strdup(real_dir);
358 g_free (olddir);
361 if (run_in_terminal)
363 pid = execute_with_terminal (plugin, dir, cmd, env);
364 if (!pid)
366 pid = execute_without_terminal (plugin, dir, cmd, env);
369 else
371 pid = execute_without_terminal (plugin, dir, cmd, env);
374 if (pid == 0)
376 anjuta_util_dialog_error (GTK_WINDOW (ANJUTA_PLUGIN (plugin)->shell),
377 "Unable to execute %s", cmd);
379 run_plugin_update_menu_sensitivity (plugin);
381 g_free (dir);
382 g_strfreev (env);
383 g_free (cmd);
385 return TRUE;
388 gboolean
389 run_plugin_kill_program (RunProgramPlugin *plugin, gboolean terminate)
391 if (plugin->child != NULL)
393 RunProgramChild *child = (RunProgramChild *)plugin->child->data;
395 if (!child->terminated && terminate)
397 kill (child->pid, SIGTERM);
398 child->terminated = TRUE;
400 else
402 kill (child->pid, SIGKILL);
403 run_plugin_child_free (plugin, child->pid);
407 return TRUE;
410 void
411 run_free_all_children (RunProgramPlugin *plugin)
413 GList *child;
414 IAnjutaTerminal *term;
416 /* Remove terminal child-exited handle */
417 term = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
418 IAnjutaTerminal, NULL);
419 g_signal_handlers_disconnect_by_func (term, on_child_terminated, plugin);
420 plugin->child_exited_connection = 0;
422 /* Remove all child-exited source */
423 for (child = g_list_first (plugin->child); child != NULL; child = g_list_next (child))
425 if (!((RunProgramChild *)child->data)->use_signal)
427 g_source_remove (((RunProgramChild *)child->data)->source);
429 g_free (child->data);
431 g_list_free (plugin->child);
432 plugin->child = NULL;