r3663: If we can't guess a file's type from its name or extended attribute, try
[rox-filer.git] / ROX-Filer / src / run.c
blob5744d19e5d9b7a2e528975ebeada6423f710a8a6
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 void run_app_with_arg(const char *path, const char *arg)
86 GString *apprun;
87 const char *argv[] = {NULL, NULL, NULL};
89 apprun = g_string_new(path);
90 argv[0] = g_string_append(apprun, "/AppRun")->str;
91 argv[1] = arg;
93 rox_spawn(home_dir, argv);
95 g_string_free(apprun, TRUE);
98 /* Execute this program, passing all the URIs in the list as arguments.
99 * URIs that are files on the local machine will be passed as simple
100 * pathnames. The uri_list should be freed after this function returns.
102 void run_with_files(const char *path, GList *uri_list)
104 const char **argv;
105 int argc = 0, i;
106 struct stat info;
108 if (stat(path, &info))
110 delayed_error(_("Program %s not found - deleted?"), path);
111 return;
114 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
116 if (S_ISDIR(info.st_mode))
117 argv[argc++] = make_path(path, "AppRun");
118 else
119 argv[argc++] = path;
121 while (uri_list)
123 const char *uri = uri_list->data;
124 char *local;
126 local = get_local_path(uri);
127 if (local)
128 argv[argc++] = local;
129 else
130 argv[argc++] = g_strdup(uri);
131 uri_list = uri_list->next;
134 argv[argc++] = NULL;
136 rox_spawn(home_dir, argv);
138 for (i = 1; i < argc; i++)
139 g_free((gchar *) argv[i]);
140 g_free(argv);
143 /* Run the program as '<path> -', piping the data to it via stdin.
144 * You can g_free() the data as soon as this returns.
146 void run_with_data(const char *path, gpointer data, gulong length)
148 const char *argv[] = {NULL, "-", NULL};
149 struct stat info;
150 int fds[2];
151 PipedData *pd;
153 if (stat(path, &info))
155 delayed_error(_("Program %s not found - deleted?"), path);
156 return;
159 if (S_ISDIR(info.st_mode))
160 argv[0] = make_path(path, "AppRun");
161 else
162 argv[0] = path;
164 if (pipe(fds))
166 delayed_error("pipe: %s", g_strerror(errno));
167 return;
169 close_on_exec(fds[1], TRUE);
170 close_on_exec(fds[0], TRUE);
172 switch (fork())
174 case -1:
175 delayed_error("fork: %s", g_strerror(errno));
176 close(fds[1]);
177 break;
178 case 0:
179 /* We are the child */
180 chdir(home_dir);
181 if (dup2(fds[0], 0) == -1)
182 g_warning("dup2() failed: %s\n",
183 g_strerror(errno));
184 else
186 close_on_exec(0, FALSE);
187 if (execv(argv[0], (char **) argv))
188 g_warning("execv(%s) failed: %s\n",
189 argv[0], g_strerror(errno));
191 _exit(1);
192 default:
193 /* We are the parent */
194 set_blocking(fds[1], FALSE);
195 pd = g_new(PipedData, 1);
196 pd->data = g_malloc(length);
197 memcpy(pd->data, data, length);
198 pd->length = length;
199 pd->sent = 0;
200 pd->tag = gdk_input_add_full(fds[1], GDK_INPUT_WRITE,
201 write_data, pd, NULL);
202 break;
205 close(fds[0]);
208 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
209 * edit a file, open an application, follow a symlink or mount a device.
211 * arg is a string to append to the command when running apps or executables,
212 * or NULL. Ignored for non-executables
213 * filer_window is the window to use for displaying a directory.
214 * NULL will always use a new directory when needed.
215 * src_window is the window to copy options from, or NULL.
217 * Returns TRUE on success.
219 gboolean run_diritem_with_arg(const guchar *full_path,
220 DirItem *item,
221 const gchar *arg,
222 FilerWindow *filer_window,
223 FilerWindow *src_window,
224 gboolean edit)
226 if (item->flags & ITEM_FLAG_SYMLINK && edit)
227 return follow_symlink(full_path, filer_window, src_window);
229 switch (item->base_type)
231 case TYPE_DIRECTORY:
232 if (item->flags & ITEM_FLAG_APPDIR && !edit)
234 if(arg)
235 run_app_with_arg(full_path, arg);
236 else
237 run_app(full_path);
238 return TRUE;
241 if (item->flags & ITEM_FLAG_MOUNT_POINT)
243 open_mountpoint(full_path, item,
244 filer_window, src_window, edit);
246 else if (filer_window)
247 filer_change_to(filer_window, full_path, NULL);
248 else
249 filer_opendir(full_path, src_window, NULL);
250 return TRUE;
251 case TYPE_FILE:
252 if (item->mime_type == application_executable && !edit)
254 const char *argv[] = {NULL, NULL, NULL};
255 guchar *dir = filer_window
256 ? filer_window->sym_path
257 : NULL;
259 argv[0] = full_path;
260 argv[1] = arg;
262 return rox_spawn(dir, argv) != 0;
265 return open_file(full_path, edit ? text_plain
266 : item->mime_type);
267 case TYPE_ERROR:
268 delayed_error(_("File doesn't exist, or I can't "
269 "access it: %s"), full_path);
270 return FALSE;
271 default:
272 delayed_error(
273 _("I don't know how to open '%s'"), full_path);
274 return FALSE;
278 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
279 * edit a file, open an application, follow a symlink or mount a device.
281 * filer_window is the window to use for displaying a directory.
282 * NULL will always use a new directory when needed.
283 * src_window is the window to copy options from, or NULL.
285 * Returns TRUE on success.
287 gboolean run_diritem(const guchar *full_path,
288 DirItem *item,
289 FilerWindow *filer_window,
290 FilerWindow *src_window,
291 gboolean edit)
293 return run_diritem_with_arg(full_path, item, NULL, filer_window,
294 src_window, edit);
297 /* Attempt to open this item */
298 gboolean run_by_path(const guchar *full_path)
300 gboolean retval;
301 DirItem *item;
303 /* XXX: Loads an image - wasteful */
304 item = diritem_new("");
305 diritem_restat(full_path, item, NULL);
306 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
307 diritem_free(item);
309 return retval;
312 /* Open dir/Help, or show a message if missing */
313 void show_help_files(const char *dir)
315 const char *help_dir;
317 help_dir = make_path(dir, "Help");
319 if (file_exists(help_dir))
320 filer_opendir(help_dir, NULL, NULL);
321 else
322 info_message(
323 _("Application:\n"
324 "This is an application directory - you can "
325 "run it as a program, or open it (hold down "
326 "Shift while you open it). Most applications provide "
327 "their own help here, but this one doesn't."));
330 /* Open a directory viewer showing this file, and wink it */
331 void open_to_show(const guchar *path)
333 FilerWindow *new;
334 guchar *dir, *slash;
336 g_return_if_fail(path != NULL);
338 dir = g_strdup(path);
339 slash = strrchr(dir, '/');
340 if (slash == dir || !slash)
342 /* Item in the root (or root itself!) */
343 new = filer_opendir("/", NULL, NULL);
344 if (new && dir[1])
345 display_set_autoselect(new, dir + 1);
347 else
349 *slash = '\0';
350 new = filer_opendir(dir, NULL, NULL);
351 if (new)
353 if (slash[1] == '.')
354 display_set_hidden(new, TRUE);
355 display_set_autoselect(new, slash + 1);
359 g_free(dir);
362 /* Invoked using -x, this indicates that the filesystem has been modified
363 * and we should look at this item again.
365 void examine(const guchar *path)
367 struct stat info;
369 if (mc_stat(path, &info) != 0)
371 /* Deleted? Do a paranoid update of everything... */
372 filer_check_mounted(path);
374 else
376 /* Update directory containing this item... */
377 dir_check_this(path);
379 /* If this is itself a directory then rescan its contents... */
380 if (S_ISDIR(info.st_mode))
381 refresh_dirs(path);
383 /* If it's on the pinboard, update the icon... */
384 icons_may_update(path);
388 /****************************************************************
389 * INTERNAL FUNCTIONS *
390 ****************************************************************/
393 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
395 PipedData *pd = (PipedData *) data;
397 while (pd->sent < pd->length)
399 int sent;
401 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
403 if (sent < 0)
405 if (errno == EAGAIN)
406 return;
407 delayed_error(_("Could not send data to program: %s"),
408 g_strerror(errno));
409 goto finish;
412 pd->sent += sent;
415 finish:
416 g_source_remove(pd->tag);
417 g_free(pd->data);
418 g_free(pd);
419 close(fd);
422 /* Follow the link 'full_path' and display it in filer_window, or a
423 * new window if that is NULL.
425 static gboolean follow_symlink(const char *full_path,
426 FilerWindow *filer_window,
427 FilerWindow *src_window)
429 char *real, *slash;
430 char *new_dir;
431 char path[MAXPATHLEN + 1];
432 int got;
434 got = readlink(full_path, path, MAXPATHLEN);
435 if (got < 0)
437 delayed_error(_("Could not read link: %s"),
438 g_strerror(errno));
439 return FALSE;
442 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
443 path[got] = '\0';
445 /* Make a relative path absolute */
446 if (path[0] != '/')
448 guchar *tmp;
449 slash = strrchr(full_path, '/');
450 g_return_val_if_fail(slash != NULL, FALSE);
452 tmp = g_strndup(full_path, slash - full_path);
453 real = pathdup(make_path(tmp, path));
454 /* NB: full_path may be invalid here... */
455 g_free(tmp);
457 else
458 real = pathdup(path);
460 slash = strrchr(real, '/');
461 if (!slash)
463 g_free(real);
464 delayed_error(
465 _("Broken symlink (or you don't have permission "
466 "to follow it): %s"), full_path);
467 return FALSE;
470 *slash = '\0';
472 if (*real)
473 new_dir = real;
474 else
475 new_dir = "/";
477 if (filer_window)
478 filer_change_to(filer_window, new_dir, slash + 1);
479 else
481 FilerWindow *new;
483 new = filer_opendir(new_dir, src_window, NULL);
484 if (new)
485 display_set_autoselect(new, slash + 1);
488 g_free(real);
490 return TRUE;
493 /* Load this file into an appropriate editor */
494 static gboolean open_file(const guchar *path, MIME_type *type)
496 g_return_val_if_fail(type != NULL, FALSE);
498 if (type_open(path, type))
499 return TRUE;
501 report_error(
502 _("No run action specified for files of this type (%s/%s) - "
503 "you can set a run action by choosing `Set Run Action' "
504 "from the File menu, or you can just drag the file to an "
505 "application"),
506 type->media_type,
507 type->subtype);
509 return FALSE;
512 /* Called like run_diritem, when a mount-point is opened */
513 static void open_mountpoint(const guchar *full_path, DirItem *item,
514 FilerWindow *filer_window, FilerWindow *src_window,
515 gboolean edit)
517 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
519 if (mounted == edit)
521 GList *paths;
523 paths = g_list_prepend(NULL, (gpointer) full_path);
524 action_mount(paths, filer_window == NULL, -1);
525 g_list_free(paths);
526 if (filer_window && !mounted)
527 filer_change_to(filer_window, full_path, NULL);
529 else
531 if (filer_window)
532 filer_change_to(filer_window, full_path, NULL);
533 else
534 filer_opendir(full_path, src_window, NULL);