r3082: Bugfix: If no pinboard or panel icons were present, the menu could not be
[rox-filer.git] / ROX-Filer / src / run.c
blob84e0a1cc28679a5da3cfa4cdee5a5ca67539feec
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* run.c */
24 #include "config.h"
26 #include <errno.h>
27 #include <string.h>
28 #include <sys/param.h>
30 #include "global.h"
32 #include "run.h"
33 #include "support.h"
34 #include "gui_support.h"
35 #include "filer.h"
36 #include "display.h"
37 #include "main.h"
38 #include "type.h"
39 #include "dir.h"
40 #include "diritem.h"
41 #include "action.h"
42 #include "icon.h"
44 /* Static prototypes */
45 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
46 static gboolean follow_symlink(const char *full_path,
47 FilerWindow *filer_window,
48 FilerWindow *src_window);
49 static gboolean open_file(const guchar *path, MIME_type *type);
50 static void open_mountpoint(const guchar *full_path, DirItem *item,
51 FilerWindow *filer_window, FilerWindow *src_window,
52 gboolean edit);
54 typedef struct _PipedData PipedData;
56 struct _PipedData
58 guchar *data;
59 gint tag;
60 gulong sent;
61 gulong length;
65 /****************************************************************
66 * EXTERNAL INTERFACE *
67 ****************************************************************/
70 /* An application has been double-clicked (or run in some other way) */
71 void run_app(const char *path)
73 GString *apprun;
74 const char *argv[] = {NULL, NULL};
76 apprun = g_string_new(path);
77 argv[0] = g_string_append(apprun, "/AppRun")->str;
79 rox_spawn(home_dir, argv);
81 g_string_free(apprun, TRUE);
84 /* Execute this program, passing all the URIs in the list as arguments.
85 * URIs that are files on the local machine will be passed as simple
86 * pathnames. The uri_list should be freed after this function returns.
88 void run_with_files(const char *path, GList *uri_list)
90 const char **argv;
91 int argc = 0;
92 struct stat info;
94 if (stat(path, &info))
96 delayed_error(_("Program %s not found - deleted?"), path);
97 return;
100 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
102 if (S_ISDIR(info.st_mode))
103 argv[argc++] = make_path(path, "AppRun");
104 else
105 argv[argc++] = path;
107 while (uri_list)
109 const char *uri = uri_list->data;
110 const char *local;
112 local = get_local_path(uri);
113 if (local)
114 argv[argc++] = local;
115 else
116 argv[argc++] = uri;
117 uri_list = uri_list->next;
120 argv[argc++] = NULL;
122 rox_spawn(home_dir, argv);
125 /* Run the program as '<path> -', piping the data to it via stdin.
126 * You can g_free() the data as soon as this returns.
128 void run_with_data(const char *path, gpointer data, gulong length)
130 const char *argv[] = {NULL, "-", NULL};
131 struct stat info;
132 int fds[2];
133 PipedData *pd;
135 if (stat(path, &info))
137 delayed_error(_("Program %s not found - deleted?"), path);
138 return;
141 if (S_ISDIR(info.st_mode))
142 argv[0] = make_path(path, "AppRun");
143 else
144 argv[0] = path;
146 if (pipe(fds))
148 delayed_error("pipe: %s", g_strerror(errno));
149 return;
151 close_on_exec(fds[1], TRUE);
152 close_on_exec(fds[0], TRUE);
154 switch (fork())
156 case -1:
157 delayed_error("fork: %s", g_strerror(errno));
158 close(fds[1]);
159 break;
160 case 0:
161 /* We are the child */
162 chdir(home_dir);
163 if (dup2(fds[0], 0) == -1)
164 g_warning("dup2() failed: %s\n",
165 g_strerror(errno));
166 else
168 close_on_exec(0, FALSE);
169 if (execv(argv[0], (char **) argv))
170 g_warning("execv(%s) failed: %s\n",
171 argv[0], g_strerror(errno));
173 _exit(1);
174 default:
175 /* We are the parent */
176 set_blocking(fds[1], FALSE);
177 pd = g_new(PipedData, 1);
178 pd->data = g_malloc(length);
179 memcpy(pd->data, data, length);
180 pd->length = length;
181 pd->sent = 0;
182 pd->tag = gtk_input_add_full(fds[1], GDK_INPUT_WRITE,
183 write_data, NULL, pd, NULL);
184 break;
187 close(fds[0]);
190 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
191 * edit a file, open an application, follow a symlink or mount a device.
193 * filer_window is the window to use for displaying a directory.
194 * NULL will always use a new directory when needed.
195 * src_window is the window to copy options from, or NULL.
197 * Returns TRUE on success.
199 gboolean run_diritem(const guchar *full_path,
200 DirItem *item,
201 FilerWindow *filer_window,
202 FilerWindow *src_window,
203 gboolean edit)
205 if (item->flags & ITEM_FLAG_SYMLINK && edit)
206 return follow_symlink(full_path, filer_window, src_window);
208 switch (item->base_type)
210 case TYPE_DIRECTORY:
211 if (item->flags & ITEM_FLAG_APPDIR && !edit)
213 run_app(full_path);
214 return TRUE;
217 if (item->flags & ITEM_FLAG_MOUNT_POINT)
219 open_mountpoint(full_path, item,
220 filer_window, src_window, edit);
222 else if (filer_window)
223 filer_change_to(filer_window, full_path, NULL);
224 else
225 filer_opendir(full_path, src_window, NULL);
226 return TRUE;
227 case TYPE_FILE:
228 if (item->mime_type == application_executable && !edit)
230 const char *argv[] = {NULL, NULL};
231 guchar *dir = filer_window
232 ? filer_window->sym_path
233 : NULL;
235 argv[0] = full_path;
237 return rox_spawn(dir, argv) != 0;
240 return open_file(full_path, edit ? text_plain
241 : item->mime_type);
242 case TYPE_ERROR:
243 delayed_error(_("File doesn't exist, or I can't "
244 "access it: %s"), full_path);
245 return FALSE;
246 default:
247 delayed_error(
248 _("I don't know how to open '%s'"), full_path);
249 return FALSE;
253 /* Attempt to open this item */
254 gboolean run_by_path(const guchar *full_path)
256 gboolean retval;
257 DirItem *item;
259 /* XXX: Loads an image - wasteful */
260 item = diritem_new("");
261 diritem_restat(full_path, item, NULL);
262 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
263 diritem_free(item);
265 return retval;
268 /* Open dir/Help, or show a message if missing */
269 void show_help_files(const char *dir)
271 const char *help_dir;
273 help_dir = make_path(dir, "Help");
275 if (file_exists(help_dir))
276 filer_opendir(help_dir, NULL, NULL);
277 else
278 info_message(
279 _("Application:\n"
280 "This is an application directory - you can "
281 "run it as a program, or open it (hold down "
282 "Shift while you open it). Most applications provide "
283 "their own help here, but this one doesn't."));
286 /* Open a directory viewer showing this file, and wink it */
287 void open_to_show(const guchar *path)
289 FilerWindow *new;
290 guchar *dir, *slash;
292 g_return_if_fail(path != NULL);
294 dir = g_strdup(path);
295 slash = strrchr(dir, '/');
296 if (slash == dir || !slash)
298 /* Item in the root (or root itself!) */
299 new = filer_opendir("/", NULL, NULL);
300 if (new && dir[1])
301 display_set_autoselect(new, dir + 1);
303 else
305 *slash = '\0';
306 new = filer_opendir(dir, NULL, NULL);
307 if (new)
309 if (slash[1] == '.')
310 display_set_hidden(new, TRUE);
311 display_set_autoselect(new, slash + 1);
315 g_free(dir);
318 /* Invoked using -x, this indicates that the filesystem has been modified
319 * and we should look at this item again.
321 void examine(const guchar *path)
323 struct stat info;
325 if (mc_stat(path, &info) != 0)
327 /* Deleted? Do a paranoid update of everything... */
328 filer_check_mounted(path);
330 else
332 /* Update directory containing this item... */
333 dir_check_this(path);
335 /* If this is itself a directory then rescan its contents... */
336 if (S_ISDIR(info.st_mode))
337 refresh_dirs(path);
339 /* If it's on the pinboard, update the icon... */
340 icons_may_update(path);
344 /****************************************************************
345 * INTERNAL FUNCTIONS *
346 ****************************************************************/
349 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
351 PipedData *pd = (PipedData *) data;
353 while (pd->sent < pd->length)
355 int sent;
357 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
359 if (sent < 0)
361 if (errno == EAGAIN)
362 return;
363 delayed_error(_("Could not send data to program: %s"),
364 g_strerror(errno));
365 goto finish;
368 pd->sent += sent;
371 finish:
372 g_source_remove(pd->tag);
373 g_free(pd->data);
374 g_free(pd);
375 close(fd);
378 /* Follow the link 'full_path' and display it in filer_window, or a
379 * new window if that is NULL.
381 static gboolean follow_symlink(const char *full_path,
382 FilerWindow *filer_window,
383 FilerWindow *src_window)
385 char *real, *slash;
386 char *new_dir;
387 char path[MAXPATHLEN + 1];
388 int got;
390 got = readlink(full_path, path, MAXPATHLEN);
391 if (got < 0)
393 delayed_error(_("Could not read link: %s"),
394 g_strerror(errno));
395 return FALSE;
398 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
399 path[got] = '\0';
401 /* Make a relative path absolute */
402 if (path[0] != '/')
404 guchar *tmp;
405 slash = strrchr(full_path, '/');
406 g_return_val_if_fail(slash != NULL, FALSE);
408 tmp = g_strndup(full_path, slash - full_path);
409 real = pathdup(make_path(tmp, path));
410 /* NB: full_path may be invalid here... */
411 g_free(tmp);
413 else
414 real = pathdup(path);
416 slash = strrchr(real, '/');
417 if (!slash)
419 g_free(real);
420 delayed_error(
421 _("Broken symlink (or you don't have permission "
422 "to follow it): %s"), full_path);
423 return FALSE;
426 *slash = '\0';
428 if (*real)
429 new_dir = real;
430 else
431 new_dir = "/";
433 if (filer_window)
434 filer_change_to(filer_window, new_dir, slash + 1);
435 else
437 FilerWindow *new;
439 new = filer_opendir(new_dir, src_window, NULL);
440 if (new)
441 display_set_autoselect(new, slash + 1);
444 g_free(real);
446 return TRUE;
449 /* Load this file into an appropriate editor */
450 static gboolean open_file(const guchar *path, MIME_type *type)
452 g_return_val_if_fail(type != NULL, FALSE);
454 if (type_open(path, type))
455 return TRUE;
457 report_error(
458 _("No run action specified for files of this type (%s/%s) - "
459 "you can set a run action by choosing `Set Run Action' "
460 "from the File menu, or you can just drag the file to an "
461 "application"),
462 type->media_type,
463 type->subtype);
465 return FALSE;
468 /* Called like run_diritem, when a mount-point is opened */
469 static void open_mountpoint(const guchar *full_path, DirItem *item,
470 FilerWindow *filer_window, FilerWindow *src_window,
471 gboolean edit)
473 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
475 if (mounted == edit)
477 GList *paths;
479 paths = g_list_prepend(NULL, (gpointer) full_path);
480 action_mount(paths, filer_window == NULL, -1);
481 g_list_free(paths);
482 if (filer_window && !mounted)
483 filer_change_to(filer_window, full_path, NULL);
485 else
487 if (filer_window)
488 filer_change_to(filer_window, full_path, NULL);
489 else
490 filer_opendir(full_path, src_window, NULL);