r339: The menu key bindings are now only saved if they actually changed.
[rox-filer.git] / ROX-Filer / src / run.c
blob80d5032f4cd26dde74a21b55af6424deb0b2b136
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 "stdlib.h"
33 #include "support.h"
34 #include "gui_support.h"
35 #include "filer.h"
36 #include "menu.h"
37 #include "main.h"
38 #include "type.h"
39 #include "dir.h"
40 #include "action.h"
41 #include "pinboard.h"
43 /* Static prototypes */
44 static void write_data(gpointer data, gint fd, GdkInputCondition cond);
45 static gboolean follow_symlink(char *full_path, FilerWindow *filer_window);
46 static gboolean open_file(guchar *path, MIME_type *type);
47 static void app_show_help(char *path);
49 typedef struct _PipedData PipedData;
51 struct _PipedData
53 guchar *data;
54 gint tag;
55 gulong sent;
56 gulong length;
60 /****************************************************************
61 * EXTERNAL INTERFACE *
62 ****************************************************************/
65 /* An application has been double-clicked (or run in some other way) */
66 void run_app(char *path)
68 GString *apprun;
69 char *argv[] = {NULL, NULL};
71 apprun = g_string_new(path);
72 argv[0] = g_string_append(apprun, "/AppRun")->str;
74 if (!spawn_full(argv, home_dir))
75 report_error(PROJECT, "Failed to fork() child process");
77 g_string_free(apprun, TRUE);
80 /* Execute this program, passing all the URIs in the list as arguments.
81 * URIs that are files on the local machine will be passed as simple
82 * pathnames. The uri_list should be freed after this function returns.
84 void run_with_files(char *path, GSList *uri_list)
86 char **argv;
87 int argc = 0;
88 struct stat info;
90 if (stat(path, &info))
92 delayed_error(PROJECT, _("Program not found - deleted?"));
93 return;
96 argv = g_malloc(sizeof(char *) * (g_slist_length(uri_list) + 2));
98 if (S_ISDIR(info.st_mode))
99 argv[argc++] = make_path(path, "AppRun")->str;
100 else
101 argv[argc++] = path;
103 while (uri_list)
105 char *uri = (char *) uri_list->data;
106 char *local;
108 local = get_local_path(uri);
109 if (local)
110 argv[argc++] = local;
111 else
112 argv[argc++] = uri;
113 uri_list = uri_list->next;
116 argv[argc++] = NULL;
118 if (!spawn_full(argv, home_dir))
119 delayed_error(PROJECT, _("Failed to fork() child process"));
122 /* Run the program as '<path> -', piping the data to it via stdin.
123 * You can g_free() the data as soon as this returns.
125 void run_with_data(char *path, gpointer data, gulong length)
127 char *argv[] = {NULL, "-", NULL};
128 struct stat info;
129 int fds[2];
130 PipedData *pd;
132 if (stat(path, &info))
134 delayed_error(PROJECT, _("Program not found - deleted?"));
135 return;
138 if (S_ISDIR(info.st_mode))
139 argv[0] = make_path(path, "AppRun")->str;
140 else
141 argv[0] = path;
143 if (pipe(fds))
145 delayed_error("pipe() failed", g_strerror(errno));
146 return;
148 close_on_exec(fds[1], TRUE);
149 close_on_exec(fds[0], TRUE);
151 switch (fork())
153 case -1:
154 delayed_error("fork() failed", g_strerror(errno));
155 close(fds[1]);
156 break;
157 case 0:
158 /* We are the child */
159 chdir(home_dir);
160 dup2(to_error_log, STDERR_FILENO);
161 close_on_exec(STDERR_FILENO, FALSE);
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 (filer_window && filer_window->panel_type)
203 filer_window = NULL;
205 if (item->flags & ITEM_FLAG_SYMLINK && edit)
206 return follow_symlink(full_path, filer_window);
208 switch (item->base_type)
210 case TYPE_DIRECTORY:
211 if (item->flags & ITEM_FLAG_APPDIR && !edit)
213 run_app(full_path);
214 return TRUE;
217 if (item->flags & ITEM_FLAG_MOUNT_POINT && edit)
219 GList *paths;
221 paths = g_list_prepend(NULL, full_path);
222 action_mount(paths);
223 g_list_free(paths);
224 if (item->flags & ITEM_FLAG_MOUNTED)
225 return TRUE;
228 if (filer_window)
229 filer_change_to(filer_window, full_path, NULL);
230 else
231 filer_opendir(full_path);
232 return TRUE;
233 case TYPE_FILE:
234 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !edit)
236 char *argv[] = {NULL, NULL};
237 guchar *dir = filer_window ? filer_window->path
238 : NULL;
240 argv[0] = full_path;
242 if (spawn_full(argv, dir))
243 return TRUE;
244 else
246 report_error(PROJECT,
247 _("Failed to fork() child"));
248 return FALSE;
252 return open_file(full_path, edit ? &text_plain
253 : item->mime_type);
254 default:
255 delayed_error(full_path,
256 "I don't know how to open that");
257 return FALSE;
261 /* Attempt to open this item */
262 gboolean run_by_path(guchar *full_path)
264 gboolean retval;
265 DirItem item;
267 /* XXX: Loads an image - wasteful */
268 dir_stat(full_path, &item);
269 retval = run_diritem(full_path, &item, NULL, FALSE);
270 dir_item_clear(&item);
272 return retval;
275 void show_item_help(guchar *path, DirItem *item)
277 switch (item->base_type)
279 case TYPE_FILE:
280 if (item->flags & ITEM_FLAG_EXEC_FILE)
281 delayed_error(_("Executable file"),
282 _("This is a file with an eXecute bit "
283 "set - it can be run as a program."));
284 else
285 delayed_error(_("File"), _(
286 "This is a data file. Try using the "
287 "Info menu item to find out more..."));
288 break;
289 case TYPE_DIRECTORY:
290 if (item->flags & ITEM_FLAG_APPDIR)
291 app_show_help(path);
292 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
293 delayed_error(_("Mount point"), _(
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 delayed_error(_("Directory"), _(
300 "This is a directory. It contains an index to "
301 "other items - open it to see the list."));
302 break;
303 case TYPE_CHAR_DEVICE:
304 case TYPE_BLOCK_DEVICE:
305 delayed_error(_("Device file"), _(
306 "Device files allow you to read from or write "
307 "to a device driver as though it was an "
308 "ordinary file."));
309 break;
310 case TYPE_PIPE:
311 delayed_error(_("Named pipe"), _(
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 delayed_error(_("Socket"), _(
318 "Sockets allow processes to communicate."));
319 break;
320 default:
321 delayed_error(_("Unknown type"), _(
322 "I couldn't find out what kind of file this "
323 "is. Maybe it doesn't exist anymore or you "
324 "don't have search permission on the directory "
325 "it's in?"));
326 break;
330 /* Runs each item in the list. Items may be files, directories,
331 * panels or pinboards.
333 * See main() for a description of 'to_open'.
335 void run_list(guchar *to_open)
337 guchar *next;
339 /* TODO: Should escape < characters in case one really does
340 * appear in a filename!
343 while (*to_open)
345 guchar code;
346 guchar *value;
348 g_return_if_fail(to_open[0] == '<');
349 code = to_open[1];
351 next = strchr(to_open + 1, '<');
352 if (!next)
353 next = to_open + strlen(to_open);
355 g_return_if_fail(next - to_open > 2);
356 g_return_if_fail(to_open[2] == '>');
358 value = g_strndup(to_open + 3, next - to_open - 3);
359 to_open = next;
361 switch (code)
363 case 'f':
364 run_by_path(value);
365 break;
366 case 'p':
367 pinboard_activate(value);
368 break;
369 case 'd':
370 filer_opendir(value);
371 break;
372 case 't':
373 filer_openpanel(value, PANEL_TOP);
374 break;
375 case 'b':
376 filer_openpanel(value, PANEL_BOTTOM);
377 break;
378 default:
379 g_warning("Don't know how to handle '%s'",
380 value);
381 return;
384 g_free(value);
389 /****************************************************************
390 * INTERNAL FUNCTIONS *
391 ****************************************************************/
394 static void write_data(gpointer data, gint fd, GdkInputCondition cond)
396 PipedData *pd = (PipedData *) data;
398 while (pd->sent < pd->length)
400 int sent;
402 sent = write(fd, pd->data + pd->sent, pd->length - pd->sent);
404 if (sent < 0)
406 if (errno == EAGAIN)
407 return;
408 delayed_error(_("ROX-Filer - Sending data to program"),
409 g_strerror(errno));
410 goto finish;
413 pd->sent += sent;
416 finish:
417 gdk_input_remove(pd->tag);
418 g_free(pd->data);
419 g_free(pd);
420 close(fd);
423 /* Follow the link 'full_path' and display it in filer_window, or a
424 * new window if that is NULL.
426 static gboolean follow_symlink(char *full_path, FilerWindow *filer_window)
428 char *real, *slash;
429 char *new_dir;
430 char path[MAXPATHLEN + 1];
431 int got;
433 got = readlink(full_path, path, MAXPATHLEN);
434 if (got < 0)
436 delayed_error(PROJECT, g_strerror(errno));
437 return FALSE;
440 g_return_val_if_fail(got <= MAXPATHLEN, FALSE);
441 path[got] = '\0';
443 /* Make a relative path absolute */
444 if (path[0] != '/')
446 guchar *tmp;
447 slash = strrchr(full_path, '/');
448 g_return_val_if_fail(slash != NULL, FALSE);
450 tmp = g_strndup(full_path, slash - full_path);
451 real = pathdup(make_path(tmp, path)->str);
452 g_free(tmp);
454 else
455 real = pathdup(path);
457 slash = strrchr(real, '/');
458 if (!slash)
460 g_free(real);
461 delayed_error(PROJECT,
462 _("Broken symlink (or you don't have permission "
463 "to follow it)."));
464 return FALSE;
467 *slash = '\0';
469 if (*real)
470 new_dir = real;
471 else
472 new_dir = "/";
474 if (filer_window)
475 filer_change_to(filer_window, new_dir, slash + 1);
476 else
478 FilerWindow *new;
480 new = filer_opendir(new_dir);
481 display_set_autoselect(new, slash + 1);
484 g_free(real);
486 return TRUE;
489 /* Load this file into an appropriate editor */
490 static gboolean open_file(guchar *path, MIME_type *type)
492 GString *message;
494 g_return_val_if_fail(type != NULL, FALSE);
496 if (type_open(path, type))
497 return TRUE;
499 message = g_string_new(NULL);
500 g_string_sprintf(message,
501 _("No run action specified for files of this type (%s/%s) - "
502 "you can set a run action using by choosing `Set Run Action' "
503 "from the Window menu"),
504 type->media_type,
505 type->subtype);
506 report_error(PROJECT, message->str);
507 g_string_free(message, TRUE);
509 return FALSE;
512 static void app_show_help(char *path)
514 char *help_dir;
515 struct stat info;
517 help_dir = g_strconcat(path, "/Help", NULL);
519 if (mc_stat(help_dir, &info))
520 delayed_error(_("Application"),
521 _("This is an application directory - you can "
522 "run it as a program, or open it (hold down "
523 "Shift while you open it). Most applications provide "
524 "their own help here, but this one doesn't."));
525 else
526 filer_opendir(help_dir);