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)
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
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
28 #include <sys/param.h>
34 #include "gui_support.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
,
54 typedef struct _PipedData PipedData
;
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
)
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
)
87 const char *argv
[] = {NULL
, NULL
, NULL
};
89 apprun
= g_string_new(path
);
90 argv
[0] = g_string_append(apprun
, "/AppRun")->str
;
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
)
108 if (stat(path
, &info
))
110 delayed_error(_("Program %s not found - deleted?"), path
);
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");
123 const char *uri
= uri_list
->data
;
126 local
= get_local_path(uri
);
128 argv
[argc
++] = local
;
130 argv
[argc
++] = g_strdup(uri
);
131 uri_list
= uri_list
->next
;
136 rox_spawn(home_dir
, argv
);
138 for (i
= 1; i
< argc
; i
++)
139 g_free((gchar
*) argv
[i
]);
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
};
153 if (stat(path
, &info
))
155 delayed_error(_("Program %s not found - deleted?"), path
);
159 if (S_ISDIR(info
.st_mode
))
160 argv
[0] = make_path(path
, "AppRun");
166 delayed_error("pipe: %s", g_strerror(errno
));
169 close_on_exec(fds
[1], TRUE
);
170 close_on_exec(fds
[0], TRUE
);
175 delayed_error("fork: %s", g_strerror(errno
));
179 /* We are the child */
181 if (dup2(fds
[0], 0) == -1)
182 g_warning("dup2() failed: %s\n",
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
));
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
);
200 pd
->tag
= gdk_input_add_full(fds
[1], GDK_INPUT_WRITE
,
201 write_data
, pd
, NULL
);
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
,
222 FilerWindow
*filer_window
,
223 FilerWindow
*src_window
,
226 if (item
->flags
& ITEM_FLAG_SYMLINK
&& edit
)
227 return follow_symlink(full_path
, filer_window
, src_window
);
229 switch (item
->base_type
)
232 if (item
->flags
& ITEM_FLAG_APPDIR
&& !edit
)
235 run_app_with_arg(full_path
, arg
);
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
);
249 filer_opendir(full_path
, src_window
, NULL
);
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
262 return rox_spawn(dir
, argv
) != 0;
265 return open_file(full_path
, edit
? text_plain
268 delayed_error(_("File doesn't exist, or I can't "
269 "access it: %s"), full_path
);
273 _("I don't know how to open '%s'"), full_path
);
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
,
289 FilerWindow
*filer_window
,
290 FilerWindow
*src_window
,
293 return run_diritem_with_arg(full_path
, item
, NULL
, filer_window
,
297 /* Attempt to open this item */
298 gboolean
run_by_path(const guchar
*full_path
)
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
);
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
);
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
)
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
);
345 display_set_autoselect(new, dir
+ 1);
350 new = filer_opendir(dir
, NULL
, NULL
);
354 display_set_hidden(new, TRUE
);
355 display_set_autoselect(new, slash
+ 1);
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
)
369 if (mc_stat(path
, &info
) != 0)
371 /* Deleted? Do a paranoid update of everything... */
372 filer_check_mounted(path
);
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
))
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
)
401 sent
= write(fd
, pd
->data
+ pd
->sent
, pd
->length
- pd
->sent
);
407 delayed_error(_("Could not send data to program: %s"),
416 g_source_remove(pd
->tag
);
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
)
431 char path
[MAXPATHLEN
+ 1];
434 got
= readlink(full_path
, path
, MAXPATHLEN
);
437 delayed_error(_("Could not read link: %s"),
442 g_return_val_if_fail(got
<= MAXPATHLEN
, FALSE
);
445 /* Make a relative path absolute */
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... */
458 real
= pathdup(path
);
460 slash
= strrchr(real
, '/');
465 _("Broken symlink (or you don't have permission "
466 "to follow it): %s"), full_path
);
478 filer_change_to(filer_window
, new_dir
, slash
+ 1);
483 new = filer_opendir(new_dir
, src_window
, NULL
);
485 display_set_autoselect(new, slash
+ 1);
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
))
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 "
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
,
517 gboolean mounted
= (item
->flags
& ITEM_FLAG_MOUNTED
) != 0;
523 paths
= g_list_prepend(NULL
, (gpointer
) full_path
);
524 action_mount(paths
, filer_window
== NULL
, -1);
526 if (filer_window
&& !mounted
)
527 filer_change_to(filer_window
, full_path
, NULL
);
532 filer_change_to(filer_window
, full_path
, NULL
);
534 filer_opendir(full_path
, src_window
, NULL
);