r496: Many internal changes to the Options system.
[rox-filer.git] / ROX-Filer / src / run.c
blob6657197d6a6e8879d8a9986375c2acb13e13fe9f
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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 "main.h"
37 #include "type.h"
38 #include "dir.h"
39 #include "action.h"
40 #include "pinboard.h"
41 #include "panel.h"
42 #include "icon.h"
44 /* Static prototypes */
45 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
46 static gboolean follow_symlink(char *full_path, FilerWindow *filer_window);
47 static gboolean open_file(guchar *path, MIME_type *type);
48 static void app_show_help(char *path);
49 static void examine(guchar *path);
51 typedef struct _PipedData PipedData;
53 struct _PipedData
55 guchar *data;
56 gint tag;
57 gulong sent;
58 gulong length;
62 /****************************************************************
63 * EXTERNAL INTERFACE *
64 ****************************************************************/
67 /* An application has been double-clicked (or run in some other way) */
68 void run_app(char *path)
70 GString *apprun;
71 char *argv[] = {NULL, NULL};
73 apprun = g_string_new(path);
74 argv[0] = g_string_append(apprun, "/AppRun")->str;
76 if (!spawn_full(argv, home_dir))
77 report_error(PROJECT, "Failed to fork() child process");
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(char *path, GSList *uri_list)
88 char **argv;
89 int argc = 0;
90 struct stat info;
92 if (stat(path, &info))
94 delayed_error(PROJECT, _("Program not found - deleted?"));
95 return;
98 argv = g_malloc(sizeof(char *) * (g_slist_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 char *uri = (char *) uri_list->data;
108 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 if (!spawn_full(argv, home_dir))
121 delayed_error(PROJECT, _("Failed to fork() child process"));
124 /* Run the program as '<path> -', piping the data to it via stdin.
125 * You can g_free() the data as soon as this returns.
127 void run_with_data(char *path, gpointer data, gulong length)
129 char *argv[] = {NULL, "-", NULL};
130 struct stat info;
131 int fds[2];
132 PipedData *pd;
134 if (stat(path, &info))
136 delayed_error(PROJECT, _("Program not found - deleted?"));
137 return;
140 if (S_ISDIR(info.st_mode))
141 argv[0] = make_path(path, "AppRun")->str;
142 else
143 argv[0] = path;
145 if (pipe(fds))
147 delayed_error("pipe() failed", g_strerror(errno));
148 return;
150 close_on_exec(fds[1], TRUE);
151 close_on_exec(fds[0], TRUE);
153 switch (fork())
155 case -1:
156 delayed_error("fork() failed", g_strerror(errno));
157 close(fds[1]);
158 break;
159 case 0:
160 /* We are the child */
161 chdir(home_dir);
162 if (dup2(fds[0], 0) == -1)
163 g_warning("dup2() failed: %s\n",
164 g_strerror(errno));
165 else
167 close_on_exec(0, FALSE);
168 if (execv(argv[0], argv))
169 g_warning("execv(%s) failed: %s\n",
170 argv[0], g_strerror(errno));
172 _exit(1);
173 default:
174 /* We are the parent */
175 set_blocking(fds[1], FALSE);
176 pd = g_new(PipedData, 1);
177 pd->data = g_malloc(length);
178 memcpy(pd->data, data, length);
179 pd->length = length;
180 pd->sent = 0;
181 pd->tag = gdk_input_add(fds[1], GDK_INPUT_WRITE,
182 write_data, pd);
183 break;
186 close(fds[0]);
189 /* Load a file, open a directory or run an application. Or, if 'edit' is set:
190 * edit a file, open an application, follow a symlink or mount a device.
192 * filer_window is the window to use for displaying a directory.
193 * NULL will always use a new directory when needed.
195 * Returns TRUE on success.
197 gboolean run_diritem(guchar *full_path,
198 DirItem *item,
199 FilerWindow *filer_window,
200 gboolean edit)
202 if (item->flags & ITEM_FLAG_SYMLINK && edit)
203 return follow_symlink(full_path, filer_window);
205 switch (item->base_type)
207 case TYPE_DIRECTORY:
208 if (item->flags & ITEM_FLAG_APPDIR && !edit)
210 run_app(full_path);
211 return TRUE;
214 if (item->flags & ITEM_FLAG_MOUNT_POINT && edit)
216 GList *paths;
218 paths = g_list_prepend(NULL, full_path);
219 action_mount(paths);
220 g_list_free(paths);
221 if (item->flags & ITEM_FLAG_MOUNTED)
222 return TRUE;
225 if (filer_window)
226 filer_change_to(filer_window, full_path, NULL);
227 else
228 filer_opendir(full_path);
229 return TRUE;
230 case TYPE_FILE:
231 if ((item->mime_type == &special_exec) && !edit)
233 char *argv[] = {NULL, NULL};
234 guchar *dir = filer_window ? filer_window->path
235 : NULL;
237 argv[0] = full_path;
239 if (spawn_full(argv, dir))
240 return TRUE;
241 else
243 report_error(PROJECT,
244 _("Failed to fork() child"));
245 return FALSE;
249 return open_file(full_path, edit ? &text_plain
250 : item->mime_type);
251 case TYPE_ERROR:
252 delayed_error(full_path,
253 _("File doesn't exist, or I can't "
254 "access it"));
255 return FALSE;
256 default:
257 delayed_error(full_path,
258 _("I don't know how to open that"));
259 return FALSE;
263 /* Attempt to open this item */
264 gboolean run_by_path(guchar *full_path)
266 gboolean retval;
267 DirItem item;
269 /* XXX: Loads an image - wasteful */
270 dir_stat(full_path, &item, FALSE);
271 retval = run_diritem(full_path, &item, NULL, FALSE);
272 dir_item_clear(&item);
274 return retval;
277 void show_item_help(guchar *path, DirItem *item)
279 switch (item->base_type)
281 case TYPE_FILE:
282 if (item->flags & ITEM_FLAG_EXEC_FILE)
283 delayed_error(_("Executable file"),
284 _("This is a file with an eXecute bit "
285 "set - it can be run as a program."));
286 else
287 delayed_error(_("File"), _(
288 "This is a data file. Try using the "
289 "Info menu item to find out more..."));
290 break;
291 case TYPE_DIRECTORY:
292 if (item->flags & ITEM_FLAG_APPDIR)
293 app_show_help(path);
294 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
295 delayed_error(_("Mount point"), _(
296 "A mount point is a directory which another "
297 "filing system can be mounted on. Everything "
298 "on the mounted filesystem then appears to be "
299 "inside the directory."));
300 else
301 delayed_error(_("Directory"), _(
302 "This is a directory. It contains an index to "
303 "other items - open it to see the list."));
304 break;
305 case TYPE_CHAR_DEVICE:
306 case TYPE_BLOCK_DEVICE:
307 delayed_error(_("Device file"), _(
308 "Device files allow you to read from or write "
309 "to a device driver as though it was an "
310 "ordinary file."));
311 break;
312 case TYPE_PIPE:
313 delayed_error(_("Named pipe"), _(
314 "Pipes allow different programs to "
315 "communicate. One program writes data to the "
316 "pipe while another one reads it out again."));
317 break;
318 case TYPE_SOCKET:
319 delayed_error(_("Socket"), _(
320 "Sockets allow processes to communicate."));
321 break;
322 default:
323 delayed_error(_("Unknown type"), _(
324 "I couldn't find out what kind of file this "
325 "is. Maybe it doesn't exist anymore or you "
326 "don't have search permission on the directory "
327 "it's in?"));
328 break;
332 /* Runs each item in the list. Items may be files, directories,
333 * panels or pinboards.
335 * See main() for a description of 'to_open'.
337 void run_list(guchar *to_open)
339 guchar *next;
341 /* TODO: Should escape < characters in case one really does
342 * appear in a filename!
345 while (*to_open)
347 guchar code;
348 guchar *value;
350 g_return_if_fail(to_open[0] == '<');
351 code = to_open[1];
353 next = strchr(to_open + 1, '<');
354 if (!next)
355 next = to_open + strlen(to_open);
357 g_return_if_fail(next - to_open > 2);
358 g_return_if_fail(to_open[2] == '>');
360 value = g_strndup(to_open + 3, next - to_open - 3);
361 to_open = next;
363 switch (code)
365 case 'f':
366 run_by_path(value);
367 break;
368 case 'p':
369 pinboard_activate(value);
370 break;
371 case 's':
372 open_to_show(value);
373 break;
374 case 'd':
375 filer_opendir(value);
376 break;
377 case 't':
378 panel_new(value, PANEL_TOP);
379 break;
380 case 'b':
381 panel_new(value, PANEL_BOTTOM);
382 break;
383 case 'l':
384 panel_new(value, PANEL_LEFT);
385 break;
386 case 'r':
387 panel_new(value, PANEL_RIGHT);
388 break;
389 case 'x':
390 examine(value);
391 break;
392 default:
393 g_warning("Don't know how to handle '%s'",
394 value);
395 return;
398 g_free(value);
403 /* Open a directory viewer showing this file, and wink it */
404 void open_to_show(guchar *path)
406 FilerWindow *new;
407 guchar *dir, *slash;
409 g_return_if_fail(path != NULL);
411 dir = g_strdup(path);
412 slash = strrchr(dir, '/');
413 if (slash == dir || !slash)
415 /* Item in the root (or root itself!) */
416 new = filer_opendir("/");
417 if (new && dir[1])
418 display_set_autoselect(new, dir + 1);
421 else
423 *slash = '\0';
424 new = filer_opendir(dir);
425 if (new)
426 display_set_autoselect(new, slash + 1);
429 g_free(dir);
432 /****************************************************************
433 * INTERNAL FUNCTIONS *
434 ****************************************************************/
437 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
439 PipedData *pd = (PipedData *) data;
441 while (pd->sent < pd->length)
443 int sent;
445 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
447 if (sent < 0)
449 if (errno == EAGAIN)
450 return;
451 delayed_error(_("ROX-Filer - Sending data to program"),
452 g_strerror(errno));
453 goto finish;
456 pd->sent += sent;
459 finish:
460 gdk_input_remove(pd->tag);
461 g_free(pd->data);
462 g_free(pd);
463 close(fd);
466 /* Follow the link 'full_path' and display it in filer_window, or a
467 * new window if that is NULL.
469 static gboolean follow_symlink(char *full_path, FilerWindow *filer_window)
471 char *real, *slash;
472 char *new_dir;
473 char path[MAXPATHLEN + 1];
474 int got;
476 got = readlink(full_path, path, MAXPATHLEN);
477 if (got < 0)
479 delayed_error(PROJECT, g_strerror(errno));
480 return FALSE;
483 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
484 path[got] = '\0';
486 /* Make a relative path absolute */
487 if (path[0] != '/')
489 guchar *tmp;
490 slash = strrchr(full_path, '/');
491 g_return_val_if_fail(slash != NULL, FALSE);
493 tmp = g_strndup(full_path, slash - full_path);
494 real = pathdup(make_path(tmp, path)->str);
495 /* NB: full_path may be invalid here... */
496 g_free(tmp);
498 else
499 real = pathdup(path);
501 slash = strrchr(real, '/');
502 if (!slash)
504 g_free(real);
505 delayed_error(PROJECT,
506 _("Broken symlink (or you don't have permission "
507 "to follow it)."));
508 return FALSE;
511 *slash = '\0';
513 if (*real)
514 new_dir = real;
515 else
516 new_dir = "/";
518 if (filer_window)
519 filer_change_to(filer_window, new_dir, slash + 1);
520 else
522 FilerWindow *new;
524 new = filer_opendir(new_dir);
525 display_set_autoselect(new, slash + 1);
528 g_free(real);
530 return TRUE;
533 /* Load this file into an appropriate editor */
534 static gboolean open_file(guchar *path, MIME_type *type)
536 GString *message;
538 g_return_val_if_fail(type != NULL, FALSE);
540 if (type_open(path, type))
541 return TRUE;
543 message = g_string_new(NULL);
544 g_string_sprintf(message,
545 _("No run action specified for files of this type (%s/%s) - "
546 "you can set a run action by choosing `Set Run Action' "
547 "from the File menu, or you can just drag the file to an "
548 "application"),
549 type->media_type,
550 type->subtype);
551 report_error(PROJECT, message->str);
552 g_string_free(message, TRUE);
554 return FALSE;
557 static void app_show_help(char *path)
559 char *help_dir;
560 struct stat info;
562 help_dir = g_strconcat(path, "/Help", NULL);
564 if (mc_stat(help_dir, &info))
565 delayed_error(_("Application"),
566 _("This is an application directory - you can "
567 "run it as a program, or open it (hold down "
568 "Shift while you open it). Most applications provide "
569 "their own help here, but this one doesn't."));
570 else
571 filer_opendir(help_dir);
574 /* Invoked using -x, this indicates that the filesystem has been modified
575 * and we should look at this item again.
577 static void examine(guchar *path)
579 struct stat info;
581 if (mc_stat(path, &info) != 0)
583 /* Deleted? Do a paranoid update of everything... */
584 filer_check_mounted(path);
586 else
588 /* Update directory containing this item... */
589 dir_check_this(path);
591 /* If this is itself directory then rescan its contents... */
592 refresh_dirs(path);
594 /* If it's on the pinboard, update the icon... */
595 icons_may_update(path);