r288: Removed the borders from the toolbar buttons and put the new help icon
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob8ddc3fffbd1810793d5f3a3cc975a88a14d52a37
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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 /* action.c - code for handling the filer action windows.
23 * These routines generally fork() and talk to us via pipes.
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <signal.h>
37 #include <dirent.h>
38 #include <sys/time.h>
40 #include "action.h"
41 #include "string.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "filer.h"
45 #include "main.h"
46 #include "options.h"
47 #include "modechange.h"
48 #include "find.h"
50 /* Options bits */
51 static GtkWidget *create_options();
52 static void update_options();
53 static void set_options();
54 static void save_options();
55 static char *action_auto_quiet(char *data);
57 static OptionsSection options =
59 N_("Action window options"),
60 create_options,
61 update_options,
62 set_options,
63 save_options
66 static gboolean o_auto_copy = TRUE;
67 static gboolean o_auto_move = TRUE;
68 static gboolean o_auto_link = TRUE;
69 static gboolean o_auto_delete = FALSE;
70 static gboolean o_auto_mount = TRUE;
72 static GtkWidget *w_auto_copy = NULL;
73 static GtkWidget *w_auto_move = NULL;
74 static GtkWidget *w_auto_link = NULL;
75 static GtkWidget *w_auto_delete = NULL;
76 static GtkWidget *w_auto_mount = NULL;
78 /* Parent->Child messages are one character each:
80 * Q/Y/N Quiet/Yes/No button clicked
81 * F Force deletion of non-writeable items
84 #define SENSITIVE_YESNO(gui_side, state) \
85 do { \
86 gtk_widget_set_sensitive((gui_side)->yes, state); \
87 gtk_widget_set_sensitive((gui_side)->no, state); \
88 if ((gui_side)->entry) \
89 gtk_widget_set_sensitive((gui_side)->entry, state);\
90 } while (0)
92 typedef struct _GUIside GUIside;
93 typedef void ActionChild(gpointer data);
94 typedef gboolean ForDirCB(char *path, char *dest_path);
96 struct _GUIside
98 int from_child; /* File descriptor */
99 FILE *to_child;
100 int input_tag; /* gdk_input_add() */
101 GtkWidget *vbox, *log, *window, *dir, *log_hbox;
102 GtkWidget *quiet, *yes, *no;
103 int child; /* Process ID */
104 int errors;
105 gboolean show_info; /* For Disk Usage */
107 GtkWidget *entry; /* May be NULL */
108 guchar **default_string; /* Changed when the entry changes */
110 char *next_dir; /* NULL => no timer active */
111 gint next_timer;
113 /* Used by Find */
114 FilerWindow *preview;
115 GtkWidget *results;
118 /* These don't need to be in a structure because we fork() before
119 * using them again.
121 static int from_parent = 0;
122 static FILE *to_parent = NULL;
123 static gboolean quiet = FALSE;
124 static GString *message = NULL;
125 static char *action_dest = NULL;
126 static char *action_leaf = NULL;
127 static gboolean (*action_do_func)(char *source, char *dest);
128 static size_t size_tally; /* For Disk Usage */
129 static DirItem *mount_item;
131 static struct mode_change *mode_change = NULL; /* For Permissions */
132 static FindCondition *find_condition = NULL; /* For Find */
134 static gboolean o_force = FALSE;
135 static gboolean o_brief = FALSE;
136 static gboolean o_recurse = FALSE;
138 /* Whenever the text in these boxes is changed we store a copy of the new
139 * string to be used as the default next time.
141 static guchar *last_chmod_string = NULL;
142 static guchar *last_find_string = NULL;
144 /* Set to one of the above before forking. This may change over a call to
145 * reply(). It is reset to NULL once the text is parsed.
147 static guchar *new_entry_string = NULL;
149 /* Static prototypes */
150 static gboolean send();
151 static gboolean send_error();
152 static gboolean send_dir(char *dir);
153 static gboolean read_exact(int source, char *buffer, ssize_t len);
154 static void do_mount(FilerWindow *filer_window, DirItem *item);
155 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
156 static gboolean reply(int fd, gboolean ignore_quiet);
158 /* OPTIONS */
160 /* Build up some option widgets to go in the options dialog, but don't
161 * fill them in yet.
163 static GtkWidget *create_options()
165 GtkWidget *vbox, *hbox, *label;
167 vbox = gtk_vbox_new(FALSE, 0);
168 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
170 label = gtk_label_new(_("Auto-start (Quiet) these actions:"));
171 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
173 hbox = gtk_hbox_new(TRUE, 0);
174 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
176 w_auto_copy = gtk_check_button_new_with_label(_("Copy"));
177 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
178 w_auto_move = gtk_check_button_new_with_label(_("Move"));
179 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
180 w_auto_link = gtk_check_button_new_with_label(_("Link"));
181 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
182 w_auto_delete = gtk_check_button_new_with_label(_("Delete"));
183 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
184 w_auto_mount = gtk_check_button_new_with_label(_("Mount"));
185 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
187 return vbox;
190 /* Reflect current state by changing the widgets in the options box */
191 static void update_options()
193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
194 o_auto_copy);
195 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
196 o_auto_move);
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
198 o_auto_link);
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
200 o_auto_delete);
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
202 o_auto_mount);
205 /* Set current values by reading the states of the widgets in the options box */
206 static void set_options()
208 o_auto_copy = gtk_toggle_button_get_active(
209 GTK_TOGGLE_BUTTON(w_auto_copy));
210 o_auto_move = gtk_toggle_button_get_active(
211 GTK_TOGGLE_BUTTON(w_auto_move));
212 o_auto_link = gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_link));
214 o_auto_delete = gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_delete));
216 o_auto_mount = gtk_toggle_button_get_active(
217 GTK_TOGGLE_BUTTON(w_auto_mount));
220 static void save_options()
222 guchar str[] = "cmldt";
224 if (o_auto_copy)
225 str[0] = 'C';
226 if (o_auto_move)
227 str[1] = 'M';
228 if (o_auto_link)
229 str[2] = 'L';
230 if (o_auto_delete)
231 str[3] = 'D';
232 if (o_auto_mount)
233 str[4] = 'T';
235 option_write("action_auto_quiet", str);
238 static char *action_auto_quiet(char *data)
240 while (*data)
242 char c = *data++;
243 gboolean state;
245 state = isupper(c);
247 switch (tolower(c))
249 case 'c':
250 o_auto_copy = state;
251 break;
252 case 'm':
253 o_auto_move = state;
254 break;
255 case 'l':
256 o_auto_link = state;
257 break;
258 case 'd':
259 o_auto_delete = state;
260 break;
261 case 't':
262 o_auto_mount = state;
263 break;
264 default:
265 return _("Unknown flag");
269 return NULL;
272 /* SUPPORT */
274 static void preview_closed(GtkWidget *window, GUIside *gui_side)
276 gui_side->preview = NULL;
279 static void select_row_callback(GtkWidget *widget,
280 gint row, gint column,
281 GdkEventButton *event,
282 GUIside *gui_side)
284 gchar *leaf, *dir;
286 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
287 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
289 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
291 if (gui_side->preview)
293 if (strcmp(gui_side->preview->path, dir) == 0)
294 filer_set_autoselect(gui_side->preview, leaf);
295 else
296 filer_change_to(gui_side->preview, dir, leaf);
297 return;
300 gui_side->preview = filer_opendir(dir, PANEL_NO);
301 if (gui_side->preview)
303 filer_set_autoselect(gui_side->preview, leaf);
304 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window),
305 "destroy",
306 GTK_SIGNAL_FUNC(preview_closed), gui_side);
311 /* This is called whenever the user edits the entry box (if any) - send the
312 * new string.
314 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
316 guchar *text;
318 g_return_if_fail(gui_side->default_string != NULL);
320 text = gtk_entry_get_text(entry);
322 g_free(*(gui_side->default_string));
323 *(gui_side->default_string) = g_strdup(text);
325 if (!gui_side->to_child)
326 return;
328 fputc('E', gui_side->to_child);
329 fputs(text, gui_side->to_child);
330 fputc('\n', gui_side->to_child);
331 fflush(gui_side->to_child);
334 void show_condition_help(gpointer data)
336 static GtkWidget *help = NULL;
338 if (!help)
340 GtkWidget *text, *vbox, *button, *hbox, *frame;
342 help = gtk_window_new(GTK_WINDOW_DIALOG);
343 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
344 gtk_window_set_title(GTK_WINDOW(help),
345 _("Find expression reference"));
347 vbox = gtk_vbox_new(FALSE, 0);
348 gtk_container_add(GTK_CONTAINER(help), vbox);
350 frame = gtk_frame_new(_("Quick Start"));
351 text = gtk_label_new(
352 _("Just put the name of the file you're looking for in single quotes:\n"
353 "'index.html' (to find a file called 'index.html')"));
354 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
355 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
356 gtk_container_add(GTK_CONTAINER(frame), text);
357 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
359 frame = gtk_frame_new(_("Examples"));
360 text = gtk_label_new(
361 _("'*.htm', '*.html' (finds HTML files)\n"
362 "IsDir 'lib' (finds directories called 'lib')\n"
363 "IsReg 'core' (finds a regular file called 'core')\n"
364 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
365 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
366 "'CVS' prune, isreg (a regular file not in CVS)\n"
367 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
368 gtk_widget_set_style(text, fixed_style);
369 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
370 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
371 gtk_container_add(GTK_CONTAINER(frame), text);
372 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
374 frame = gtk_frame_new(_("Simple Tests"));
375 text = gtk_label_new(
376 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
377 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
378 "\n"
379 "IsEmpty, IsMine\n"
380 "\n"
381 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
382 "contains a slash then the match is against the full path; otherwise it is \n"
383 "against the leafname only."));
384 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
385 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
386 gtk_container_add(GTK_CONTAINER(frame), text);
387 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
389 frame = gtk_frame_new(_("Comparisons"));
390 text = gtk_label_new(
391 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
392 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
393 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
394 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
395 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
396 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
397 gtk_container_add(GTK_CONTAINER(frame), text);
398 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
400 frame = gtk_frame_new(_("Specials"));
401 text = gtk_label_new(
402 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
403 "in 'command' is replaced with the path of the current file)\n"
404 "prune (false, and prevents searching the contents of a directory).")
406 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
407 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
408 gtk_container_add(GTK_CONTAINER(frame), text);
409 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
411 hbox = gtk_hbox_new(FALSE, 20);
412 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
414 text = gtk_label_new(
415 _("See the ROX-Filer manual for full details."));
416 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
417 button = gtk_button_new_with_label(_("Close"));
418 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
419 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
420 gtk_widget_hide, GTK_OBJECT(help));
422 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
423 gtk_widget_hide, GTK_OBJECT(help));
426 if (GTK_WIDGET_VISIBLE(help))
427 gtk_widget_hide(help);
428 gtk_widget_show_all(help);
431 static void show_chmod_help(gpointer data)
433 static GtkWidget *help = NULL;
435 if (!help)
437 GtkWidget *text, *vbox, *button, *hbox, *sep;
439 help = gtk_window_new(GTK_WINDOW_DIALOG);
440 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
441 gtk_window_set_title(GTK_WINDOW(help),
442 _("Permissions command reference"));
444 vbox = gtk_vbox_new(FALSE, 0);
445 gtk_container_add(GTK_CONTAINER(help), vbox);
447 text = gtk_label_new(
448 _("The format of a command is:\n"
449 "CHANGE, CHANGE, ...\n"
450 "Each CHANGE is:\n"
451 "WHO HOW PERMISSIONS\n"
452 "WHO is some combination of u, g and o which determines whether to\n"
453 "change the permissions for the User (owner), Group or Others.\n"
454 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
455 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
457 "Examples:\n"
458 "u+rw (the file owner gains read and write permission)\n"
459 "g=u (the group permissions are set to be the same as the user's)\n"
460 "o=u-w (others get the same permissions as the owner, but without "
461 "write permission)\n"
462 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
463 "a+X (directories become accessable by everyone; files which were\n"
464 "executable by anyone become executable by everyone)\n"
465 "u+rw, go+r (two commands at once!)\n"
466 "u+s (set the SetUID bit - often has no effect on script files)\n"
467 "755 (set the permissions directly)\n"
469 "\nSee the chmod(1) man page for full details."));
470 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
471 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
473 hbox = gtk_hbox_new(FALSE, 20);
474 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
476 sep = gtk_hseparator_new();
477 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
478 button = gtk_button_new_with_label(_("Close"));
479 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
480 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
481 gtk_widget_hide, GTK_OBJECT(help));
483 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
484 gtk_widget_hide, GTK_OBJECT(help));
487 if (GTK_WIDGET_VISIBLE(help))
488 gtk_widget_hide(help);
489 gtk_widget_show_all(help);
492 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
493 * (or the two are the same directory)
495 static gboolean is_sub_dir(char *sub, char *parent)
497 int parent_len;
498 guchar *real_sub, *real_parent;
499 gboolean retval;
501 real_sub = pathdup(sub);
502 real_parent = pathdup(parent);
504 parent_len = strlen(real_parent);
505 if (strncmp(real_parent, real_sub, parent_len))
506 retval = FALSE;
507 else
509 /* real_sub is at least as long as real_parent and all
510 * characters upto real_parent's length match.
513 retval = real_sub[parent_len] == '\0' ||
514 real_sub[parent_len] == '/';
517 g_free(real_sub);
518 g_free(real_parent);
520 return retval;
523 static gboolean display_dir(gpointer data)
525 GUIside *gui_side = (GUIside *) data;
527 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
528 g_free(gui_side->next_dir);
529 gui_side->next_dir = NULL;
531 return FALSE;
534 static void add_to_results(GUIside *gui_side, gchar *path)
536 gchar *row[] = {"Leaf", "Dir"};
537 gchar *slash;
538 int len;
540 slash = strrchr(path, '/');
541 g_return_if_fail(slash != NULL);
543 len = slash - path;
544 row[1] = g_strndup(path, MAX(len, 1));
545 row[0] = slash + 1;
547 gtk_clist_append(GTK_CLIST(gui_side->results), row);
549 g_free(row[1]);
552 /* Called when the child sends us a message */
553 static void message_from_child(gpointer data,
554 gint source,
555 GdkInputCondition condition)
557 char buf[5];
558 GUIside *gui_side = (GUIside *) data;
559 GtkWidget *log = gui_side->log;
561 if (read_exact(source, buf, 4))
563 ssize_t message_len;
564 char *buffer;
566 buf[4] = '\0';
567 message_len = strtol(buf, NULL, 16);
568 buffer = g_malloc(message_len + 1);
569 if (message_len > 0 && read_exact(source, buffer, message_len))
571 buffer[message_len] = '\0';
572 if (*buffer == '?')
574 SENSITIVE_YESNO(gui_side, TRUE);
575 gtk_window_set_focus(
576 GTK_WINDOW(gui_side->window),
577 gui_side->entry ? gui_side->entry
578 : gui_side->yes);
580 else if (*buffer == '+')
582 refresh_dirs(buffer + 1);
583 g_free(buffer);
584 return;
586 else if (*buffer == '=')
588 add_to_results(gui_side, buffer + 1);
589 g_free(buffer);
590 return;
592 else if (*buffer == 'm')
594 filer_check_mounted(buffer + 1);
595 g_free(buffer);
596 return;
598 else if (*buffer == '/')
600 if (gui_side->next_dir)
601 g_free(gui_side->next_dir);
602 else
603 gui_side->next_timer =
604 gtk_timeout_add(500,
605 display_dir,
606 gui_side);
607 gui_side->next_dir = buffer;
608 return;
610 else if (*buffer == '!')
611 gui_side->errors++;
613 gtk_text_insert(GTK_TEXT(log),
614 NULL,
615 *buffer == '!' ? &red : NULL,
616 NULL,
617 buffer + 1, message_len - 1);
618 g_free(buffer);
619 return;
621 g_printerr("Child died in the middle of a message.\n");
624 /* The child is dead */
625 gui_side->child = 0;
627 fclose(gui_side->to_child);
628 gui_side->to_child = NULL;
629 close(gui_side->from_child);
630 gdk_input_remove(gui_side->input_tag);
631 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
633 if (gui_side->errors)
635 guchar *report;
637 if (gui_side->errors == 1)
638 report = g_strdup(_("There was one error.\n"));
639 else
640 report = g_strdup_printf(_("There were %d errors.\n"),
641 gui_side->errors);
643 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
644 report, -1);
646 g_free(report);
648 else if (gui_side->show_info == FALSE)
649 gtk_widget_destroy(gui_side->window);
652 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
653 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
655 DIR *d;
656 struct dirent *ent;
657 GSList *list = NULL, *next;
659 d = mc_opendir(src_dir);
660 if (!d)
662 /* Message displayed is "ERROR reading 'path': message" */
663 g_string_sprintf(message, "!%s '%s': %s\n",
664 _("ERROR reading"),
665 src_dir, g_strerror(errno));
666 send();
667 return;
670 send_dir(src_dir);
672 while ((ent = mc_readdir(d)))
674 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
675 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
676 continue;
677 list = g_slist_append(list, g_strdup(make_path(src_dir,
678 ent->d_name)->str));
680 mc_closedir(d);
682 if (!list)
683 return;
685 next = list;
687 while (next)
689 if (cb((char *) next->data, dest_path))
691 g_string_sprintf(message, "+%s", dest_path);
692 send();
695 g_free(next->data);
696 next = next->next;
698 g_slist_free(list);
699 return;
702 /* Read this many bytes into the buffer. TRUE on success. */
703 static gboolean read_exact(int source, char *buffer, ssize_t len)
705 while (len > 0)
707 ssize_t got;
708 got = read(source, buffer, len);
709 if (got < 1)
710 return FALSE;
711 len -= got;
712 buffer += got;
714 return TRUE;
717 /* Send 'message' to our parent process. TRUE on success. */
718 static gboolean send()
720 char len_buffer[5];
721 ssize_t len;
723 g_return_val_if_fail(message->len < 0xffff, FALSE);
725 sprintf(len_buffer, "%04x", message->len);
726 fwrite(len_buffer, 1, 4, to_parent);
727 len = fwrite(message->str, 1, message->len, to_parent);
728 fflush(to_parent);
729 return len == message->len;
732 /* Set the directory indicator at the top of the window */
733 static gboolean send_dir(char *dir)
735 g_string_sprintf(message, "/%s", dir);
736 return send();
739 static gboolean send_error()
741 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
742 return send();
745 static void button_reply(GtkWidget *button, GUIside *gui_side)
747 char *text;
749 if (!gui_side->to_child)
750 return;
752 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
753 g_return_if_fail(text != NULL);
754 fputc(*text, gui_side->to_child);
755 fflush(gui_side->to_child);
757 if (*text == 'Y' || *text == 'N' || *text == 'Q')
758 SENSITIVE_YESNO(gui_side, FALSE);
761 static void process_flag(char flag)
763 switch (flag)
765 case 'Q':
766 quiet = !quiet;
767 break;
768 case 'F':
769 o_force = !o_force;
770 break;
771 case 'R':
772 o_recurse = !o_recurse;
773 break;
774 case 'B':
775 o_brief = !o_brief;
776 break;
777 default:
778 g_string_sprintf(message,
779 "!ERROR: Bad message '%c'\n", flag);
780 send();
781 break;
785 /* If the parent has sent any flag toggles, read them */
786 static void check_flags(void)
788 fd_set set;
789 int got;
790 char retval;
791 struct timeval tv;
793 FD_ZERO(&set);
795 while (1)
797 FD_SET(from_parent, &set);
798 tv.tv_sec = 0;
799 tv.tv_usec = 0;
800 got = select(from_parent + 1, &set, NULL, NULL, &tv);
802 if (got == -1)
803 g_error("select() failed: %s\n", g_strerror(errno));
804 else if (!got)
805 return;
807 got = read(from_parent, &retval, 1);
808 if (got != 1)
809 g_error("read() error: %s\n", g_strerror(errno));
811 process_flag(retval);
815 static void read_new_entry_text(void)
817 int len;
818 char c;
819 GString *new;
821 new = g_string_new(NULL);
823 for (;;)
825 len = read(from_parent, &c, 1);
826 if (len != 1)
828 fprintf(stderr, "read() error: %s\n",
829 g_strerror(errno));
830 _exit(1); /* Parent died? */
833 if (c == '\n')
834 break;
835 g_string_append_c(new, c);
838 g_free(new_entry_string);
839 new_entry_string = new->str;
840 g_string_free(new, FALSE);
843 /* Read until the user sends a reply. If ignore_quiet is TRUE then
844 * the user MUST click Yes or No, else treat quiet on as Yes.
845 * If the user needs prompting then does send().
847 static gboolean reply(int fd, gboolean ignore_quiet)
849 ssize_t len;
850 char retval;
851 gboolean asked = FALSE;
853 while (ignore_quiet || !quiet)
855 if (!asked)
857 send();
858 asked = TRUE;
861 len = read(fd, &retval, 1);
862 if (len != 1)
864 fprintf(stderr, "read() error: %s\n",
865 g_strerror(errno));
866 _exit(1); /* Parent died? */
869 switch (retval)
871 case 'Q':
872 quiet = !quiet;
873 if (ignore_quiet)
875 g_string_assign(message, "?");
876 send();
878 break;
879 case 'Y':
880 g_string_sprintf(message, "' %s\n", _("Yes"));
881 send();
882 return TRUE;
883 case 'N':
884 g_string_sprintf(message, "' %s\n", _("No"));
885 send();
886 return FALSE;
887 case 'E':
888 read_new_entry_text();
889 break;
890 default:
891 process_flag(retval);
892 break;
896 if (asked)
898 g_string_sprintf(message, "' %s\n", _("Quiet"));
899 send();
901 return TRUE;
904 static void destroy_action_window(GtkWidget *widget, gpointer data)
906 GUIside *gui_side = (GUIside *) data;
908 if (gui_side->child)
910 kill(gui_side->child, SIGTERM);
911 fclose(gui_side->to_child);
912 close(gui_side->from_child);
913 gdk_input_remove(gui_side->input_tag);
916 if (gui_side->next_dir)
918 gtk_timeout_remove(gui_side->next_timer);
919 g_free(gui_side->next_dir);
922 if (gui_side->preview)
924 gtk_signal_disconnect_by_data(
925 GTK_OBJECT(gui_side->preview->window),
926 gui_side);
927 gui_side->preview = NULL;
930 g_free(gui_side);
932 if (--number_of_windows < 1)
933 gtk_main_quit();
936 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
937 * (NULL on failure). The child calls func().
939 * If autoq then automatically selects 'Quiet'.
941 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
943 int filedes[4]; /* 0 and 2 are for reading */
944 GUIside *gui_side;
945 int child;
946 GtkWidget *vbox, *button, *scrollbar, *actions;
947 struct sigaction act;
949 if (pipe(filedes))
951 report_error(PROJECT, g_strerror(errno));
952 return NULL;
955 if (pipe(filedes + 2))
957 close(filedes[0]);
958 close(filedes[1]);
959 report_error(PROJECT, g_strerror(errno));
960 return NULL;
963 child = fork();
964 switch (child)
966 case -1:
967 report_error(PROJECT, g_strerror(errno));
968 return NULL;
969 case 0:
970 /* We are the child */
972 dup2(to_error_log, STDOUT_FILENO);
973 close_on_exec(STDOUT_FILENO, FALSE);
974 dup2(to_error_log, STDERR_FILENO);
975 close_on_exec(STDERR_FILENO, FALSE);
977 quiet = autoq;
979 /* Reset the SIGCHLD handler */
980 act.sa_handler = SIG_DFL;
981 sigemptyset(&act.sa_mask);
982 act.sa_flags = 0;
983 sigaction(SIGCHLD, &act, NULL);
985 message = g_string_new(NULL);
986 close(filedes[0]);
987 close(filedes[3]);
988 to_parent = fdopen(filedes[1], "wb");
989 from_parent = filedes[2];
990 func(data);
991 send_dir("");
992 _exit(0);
995 /* We are the parent */
996 close(filedes[1]);
997 close(filedes[2]);
998 gui_side = g_malloc(sizeof(GUIside));
999 gui_side->from_child = filedes[0];
1000 gui_side->to_child = fdopen(filedes[3], "wb");
1001 gui_side->log = NULL;
1002 gui_side->child = child;
1003 gui_side->errors = 0;
1004 gui_side->show_info = FALSE;
1005 gui_side->preview = NULL;
1006 gui_side->results = NULL;
1007 gui_side->entry = NULL;
1008 gui_side->default_string = NULL;
1010 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1011 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
1012 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
1013 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
1014 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
1016 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
1017 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
1019 gui_side->dir = gtk_label_new(_("<dir>"));
1020 gui_side->next_dir = NULL;
1021 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
1022 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
1024 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
1025 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
1027 gui_side->log = gtk_text_new(NULL, NULL);
1028 gtk_widget_set_usize(gui_side->log, 400, 100);
1029 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1030 gui_side->log, TRUE, TRUE, 0);
1031 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1032 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1033 scrollbar, FALSE, TRUE, 0);
1035 actions = gtk_hbox_new(TRUE, 4);
1036 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1038 gui_side->quiet = button = gtk_toggle_button_new_with_label(_("Quiet"));
1039 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1040 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
1041 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1042 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1043 button_reply, gui_side);
1044 gui_side->yes = button = gtk_button_new_with_label(_("Yes"));
1045 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1046 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1047 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1048 button_reply, gui_side);
1049 gui_side->no = button = gtk_button_new_with_label(_("No"));
1050 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1051 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1052 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1053 button_reply, gui_side);
1054 SENSITIVE_YESNO(gui_side, FALSE);
1056 button = gtk_button_new_with_label(_("Abort"));
1057 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1058 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1059 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1061 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1062 GDK_INPUT_READ,
1063 message_from_child,
1064 gui_side);
1066 return gui_side;
1069 /* ACTIONS ON ONE ITEM */
1071 /* These may call themselves recursively, or ask questions, etc.
1072 * TRUE iff the directory containing dest_path needs to be rescanned.
1075 /* dest_path is the dir containing src_path.
1076 * Updates the global size_tally.
1078 static gboolean do_usage(char *src_path, char *dest_path)
1080 struct stat info;
1082 check_flags();
1084 if (mc_lstat(src_path, &info))
1086 g_string_sprintf(message, "'%s:\n", src_path);
1087 send();
1088 send_error();
1089 return FALSE;
1092 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1093 size_tally += info.st_size;
1094 else if (S_ISDIR(info.st_mode))
1096 g_string_sprintf(message, _("?Count contents of %s?"),
1097 src_path);
1098 if (reply(from_parent, FALSE))
1100 char *safe_path;
1101 safe_path = g_strdup(src_path);
1102 for_dir_contents(do_usage, safe_path, safe_path);
1103 g_free(safe_path);
1107 return FALSE;
1110 /* dest_path is the dir containing src_path */
1111 static gboolean do_delete(char *src_path, char *dest_path)
1113 struct stat info;
1114 gboolean write_prot;
1116 check_flags();
1118 if (mc_lstat(src_path, &info))
1120 send_error();
1121 return FALSE;
1124 write_prot = S_ISLNK(info.st_mode) ? FALSE
1125 : access(src_path, W_OK) != 0;
1126 if (write_prot || !quiet)
1128 g_string_sprintf(message, _("?Delete %s'%s'?"),
1129 write_prot ? _("WRITE-PROTECTED ") : " ",
1130 src_path);
1131 if (!reply(from_parent, write_prot && !o_force))
1132 return FALSE;
1134 else if (!o_brief)
1136 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
1137 send();
1140 if (S_ISDIR(info.st_mode))
1142 char *safe_path;
1143 safe_path = g_strdup(src_path);
1144 for_dir_contents(do_delete, safe_path, safe_path);
1145 if (rmdir(safe_path))
1147 g_free(safe_path);
1148 send_error();
1149 return FALSE;
1151 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
1152 safe_path);
1153 send();
1154 g_string_sprintf(message, "m%s", safe_path);
1155 send();
1156 g_free(safe_path);
1158 else if (unlink(src_path))
1160 send_error();
1161 return FALSE;
1164 return TRUE;
1167 /* path is the item to check. If is is a directory then we may recurse
1168 * (unless prune is used).
1170 static gboolean do_find(char *path, char *dummy)
1172 FindInfo info;
1173 char *slash;
1175 check_flags();
1177 if (!quiet)
1179 g_string_sprintf(message, _("?Check '%s'?"), path);
1180 if (!reply(from_parent, FALSE))
1181 return FALSE;
1184 for (;;)
1186 if (new_entry_string)
1188 if (find_condition)
1189 find_condition_free(find_condition);
1190 find_condition = find_compile(new_entry_string);
1191 g_free(new_entry_string);
1192 new_entry_string = NULL;
1195 if (find_condition)
1196 break;
1198 g_string_assign(message, _("!Invalid find condition - "
1199 "change it and try again\n"));
1200 send();
1201 g_string_sprintf(message, _("?Check '%s'?"), path);
1202 if (!reply(from_parent, TRUE))
1203 return FALSE;
1206 if (mc_lstat(path, &info.stats))
1208 send_error();
1209 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1210 send();
1211 return FALSE;
1214 info.fullpath = path;
1215 time(&info.now); /* XXX: Not for each check! */
1217 slash = strrchr(path, '/');
1218 info.leaf = slash ? slash + 1 : path;
1219 info.prune = FALSE;
1220 if (find_test_condition(find_condition, &info))
1222 g_string_sprintf(message, "=%s", path);
1223 send();
1226 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1228 char *safe_path;
1229 safe_path = g_strdup(path);
1230 for_dir_contents(do_find, safe_path, safe_path);
1231 g_free(safe_path);
1234 return FALSE;
1237 static gboolean do_chmod(char *path, char *dummy)
1239 struct stat info;
1240 mode_t new_mode;
1242 check_flags();
1244 if (mc_lstat(path, &info))
1246 send_error();
1247 return FALSE;
1249 if (S_ISLNK(info.st_mode))
1250 return FALSE;
1252 if (!quiet)
1254 g_string_sprintf(message,
1255 _("?Change permissions of '%s'?"), path);
1256 if (!reply(from_parent, FALSE))
1257 return FALSE;
1259 else if (!o_brief)
1261 g_string_sprintf(message,
1262 _("'Changing permissions of '%s'\n"),
1263 path);
1264 send();
1267 for (;;)
1269 if (new_entry_string)
1271 if (mode_change)
1272 mode_free(mode_change);
1273 mode_change = mode_compile(new_entry_string,
1274 MODE_MASK_ALL);
1275 g_free(new_entry_string);
1276 new_entry_string = NULL;
1279 if (mode_change)
1280 break;
1282 g_string_assign(message,
1283 _("!Invalid mode command - change it and try again\n"));
1284 send();
1285 g_string_sprintf(message,
1286 _("?Change permissions of '%s'?"), path);
1287 if (!reply(from_parent, TRUE))
1288 return FALSE;
1291 if (mc_lstat(path, &info))
1293 send_error();
1294 return FALSE;
1296 if (S_ISLNK(info.st_mode))
1297 return FALSE;
1299 new_mode = mode_adjust(info.st_mode, mode_change);
1300 if (chmod(path, new_mode))
1302 send_error();
1303 return FALSE;
1306 if (o_recurse && S_ISDIR(info.st_mode))
1308 char *safe_path;
1309 safe_path = g_strdup(path);
1310 for_dir_contents(do_chmod, safe_path, safe_path);
1311 g_free(safe_path);
1314 return TRUE;
1317 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1318 * is set then that is the new leafname, otherwise the leafname stays
1319 * the same.
1321 static char *make_dest_path(char *object, char *dir)
1323 char *leaf;
1325 if (action_leaf)
1326 leaf = action_leaf;
1327 else
1329 leaf = strrchr(object, '/');
1330 if (!leaf)
1331 leaf = object; /* Error? */
1332 else
1333 leaf++;
1336 return make_path(dir, leaf)->str;
1339 /* If action_leaf is not NULL it specifies the new leaf name
1341 static gboolean do_copy2(char *path, char *dest)
1343 char *dest_path;
1344 struct stat info;
1345 struct stat dest_info;
1346 gboolean retval = TRUE;
1348 check_flags();
1350 dest_path = make_dest_path(path, dest);
1352 if (mc_lstat(path, &info))
1354 send_error();
1355 return FALSE;
1358 if (mc_lstat(dest_path, &dest_info) == 0)
1360 int err;
1361 gboolean merge;
1363 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1365 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1366 dest_path,
1367 merge ? _("merge contents") : _("overwrite"));
1369 if (!reply(from_parent, TRUE))
1370 return FALSE;
1372 if (!merge)
1374 if (S_ISDIR(dest_info.st_mode))
1375 err = rmdir(dest_path);
1376 else
1377 err = unlink(dest_path);
1379 if (err)
1381 send_error();
1382 if (errno != ENOENT)
1383 return FALSE;
1384 g_string_sprintf(message,
1385 _("'Trying copy anyway...\n"));
1386 send();
1390 else if (!quiet)
1392 g_string_sprintf(message,
1393 _("?Copy %s as %s?"), path, dest_path);
1394 if (!reply(from_parent, FALSE))
1395 return FALSE;
1397 else
1399 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1400 dest_path);
1401 send();
1404 if (S_ISDIR(info.st_mode))
1406 char *safe_path, *safe_dest;
1407 struct stat dest_info;
1408 gboolean exists;
1410 /* (we will do the update ourselves now, rather than
1411 * afterwards)
1413 retval = FALSE;
1415 safe_path = g_strdup(path);
1416 safe_dest = g_strdup(dest_path);
1418 exists = !mc_lstat(dest_path, &dest_info);
1420 if (exists && !S_ISDIR(dest_info.st_mode))
1422 g_string_sprintf(message,
1423 _("!ERROR: Destination already exists, "
1424 "but is not a directory\n"));
1426 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1427 send_error();
1428 else
1430 if (!exists)
1432 /* (just been created then) */
1433 g_string_sprintf(message, "+%s", dest);
1434 send();
1437 action_leaf = NULL;
1438 for_dir_contents(do_copy2, safe_path, safe_dest);
1439 /* Note: dest_path now invalid... */
1442 g_free(safe_path);
1443 g_free(safe_dest);
1445 else if (S_ISLNK(info.st_mode))
1447 char target[MAXPATHLEN + 1];
1448 int count;
1450 /* Not all versions of cp(1) can make symlinks,
1451 * so we special-case it.
1454 count = readlink(path, target, sizeof(target) - 1);
1455 if (count < 0)
1457 send_error();
1458 retval = FALSE;
1460 else
1462 target[count] = '\0';
1463 if (symlink(target, dest_path))
1465 send_error();
1466 retval = FALSE;
1470 else
1472 guchar *error;
1474 error = copy_file(path, dest_path);
1476 if (error)
1478 g_string_sprintf(message, _("!ERROR: %s\n"), error);
1479 send();
1480 retval = FALSE;
1484 return retval;
1487 /* Copy path to dest.
1488 * Check that path not copied into itself.
1490 static gboolean do_copy(char *path, char *dest)
1492 if (is_sub_dir(make_dest_path(path, dest), path))
1494 g_string_sprintf(message,
1495 _("!ERROR: Can't copy object into itself\n"));
1496 send();
1497 return FALSE;
1499 return do_copy2(path, dest);
1502 /* Move path to dest.
1503 * Check that path not moved into itself.
1505 static gboolean do_move(char *path, char *dest)
1507 char *dest_path;
1508 char *leaf;
1509 gboolean retval = TRUE;
1510 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1511 struct stat info2;
1512 gboolean is_dir;
1514 if (is_sub_dir(dest, path))
1516 g_string_sprintf(message,
1517 _("!ERROR: Can't move object into itself\n"));
1518 send();
1519 return FALSE;
1522 check_flags();
1524 leaf = strrchr(path, '/');
1525 if (!leaf)
1526 leaf = path; /* Error? */
1527 else
1528 leaf++;
1530 dest_path = make_path(dest, leaf)->str;
1532 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1534 if (access(dest_path, F_OK) == 0)
1536 struct stat info;
1537 int err;
1539 g_string_sprintf(message,
1540 _("?'%s' already exists - overwrite?"),
1541 dest_path);
1542 if (!reply(from_parent, TRUE))
1543 return FALSE;
1545 if (mc_lstat(dest_path, &info))
1547 send_error();
1548 return FALSE;
1551 if (S_ISDIR(info.st_mode))
1552 err = rmdir(dest_path);
1553 else
1554 err = unlink(dest_path);
1556 if (err)
1558 send_error();
1559 if (errno != ENOENT)
1560 return FALSE;
1561 g_string_sprintf(message,
1562 _("'Trying move anyway...\n"));
1563 send();
1566 else if (!quiet)
1568 g_string_sprintf(message,
1569 _("?Move %s as %s?"), path, dest_path);
1570 if (!reply(from_parent, FALSE))
1571 return FALSE;
1573 else
1575 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1576 dest_path);
1577 send();
1580 argv[2] = path;
1581 argv[3] = dest;
1583 if (fork_exec_wait(argv) == 0)
1585 g_string_sprintf(message, "+%s", path);
1586 g_string_truncate(message, leaf - path);
1587 send();
1588 if (is_dir) {
1589 g_string_sprintf(message, "m%s", path);
1590 send();
1593 else
1595 g_string_sprintf(message,
1596 _("!ERROR: Failed to move %s as %s\n"),
1597 path, dest_path);
1598 send();
1599 retval = FALSE;
1602 return retval;
1605 static gboolean do_link(char *path, char *dest)
1607 char *dest_path;
1608 char *leaf;
1610 check_flags();
1612 leaf = strrchr(path, '/');
1613 if (!leaf)
1614 leaf = path; /* Error? */
1615 else
1616 leaf++;
1618 dest_path = make_path(dest, leaf)->str;
1620 if (quiet)
1622 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1623 dest_path);
1624 send();
1626 else
1628 g_string_sprintf(message,
1629 _("?Link %s as %s?"), path, dest_path);
1630 if (!reply(from_parent, FALSE))
1631 return FALSE;
1634 if (symlink(path, dest_path))
1636 send_error();
1637 return FALSE;
1640 return TRUE;
1643 /* Mount/umount this item */
1644 static void do_mount(FilerWindow *filer_window, DirItem *item)
1646 char *argv[3] = {NULL, NULL, NULL};
1647 gboolean mount = (item->flags & ITEM_FLAG_MOUNTED) == 0;
1649 check_flags();
1651 argv[0] = mount ? "mount" : "umount";
1652 argv[1] = make_path(filer_window->path, item->leafname)->str;
1654 if (quiet)
1656 g_string_sprintf(message,
1657 mount ? _("'Mounting %s\n")
1658 : _("'Unmounting %s\n"),
1659 argv[1]);
1660 send();
1662 else
1664 g_string_sprintf(message,
1665 mount ? _("?Mount %s?\n")
1666 : _("?Unmount %s?\n"),
1667 argv[1]);
1668 if (!reply(from_parent, FALSE))
1669 return;
1672 if (fork_exec_wait(argv) == 0)
1674 g_string_sprintf(message, "+%s", filer_window->path);
1675 send();
1676 g_string_sprintf(message, "m%s", argv[1]);
1677 send();
1679 else
1681 g_string_sprintf(message, _("!ERROR: Mount failed\n"));
1682 send();
1686 /* CHILD MAIN LOOPS */
1688 /* After forking, the child calls one of these functions */
1690 static void usage_cb(gpointer data)
1692 FilerWindow *filer_window = (FilerWindow *) data;
1693 Collection *collection = filer_window->collection;
1694 DirItem *item;
1695 int left = collection->number_selected;
1696 int i = -1;
1697 off_t total_size = 0;
1699 send_dir(filer_window->path);
1701 while (left > 0)
1703 i++;
1704 if (!collection->items[i].selected)
1705 continue;
1706 item = (DirItem *) collection->items[i].data;
1707 size_tally = 0;
1708 do_usage(make_path(filer_window->path,
1709 item->leafname)->str,
1710 filer_window->path);
1711 g_string_sprintf(message, "'%s: %s\n",
1712 item->leafname,
1713 format_size((unsigned long) size_tally));
1714 send();
1715 total_size += size_tally;
1716 left--;
1719 g_string_sprintf(message, _("'\nTotal: %s\n"),
1720 format_size((unsigned long) total_size));
1721 send();
1724 #ifdef DO_MOUNT_POINTS
1725 static void mount_cb(gpointer data)
1727 FilerWindow *filer_window = (FilerWindow *) data;
1728 Collection *collection = filer_window->collection;
1729 DirItem *item;
1730 int i;
1731 gboolean mount_points = FALSE;
1733 send_dir(filer_window->path);
1735 if (mount_item)
1736 do_mount(filer_window, mount_item);
1737 else
1739 for (i = 0; i < collection->number_of_items; i++)
1741 if (!collection->items[i].selected)
1742 continue;
1743 item = (DirItem *) collection->items[i].data;
1744 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1745 continue;
1746 mount_points = TRUE;
1748 do_mount(filer_window, item);
1751 if (!mount_points)
1753 g_string_sprintf(message,
1754 _("!No mount points selected!\n"));
1755 send();
1759 g_string_sprintf(message, _("'\nDone\n"));
1760 send();
1762 #endif
1764 static void delete_cb(gpointer data)
1766 FilerWindow *filer_window = (FilerWindow *) data;
1767 Collection *collection = filer_window->collection;
1768 DirItem *item;
1769 int left = collection->number_selected;
1770 int i = -1;
1772 send_dir(filer_window->path);
1774 while (left > 0)
1776 i++;
1777 if (!collection->items[i].selected)
1778 continue;
1779 item = (DirItem *) collection->items[i].data;
1780 if (do_delete(make_path(filer_window->path,
1781 item->leafname)->str,
1782 filer_window->path))
1784 g_string_sprintf(message, "+%s", filer_window->path);
1785 send();
1787 left--;
1790 g_string_sprintf(message, _("'\nDone\n"));
1791 send();
1794 static void find_cb(gpointer data)
1796 FilerWindow *filer_window = (FilerWindow *) data;
1797 Collection *collection = filer_window->collection;
1798 DirItem *item;
1799 int left = collection->number_selected;
1800 int i = -1;
1802 send_dir(filer_window->path);
1804 while (left > 0)
1806 i++;
1807 if (!collection->items[i].selected)
1808 continue;
1809 item = (DirItem *) collection->items[i].data;
1810 do_find(make_path(filer_window->path,
1811 item->leafname)->str,
1812 NULL);
1813 left--;
1816 g_string_sprintf(message, _("'\nDone\n"));
1817 send();
1820 static void chmod_cb(gpointer data)
1822 FilerWindow *filer_window = (FilerWindow *) data;
1823 Collection *collection = filer_window->collection;
1824 DirItem *item;
1825 int left = collection->number_selected;
1826 int i = -1;
1828 send_dir(filer_window->path);
1830 while (left > 0)
1832 i++;
1833 if (!collection->items[i].selected)
1834 continue;
1835 item = (DirItem *) collection->items[i].data;
1836 if (item->flags & ITEM_FLAG_SYMLINK)
1838 g_string_sprintf(message,
1839 _("!'%s' is a symbolic link\n"),
1840 item->leafname);
1841 send();
1843 else if (do_chmod(make_path(filer_window->path,
1844 item->leafname)->str, NULL))
1846 g_string_sprintf(message, "+%s", filer_window->path);
1847 send();
1849 left--;
1852 g_string_sprintf(message, _("'\nDone\n"));
1853 send();
1856 static void list_cb(gpointer data)
1858 GSList *paths = (GSList *) data;
1860 while (paths)
1862 send_dir((char *) paths->data);
1864 if (action_do_func((char *) paths->data, action_dest))
1866 g_string_sprintf(message, "+%s", action_dest);
1867 send();
1870 paths = paths->next;
1873 g_string_sprintf(message, _("'\nDone\n"));
1874 send();
1877 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1879 GtkWidget *check;
1881 check = gtk_check_button_new_with_label(label);
1882 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1883 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1884 button_reply, gui_side);
1885 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1889 /* EXTERNAL INTERFACE */
1891 void action_find(FilerWindow *filer_window)
1893 GUIside *gui_side;
1894 Collection *collection;
1895 GtkWidget *hbox, *label, *scroller;
1896 gchar *titles[2];
1898 titles[0] = _("Name");
1899 titles[1] = _("Directory");
1901 collection = filer_window->collection;
1903 if (collection->number_selected < 1)
1905 report_error(PROJECT, _("You need to select some items "
1906 "to search through"));
1907 return;
1910 if (!last_find_string)
1911 last_find_string = g_strdup("'core'");
1913 new_entry_string = last_find_string;
1914 gui_side = start_action(filer_window, find_cb, FALSE);
1915 if (!gui_side)
1916 return;
1918 gui_side->show_info = TRUE;
1920 scroller = gtk_scrolled_window_new(NULL, NULL);
1921 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1922 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1923 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1924 gui_side->results = gtk_clist_new_with_titles(
1925 sizeof(titles) / sizeof(*titles), titles);
1926 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1927 gtk_widget_set_usize(gui_side->results, 100, 100);
1928 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1929 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1930 GTK_SELECTION_SINGLE);
1931 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1932 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1933 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1934 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1935 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1937 hbox = gtk_hbox_new(FALSE, 0);
1938 label = gtk_label_new(_("Expression:"));
1939 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1940 gui_side->default_string = &last_find_string;
1941 gui_side->entry = gtk_entry_new();
1942 gtk_widget_set_style(gui_side->entry, fixed_style);
1943 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1944 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1945 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1946 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1947 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1948 entry_changed, gui_side);
1949 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1950 gtk_box_pack_start(GTK_BOX(hbox),
1951 new_help_button(show_condition_help, NULL),
1952 FALSE, TRUE, 4);
1954 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
1955 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1956 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1957 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1958 number_of_windows++;
1959 gtk_widget_show_all(gui_side->window);
1962 /* Count disk space used by selected items */
1963 void action_usage(FilerWindow *filer_window)
1965 GUIside *gui_side;
1966 Collection *collection;
1968 collection = filer_window->collection;
1970 if (collection->number_selected < 1)
1972 report_error(PROJECT,
1973 _("You need to select some items to count"));
1974 return;
1977 gui_side = start_action(filer_window, usage_cb, TRUE);
1978 if (!gui_side)
1979 return;
1981 gui_side->show_info = TRUE;
1983 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
1984 number_of_windows++;
1985 gtk_widget_show_all(gui_side->window);
1988 /* Mount/unmount 'item', or all selected mount points if NULL. */
1989 void action_mount(FilerWindow *filer_window, DirItem *item)
1991 #ifdef DO_MOUNT_POINTS
1992 GUIside *gui_side;
1993 Collection *collection;
1995 collection = filer_window->collection;
1997 if (item == NULL && collection->number_selected < 1)
1999 report_error(PROJECT,
2000 _("You need to select some items to mount or unmount"));
2001 return;
2004 mount_item = item;
2005 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
2006 if (!gui_side)
2007 return;
2009 gtk_window_set_title(GTK_WINDOW(gui_side->window),
2010 _("Mount / Unmount"));
2011 number_of_windows++;
2012 gtk_widget_show_all(gui_side->window);
2013 #else
2014 report_error(PROJECT,
2015 _("ROX-Filer does not yet support mount points on your "
2016 "system. Sorry."));
2017 #endif /* DO_MOUNT_POINTS */
2020 /* Deletes all selected items in the window */
2021 void action_delete(FilerWindow *filer_window)
2023 GUIside *gui_side;
2024 Collection *collection;
2026 collection = filer_window->collection;
2028 if (collection->number_selected < 1)
2030 report_error(PROJECT,
2031 _("You need to select some items to delete"));
2032 return;
2035 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
2036 if (!gui_side)
2037 return;
2039 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
2040 add_toggle(gui_side,
2041 _("Force - don't confirm deletion of non-writeable items"),
2042 "F");
2043 add_toggle(gui_side,
2044 _("Brief - only log directories being deleted"),
2045 "B");
2047 number_of_windows++;
2048 gtk_widget_show_all(gui_side->window);
2051 /* Change the permissions of the selected items */
2052 void action_chmod(FilerWindow *filer_window)
2054 GUIside *gui_side;
2055 Collection *collection;
2056 GtkWidget *hbox, *label;
2058 collection = filer_window->collection;
2060 if (collection->number_selected < 1)
2062 report_error(PROJECT,
2063 _("You need to select the items "
2064 "whose permissions you want to change"));
2065 return;
2068 if (!last_chmod_string)
2069 last_chmod_string = g_strdup("a+x");
2070 new_entry_string = last_chmod_string;
2071 gui_side = start_action(filer_window, chmod_cb, FALSE);
2072 if (!gui_side)
2073 return;
2075 add_toggle(gui_side,
2076 _("Brief - don't list processed files"), "B");
2077 add_toggle(gui_side,
2078 _("Recurse - also change contents of subdirectories"), "R");
2079 hbox = gtk_hbox_new(FALSE, 0);
2080 label = gtk_label_new(_("Command:"));
2081 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2082 gui_side->default_string = &last_chmod_string;
2083 gui_side->entry = gtk_entry_new();
2084 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2085 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2086 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2087 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
2088 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2089 entry_changed, gui_side);
2090 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2091 gtk_box_pack_start(GTK_BOX(hbox),
2092 new_help_button(show_chmod_help, NULL),
2093 FALSE, TRUE, 4);
2095 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2096 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
2097 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2098 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2100 number_of_windows++;
2101 gtk_widget_show_all(gui_side->window);
2104 void action_copy(GSList *paths, char *dest, char *leaf)
2106 GUIside *gui_side;
2108 action_dest = dest;
2109 action_leaf = leaf;
2110 action_do_func = do_copy;
2111 gui_side = start_action(paths, list_cb, o_auto_copy);
2112 if (!gui_side)
2113 return;
2115 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2116 number_of_windows++;
2117 gtk_widget_show_all(gui_side->window);
2120 void action_move(GSList *paths, char *dest)
2122 GUIside *gui_side;
2124 action_dest = dest;
2125 action_do_func = do_move;
2126 gui_side = start_action(paths, list_cb, o_auto_move);
2127 if (!gui_side)
2128 return;
2130 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2131 number_of_windows++;
2132 gtk_widget_show_all(gui_side->window);
2135 void action_link(GSList *paths, char *dest)
2137 GUIside *gui_side;
2139 action_dest = dest;
2140 action_do_func = do_link;
2141 gui_side = start_action(paths, list_cb, o_auto_link);
2142 if (!gui_side)
2143 return;
2145 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2146 number_of_windows++;
2147 gtk_widget_show_all(gui_side->window);
2150 void action_init(void)
2152 options_sections = g_slist_prepend(options_sections, &options);
2153 option_register("action_auto_quiet", action_auto_quiet);