r2180: Code tidying (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / run.c
blobbe5322e921de8efd17740bad1cd7849b1a481358
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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 dir_show_help(DirItem *item, const char *path);
51 static void open_mountpoint(const guchar *full_path, DirItem *item,
52 FilerWindow *filer_window, FilerWindow *src_window,
53 gboolean edit);
55 typedef struct _PipedData PipedData;
57 struct _PipedData
59 guchar *data;
60 gint tag;
61 gulong sent;
62 gulong length;
66 /****************************************************************
67 * EXTERNAL INTERFACE *
68 ****************************************************************/
71 /* An application has been double-clicked (or run in some other way) */
72 void run_app(const char *path)
74 GString *apprun;
75 const char *argv[] = {NULL, NULL};
77 apprun = g_string_new(path);
78 argv[0] = g_string_append(apprun, "/AppRun")->str;
80 rox_spawn(home_dir, argv);
82 g_string_free(apprun, TRUE);
85 /* Execute this program, passing all the URIs in the list as arguments.
86 * URIs that are files on the local machine will be passed as simple
87 * pathnames. The uri_list should be freed after this function returns.
89 void run_with_files(const char *path, GList *uri_list)
91 const char **argv;
92 int argc = 0;
93 struct stat info;
95 if (stat(path, &info))
97 delayed_error(_("Program %s not found - deleted?"), path);
98 return;
101 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
103 if (S_ISDIR(info.st_mode))
104 argv[argc++] = make_path(path, "AppRun");
105 else
106 argv[argc++] = path;
108 while (uri_list)
110 const char *uri = uri_list->data;
111 const char *local;
113 local = get_local_path(uri);
114 if (local)
115 argv[argc++] = local;
116 else
117 argv[argc++] = uri;
118 uri_list = uri_list->next;
121 argv[argc++] = NULL;
123 rox_spawn(home_dir, argv);
126 /* Run the program as '<path> -', piping the data to it via stdin.
127 * You can g_free() the data as soon as this returns.
129 void run_with_data(const char *path, gpointer data, gulong length)
131 const char *argv[] = {NULL, "-", NULL};
132 struct stat info;
133 int fds[2];
134 PipedData *pd;
136 if (stat(path, &info))
138 delayed_error(_("Program %s not found - deleted?"), path);
139 return;
142 if (S_ISDIR(info.st_mode))
143 argv[0] = make_path(path, "AppRun");
144 else
145 argv[0] = path;
147 if (pipe(fds))
149 delayed_error("pipe: %s", g_strerror(errno));
150 return;
152 close_on_exec(fds[1], TRUE);
153 close_on_exec(fds[0], TRUE);
155 switch (fork())
157 case -1:
158 delayed_error("fork: %s", g_strerror(errno));
159 close(fds[1]);
160 break;
161 case 0:
162 /* We are the child */
163 chdir(home_dir);
164 if (dup2(fds[0], 0) == -1)
165 g_warning("dup2() failed: %s\n",
166 g_strerror(errno));
167 else
169 close_on_exec(0, FALSE);
170 if (execv(argv[0], (char **) argv))
171 g_warning("execv(%s) failed: %s\n",
172 argv[0], g_strerror(errno));
174 _exit(1);
175 default:
176 /* We are the parent */
177 set_blocking(fds[1], FALSE);
178 pd = g_new(PipedData, 1);
179 pd->data = g_malloc(length);
180 memcpy(pd->data, data, length);
181 pd->length = length;
182 pd->sent = 0;
183 pd->tag = gtk_input_add_full(fds[1], GDK_INPUT_WRITE,
184 write_data, NULL, pd, NULL);
185 break;
188 close(fds[0]);
191 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
192 * edit a file, open an application, follow a symlink or mount a device.
194 * filer_window is the window to use for displaying a directory.
195 * NULL will always use a new directory when needed.
196 * src_window is the window to copy options from, or NULL.
198 * Returns TRUE on success.
200 gboolean run_diritem(const guchar *full_path,
201 DirItem *item,
202 FilerWindow *filer_window,
203 FilerWindow *src_window,
204 gboolean edit)
206 if (item->flags & ITEM_FLAG_SYMLINK && edit)
207 return follow_symlink(full_path, filer_window, src_window);
209 switch (item->base_type)
211 case TYPE_DIRECTORY:
212 if (item->flags & ITEM_FLAG_APPDIR && !edit)
214 run_app(full_path);
215 return TRUE;
218 if (item->flags & ITEM_FLAG_MOUNT_POINT)
220 open_mountpoint(full_path, item,
221 filer_window, src_window, edit);
223 else if (filer_window)
224 filer_change_to(filer_window, full_path, NULL);
225 else
226 filer_opendir(full_path, src_window, NULL);
227 return TRUE;
228 case TYPE_FILE:
229 if (item->mime_type == application_executable && !edit)
231 const char *argv[] = {NULL, NULL};
232 guchar *dir = filer_window
233 ? filer_window->sym_path
234 : NULL;
236 argv[0] = full_path;
238 return rox_spawn(dir, argv);
241 return open_file(full_path, edit ? text_plain
242 : item->mime_type);
243 case TYPE_ERROR:
244 delayed_error(_("File doesn't exist, or I can't "
245 "access it: %s"), full_path);
246 return FALSE;
247 default:
248 delayed_error(
249 _("I don't know how to open '%s'"), full_path);
250 return FALSE;
254 /* Attempt to open this item */
255 gboolean run_by_path(const guchar *full_path)
257 gboolean retval;
258 DirItem *item;
260 /* XXX: Loads an image - wasteful */
261 item = diritem_new("");
262 diritem_restat(full_path, item, NULL);
263 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
264 diritem_free(item);
266 return retval;
269 void show_item_help(const guchar *path, DirItem *item)
271 switch (item->base_type)
273 case TYPE_FILE:
274 if (item->flags & ITEM_FLAG_EXEC_FILE)
275 info_message(
276 _("Executable file:\n"
277 "This is a file with an eXecute bit "
278 "set - it can be run as a program."));
279 else
280 info_message(
281 _("File:\n"
282 "This is a data file. Try using the "
283 "Info menu item to find out more..."));
284 break;
285 case TYPE_DIRECTORY:
286 if (item->flags & ITEM_FLAG_MOUNT_POINT)
287 info_message(
288 _("Mount point:\n"
289 "A mount point is a directory which another "
290 "filing system can be mounted on. Everything "
291 "on the mounted filesystem then appears to be "
292 "inside the directory."));
293 else
294 dir_show_help(item, path);
295 break;
296 case TYPE_CHAR_DEVICE:
297 case TYPE_BLOCK_DEVICE:
298 info_message(
299 _("Device file:\n"
300 "Device files allow you to read from or write "
301 "to a device driver as though it was an "
302 "ordinary file."));
303 break;
304 case TYPE_PIPE:
305 info_message(
306 _("Named pipe:\n"
307 "Pipes allow different programs to "
308 "communicate. One program writes data to the "
309 "pipe while another one reads it out again."));
310 break;
311 case TYPE_SOCKET:
312 info_message(
313 _("Socket:\n"
314 "Sockets allow processes to communicate."));
315 break;
316 case TYPE_DOOR:
317 info_message(
318 _("Door:\n"
319 "Doors are a little-used Solaris method for "
320 "processes to communicate."));
321 break;
322 default:
323 info_message(
324 _("Unknown type:\n"
325 "I couldn't find out what kind of file this "
326 "is. Maybe it doesn't exist anymore or you "
327 "don't have search permission on the directory "
328 "it's in?"));
329 break;
333 /* Open a directory viewer showing this file, and wink it */
334 void open_to_show(const guchar *path)
336 FilerWindow *new;
337 guchar *dir, *slash;
339 g_return_if_fail(path != NULL);
341 dir = g_strdup(path);
342 slash = strrchr(dir, '/');
343 if (slash == dir || !slash)
345 /* Item in the root (or root itself!) */
346 new = filer_opendir("/", NULL, NULL);
347 if (new && dir[1])
348 display_set_autoselect(new, dir + 1);
350 else
352 *slash = '\0';
353 new = filer_opendir(dir, NULL, NULL);
354 if (new)
356 if (slash[1] == '.')
357 display_set_hidden(new, TRUE);
358 display_set_autoselect(new, slash + 1);
362 g_free(dir);
365 /* Invoked using -x, this indicates that the filesystem has been modified
366 * and we should look at this item again.
368 void examine(const guchar *path)
370 struct stat info;
372 if (mc_stat(path, &info) != 0)
374 /* Deleted? Do a paranoid update of everything... */
375 filer_check_mounted(path);
377 else
379 /* Update directory containing this item... */
380 dir_check_this(path);
382 /* If this is itself a directory then rescan its contents... */
383 if (S_ISDIR(info.st_mode))
384 refresh_dirs(path);
386 /* If it's on the pinboard, update the icon... */
387 icons_may_update(path);
391 /****************************************************************
392 * INTERNAL FUNCTIONS *
393 ****************************************************************/
396 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
398 PipedData *pd = (PipedData *) data;
400 while (pd->sent < pd->length)
402 int sent;
404 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
406 if (sent < 0)
408 if (errno == EAGAIN)
409 return;
410 delayed_error(_("Could not send data to program: %s"),
411 g_strerror(errno));
412 goto finish;
415 pd->sent += sent;
418 finish:
419 g_source_remove(pd->tag);
420 g_free(pd->data);
421 g_free(pd);
422 close(fd);
425 /* Follow the link 'full_path' and display it in filer_window, or a
426 * new window if that is NULL.
428 static gboolean follow_symlink(const char *full_path,
429 FilerWindow *filer_window,
430 FilerWindow *src_window)
432 char *real, *slash;
433 char *new_dir;
434 char path[MAXPATHLEN + 1];
435 int got;
437 got = readlink(full_path, path, MAXPATHLEN);
438 if (got < 0)
440 delayed_error(_("Could not read link: %s"),
441 g_strerror(errno));
442 return FALSE;
445 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
446 path[got] = '\0';
448 /* Make a relative path absolute */
449 if (path[0] != '/')
451 guchar *tmp;
452 slash = strrchr(full_path, '/');
453 g_return_val_if_fail(slash != NULL, FALSE);
455 tmp = g_strndup(full_path, slash - full_path);
456 real = pathdup(make_path(tmp, path));
457 /* NB: full_path may be invalid here... */
458 g_free(tmp);
460 else
461 real = pathdup(path);
463 slash = strrchr(real, '/');
464 if (!slash)
466 g_free(real);
467 delayed_error(
468 _("Broken symlink (or you don't have permission "
469 "to follow it): %s"), full_path);
470 return FALSE;
473 *slash = '\0';
475 if (*real)
476 new_dir = real;
477 else
478 new_dir = "/";
480 if (filer_window)
481 filer_change_to(filer_window, new_dir, slash + 1);
482 else
484 FilerWindow *new;
486 new = filer_opendir(new_dir, src_window, NULL);
487 if (new)
488 display_set_autoselect(new, slash + 1);
491 g_free(real);
493 return TRUE;
496 /* Load this file into an appropriate editor */
497 static gboolean open_file(const guchar *path, MIME_type *type)
499 g_return_val_if_fail(type != NULL, FALSE);
501 if (type_open(path, type))
502 return TRUE;
504 report_error(
505 _("No run action specified for files of this type (%s/%s) - "
506 "you can set a run action by choosing `Set Run Action' "
507 "from the File menu, or you can just drag the file to an "
508 "application"),
509 type->media_type,
510 type->subtype);
512 return FALSE;
515 /* Show the help for a directory - tries to open App/Help, but if
516 * that doesn't work then it displays a default message.
518 static void dir_show_help(DirItem *item, const char *path)
520 char *help_dir;
522 help_dir = g_strconcat(path, "/Help", NULL);
524 if (file_exists(help_dir) == 0)
525 filer_opendir(help_dir, NULL, NULL);
526 else if (item->flags & ITEM_FLAG_APPDIR)
527 info_message(
528 _("Application:\n"
529 "This is an application directory - you can "
530 "run it as a program, or open it (hold down "
531 "Shift while you open it). Most applications provide "
532 "their own help here, but this one doesn't."));
533 else
534 info_message(_("Directory:\n"
535 "This is a directory. It contains an index to "
536 "other items - open it to see the list."));
539 /* Called like run_diritem, when a mount-point is opened */
540 static void open_mountpoint(const guchar *full_path, DirItem *item,
541 FilerWindow *filer_window, FilerWindow *src_window,
542 gboolean edit)
544 gboolean mounted = (item->flags & ITEM_FLAG_MOUNTED) != 0;
546 if (mounted == edit)
548 GList *paths;
550 paths = g_list_prepend(NULL, (gpointer) full_path);
551 action_mount(paths, filer_window == NULL, -1);
552 g_list_free(paths);
553 if (filer_window && !mounted)
554 filer_change_to(filer_window, full_path, NULL);
556 else
558 if (filer_window)
559 filer_change_to(filer_window, full_path, NULL);
560 else
561 filer_opendir(full_path, src_window, NULL);