r1859: New 'Class' parameter to Filer_OpenDir SOAP method allows setting the
[rox-filer.git] / ROX-Filer / src / run.c
blob4e0752a04772fc13490b00344bb558420b5250b6
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);
52 typedef struct _PipedData PipedData;
54 struct _PipedData
56 guchar *data;
57 gint tag;
58 gulong sent;
59 gulong length;
63 /****************************************************************
64 * EXTERNAL INTERFACE *
65 ****************************************************************/
68 /* An application has been double-clicked (or run in some other way) */
69 void run_app(const char *path)
71 GString *apprun;
72 const char *argv[] = {NULL, NULL};
74 apprun = g_string_new(path);
75 argv[0] = g_string_append(apprun, "/AppRun")->str;
77 rox_spawn(home_dir, argv);
79 g_string_free(apprun, TRUE);
82 /* Execute this program, passing all the URIs in the list as arguments.
83 * URIs that are files on the local machine will be passed as simple
84 * pathnames. The uri_list should be freed after this function returns.
86 void run_with_files(const char *path, GList *uri_list)
88 const char **argv;
89 int argc = 0;
90 struct stat info;
92 if (stat(path, &info))
94 delayed_error(_("Program %s not found - deleted?"), path);
95 return;
98 argv = g_malloc(sizeof(char *) * (g_list_length(uri_list) + 2));
100 if (S_ISDIR(info.st_mode))
101 argv[argc++] = make_path(path, "AppRun")->str;
102 else
103 argv[argc++] = path;
105 while (uri_list)
107 const char *uri = uri_list->data;
108 const char *local;
110 local = get_local_path(uri);
111 if (local)
112 argv[argc++] = local;
113 else
114 argv[argc++] = uri;
115 uri_list = uri_list->next;
118 argv[argc++] = NULL;
120 rox_spawn(home_dir, argv);
123 /* Run the program as '<path> -', piping the data to it via stdin.
124 * You can g_free() the data as soon as this returns.
126 void run_with_data(const char *path, gpointer data, gulong length)
128 const char *argv[] = {NULL, "-", NULL};
129 struct stat info;
130 int fds[2];
131 PipedData *pd;
133 if (stat(path, &info))
135 delayed_error(_("Program %s not found - deleted?"), path);
136 return;
139 if (S_ISDIR(info.st_mode))
140 argv[0] = make_path(path, "AppRun")->str;
141 else
142 argv[0] = path;
144 if (pipe(fds))
146 delayed_error("pipe: %s", g_strerror(errno));
147 return;
149 close_on_exec(fds[1], TRUE);
150 close_on_exec(fds[0], TRUE);
152 switch (fork())
154 case -1:
155 delayed_error("fork: %s", g_strerror(errno));
156 close(fds[1]);
157 break;
158 case 0:
159 /* We are the child */
160 chdir(home_dir);
161 if (dup2(fds[0], 0) == -1)
162 g_warning("dup2() failed: %s\n",
163 g_strerror(errno));
164 else
166 close_on_exec(0, FALSE);
167 if (execv(argv[0], (char **) argv))
168 g_warning("execv(%s) failed: %s\n",
169 argv[0], g_strerror(errno));
171 _exit(1);
172 default:
173 /* We are the parent */
174 set_blocking(fds[1], FALSE);
175 pd = g_new(PipedData, 1);
176 pd->data = g_malloc(length);
177 memcpy(pd->data, data, length);
178 pd->length = length;
179 pd->sent = 0;
180 pd->tag = gtk_input_add_full(fds[1], GDK_INPUT_WRITE,
181 write_data, NULL, pd, NULL);
182 break;
185 close(fds[0]);
188 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
189 * edit a file, open an application, follow a symlink or mount a device.
191 * filer_window is the window to use for displaying a directory.
192 * NULL will always use a new directory when needed.
193 * src_window is the window to copy options from, or NULL.
195 * Returns TRUE on success.
197 gboolean run_diritem(const guchar *full_path,
198 DirItem *item,
199 FilerWindow *filer_window,
200 FilerWindow *src_window,
201 gboolean edit)
203 if (item->flags & ITEM_FLAG_SYMLINK && edit)
204 return follow_symlink(full_path, filer_window, src_window);
206 switch (item->base_type)
208 case TYPE_DIRECTORY:
209 if (item->flags & ITEM_FLAG_APPDIR && !edit)
211 run_app(full_path);
212 return TRUE;
215 if (item->flags & ITEM_FLAG_MOUNT_POINT && edit)
217 GList *paths;
219 paths = g_list_prepend(NULL,
220 (gpointer) full_path);
221 action_mount(paths, filer_window == NULL, -1);
222 g_list_free(paths);
223 if (item->flags & ITEM_FLAG_MOUNTED ||
224 !filer_window)
225 return TRUE;
228 if (filer_window)
229 filer_change_to(filer_window, full_path, NULL);
230 else
231 filer_opendir(full_path, src_window, NULL);
232 return TRUE;
233 case TYPE_FILE:
234 if ((item->mime_type == application_executable) && !edit)
236 const char *argv[] = {NULL, NULL};
237 guchar *dir = filer_window
238 ? filer_window->sym_path
239 : NULL;
241 argv[0] = full_path;
243 return rox_spawn(dir, argv);
246 return open_file(full_path, edit ? text_plain
247 : item->mime_type);
248 case TYPE_ERROR:
249 delayed_error(_("File doesn't exist, or I can't "
250 "access it: %s"), full_path);
251 return FALSE;
252 default:
253 delayed_error(
254 _("I don't know how to open '%s'"), full_path);
255 return FALSE;
259 /* Attempt to open this item */
260 gboolean run_by_path(const guchar *full_path)
262 gboolean retval;
263 DirItem *item;
265 /* XXX: Loads an image - wasteful */
266 item = diritem_new("");
267 diritem_restat(full_path, item, NULL);
268 retval = run_diritem(full_path, item, NULL, NULL, FALSE);
269 diritem_free(item);
271 return retval;
274 void show_item_help(const guchar *path, DirItem *item)
276 switch (item->base_type)
278 case TYPE_FILE:
279 if (item->flags & ITEM_FLAG_EXEC_FILE)
280 info_message(
281 _("Executable file:\n"
282 "This is a file with an eXecute bit "
283 "set - it can be run as a program."));
284 else
285 info_message(
286 _("File:\n"
287 "This is a data file. Try using the "
288 "Info menu item to find out more..."));
289 break;
290 case TYPE_DIRECTORY:
291 if (item->flags & ITEM_FLAG_MOUNT_POINT)
292 info_message(
293 _("Mount point:\n"
294 "A mount point is a directory which another "
295 "filing system can be mounted on. Everything "
296 "on the mounted filesystem then appears to be "
297 "inside the directory."));
298 else
299 dir_show_help(item, path);
300 break;
301 case TYPE_CHAR_DEVICE:
302 case TYPE_BLOCK_DEVICE:
303 info_message(
304 _("Device file:\n"
305 "Device files allow you to read from or write "
306 "to a device driver as though it was an "
307 "ordinary file."));
308 break;
309 case TYPE_PIPE:
310 info_message(
311 _("Named pipe:\n"
312 "Pipes allow different programs to "
313 "communicate. One program writes data to the "
314 "pipe while another one reads it out again."));
315 break;
316 case TYPE_SOCKET:
317 info_message(
318 _("Socket:\n"
319 "Sockets allow processes to communicate."));
320 break;
321 case TYPE_DOOR:
322 info_message(
323 _("Door:\n"
324 "Doors are a little-used Solaris method for "
325 "processes to communicate."));
326 break;
327 default:
328 info_message(
329 _("Unknown type:\n"
330 "I couldn't find out what kind of file this "
331 "is. Maybe it doesn't exist anymore or you "
332 "don't have search permission on the directory "
333 "it's in?"));
334 break;
338 /* Open a directory viewer showing this file, and wink it */
339 void open_to_show(const guchar *path)
341 FilerWindow *new;
342 guchar *dir, *slash;
344 g_return_if_fail(path != NULL);
346 dir = g_strdup(path);
347 slash = strrchr(dir, '/');
348 if (slash == dir || !slash)
350 /* Item in the root (or root itself!) */
351 new = filer_opendir("/", NULL, NULL);
352 if (new && dir[1])
353 display_set_autoselect(new, dir + 1);
355 else
357 *slash = '\0';
358 new = filer_opendir(dir, NULL, NULL);
359 if (new)
361 if (slash[1] == '.')
362 display_set_hidden(new, TRUE);
363 display_set_autoselect(new, slash + 1);
367 g_free(dir);
370 /* Invoked using -x, this indicates that the filesystem has been modified
371 * and we should look at this item again.
373 void examine(const guchar *path)
375 struct stat info;
377 if (mc_stat(path, &info) != 0)
379 /* Deleted? Do a paranoid update of everything... */
380 filer_check_mounted(path);
382 else
384 /* Update directory containing this item... */
385 dir_check_this(path);
387 /* If this is itself a directory then rescan its contents... */
388 if (S_ISDIR(info.st_mode))
389 refresh_dirs(path);
391 /* If it's on the pinboard, update the icon... */
392 icons_may_update(path);
396 /****************************************************************
397 * INTERNAL FUNCTIONS *
398 ****************************************************************/
401 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
403 PipedData *pd = (PipedData *) data;
405 while (pd->sent < pd->length)
407 int sent;
409 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
411 if (sent < 0)
413 if (errno == EAGAIN)
414 return;
415 delayed_error(_("Could not send data to program: %s"),
416 g_strerror(errno));
417 goto finish;
420 pd->sent += sent;
423 finish:
424 g_source_remove(pd->tag);
425 g_free(pd->data);
426 g_free(pd);
427 close(fd);
430 /* Follow the link 'full_path' and display it in filer_window, or a
431 * new window if that is NULL.
433 static gboolean follow_symlink(const char *full_path,
434 FilerWindow *filer_window,
435 FilerWindow *src_window)
437 char *real, *slash;
438 char *new_dir;
439 char path[MAXPATHLEN + 1];
440 int got;
442 got = readlink(full_path, path, MAXPATHLEN);
443 if (got < 0)
445 delayed_error(_("Could not read link: %s"),
446 g_strerror(errno));
447 return FALSE;
450 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
451 path[got] = '\0';
453 /* Make a relative path absolute */
454 if (path[0] != '/')
456 guchar *tmp;
457 slash = strrchr(full_path, '/');
458 g_return_val_if_fail(slash != NULL, FALSE);
460 tmp = g_strndup(full_path, slash - full_path);
461 real = pathdup(make_path(tmp, path)->str);
462 /* NB: full_path may be invalid here... */
463 g_free(tmp);
465 else
466 real = pathdup(path);
468 slash = strrchr(real, '/');
469 if (!slash)
471 g_free(real);
472 delayed_error(
473 _("Broken symlink (or you don't have permission "
474 "to follow it): %s"), full_path);
475 return FALSE;
478 *slash = '\0';
480 if (*real)
481 new_dir = real;
482 else
483 new_dir = "/";
485 if (filer_window)
486 filer_change_to(filer_window, new_dir, slash + 1);
487 else
489 FilerWindow *new;
491 new = filer_opendir(new_dir, src_window, NULL);
492 if (new)
493 display_set_autoselect(new, slash + 1);
496 g_free(real);
498 return TRUE;
501 /* Load this file into an appropriate editor */
502 static gboolean open_file(const guchar *path, MIME_type *type)
504 g_return_val_if_fail(type != NULL, FALSE);
506 if (type_open(path, type))
507 return TRUE;
509 report_error(
510 _("No run action specified for files of this type (%s/%s) - "
511 "you can set a run action by choosing `Set Run Action' "
512 "from the File menu, or you can just drag the file to an "
513 "application"),
514 type->media_type,
515 type->subtype);
517 return FALSE;
520 /* Show the help for a directory - tries to open App/Help, but if
521 * that doesn't work then it displays a default message.
523 static void dir_show_help(DirItem *item, const char *path)
525 char *help_dir;
526 struct stat info;
528 help_dir = g_strconcat(path, "/Help", NULL);
530 if (mc_stat(help_dir, &info) == 0)
531 filer_opendir(help_dir, NULL, NULL);
532 else if (item->flags & ITEM_FLAG_APPDIR)
533 info_message(
534 _("Application:\n"
535 "This is an application directory - you can "
536 "run it as a program, or open it (hold down "
537 "Shift while you open it). Most applications provide "
538 "their own help here, but this one doesn't."));
539 else
540 info_message(_("Directory:\n"
541 "This is a directory. It contains an index to "
542 "other items - open it to see the list."));