r227: Added support for comparisons.
[rox-filer.git] / ROX-Filer / src / action.c
blobd9bd4f37275c1decce345a8a91ff505b262d941a
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 "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 static GdkColor red = {0, 0xffff, 0, 0};
80 /* Parent->Child messages are one character each:
82 * Q/Y/N Quiet/Yes/No button clicked
83 * F Force deletion of non-writeable items
86 #define SENSITIVE_YESNO(gui_side, state) \
87 do { \
88 gtk_widget_set_sensitive((gui_side)->yes, state); \
89 gtk_widget_set_sensitive((gui_side)->no, state); \
90 if ((gui_side)->entry) \
91 gtk_widget_set_sensitive((gui_side)->entry, state);\
92 } while (0)
94 #define ON(flag) ((flag) ? "on" : "off")
96 typedef struct _GUIside GUIside;
97 typedef void ActionChild(gpointer data);
98 typedef gboolean ForDirCB(char *path, char *dest_path);
100 struct _GUIside
102 int from_child; /* File descriptor */
103 FILE *to_child;
104 int input_tag; /* gdk_input_add() */
105 GtkWidget *vbox, *log, *window, *dir;
106 GtkWidget *quiet, *yes, *no;
107 int child; /* Process ID */
108 int errors;
109 gboolean show_info; /* For Disk Usage */
111 GtkWidget *entry; /* May be NULL */
112 guchar **default_string; /* Changed when the entry changes */
114 char *next_dir; /* NULL => no timer active */
115 gint next_timer;
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 /* This is called whenever the user edits the entry box (if any) - send the
275 * new string.
277 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
279 guchar *text;
281 g_return_if_fail(gui_side->default_string != NULL);
283 text = gtk_entry_get_text(entry);
285 g_free(*(gui_side->default_string));
286 *(gui_side->default_string) = g_strdup(text);
288 if (!gui_side->to_child)
289 return;
291 fputc('E', gui_side->to_child);
292 fputs(text, gui_side->to_child);
293 fputc('\n', gui_side->to_child);
294 fflush(gui_side->to_child);
297 static void show_condition_help(gpointer data)
299 static GtkWidget *help = NULL;
301 if (!help)
303 GtkWidget *text, *vbox, *button, *hbox, *frame;
305 help = gtk_window_new(GTK_WINDOW_DIALOG);
306 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
307 gtk_window_set_title(GTK_WINDOW(help),
308 "Find expression reference");
310 vbox = gtk_vbox_new(FALSE, 0);
311 gtk_container_add(GTK_CONTAINER(help), vbox);
313 frame = gtk_frame_new("Quick Start");
314 text = gtk_label_new(
315 "Just put the name of the file you're looking for in single quotes:\n"
316 "'index.html' (to find a file called 'index.html')");
317 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
318 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
319 gtk_container_add(GTK_CONTAINER(frame), text);
320 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
322 frame = gtk_frame_new("Examples");
323 text = gtk_label_new(
324 "'*.htm', '*.html' (finds HTML files)\n"
325 "IsDir 'lib' (finds directories called 'lib')\n"
326 "IsReg 'core' (finds a regular file called 'core')\n"
327 "! (IsDir, IsReg) (is neither a directory nor a regualr file)\n"
328 "mtime after 1 day ago and size > 1Mb (big, and recently modified)");
329 gtk_widget_set_style(text, fixed_style);
330 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
331 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
332 gtk_container_add(GTK_CONTAINER(frame), text);
333 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
335 frame = gtk_frame_new("Simple Tests");
336 text = gtk_label_new(
337 "IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
338 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
339 "\n"
340 "IsEmpty, IsMine\n"
341 "\n"
342 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
343 "contains a slash then the match is agaist the full path; otherwise it is \n"
344 "agaist the leafname only.");
345 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
346 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
347 gtk_container_add(GTK_CONTAINER(frame), text);
348 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
350 frame = gtk_frame_new("Comparisons");
351 text = gtk_label_new(
352 "<, <=, =, !=, >, >=, After, Before (compare two values)\n"
353 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
354 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
355 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)");
356 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
357 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
358 gtk_container_add(GTK_CONTAINER(frame), text);
359 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
361 hbox = gtk_hbox_new(FALSE, 20);
362 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
364 text = gtk_label_new(
365 "See the ROX-Filer manual for full details.");
366 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
367 button = gtk_button_new_with_label("Close");
368 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
369 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
370 gtk_widget_hide, GTK_OBJECT(help));
372 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
373 gtk_widget_hide, GTK_OBJECT(help));
376 if (GTK_WIDGET_VISIBLE(help))
377 gtk_widget_hide(help);
378 gtk_widget_show_all(help);
381 static void show_chmod_help(gpointer data)
383 static GtkWidget *help = NULL;
385 if (!help)
387 GtkWidget *text, *vbox, *button, *hbox, *sep;
389 help = gtk_window_new(GTK_WINDOW_DIALOG);
390 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
391 gtk_window_set_title(GTK_WINDOW(help),
392 "Permissions command reference");
394 vbox = gtk_vbox_new(FALSE, 0);
395 gtk_container_add(GTK_CONTAINER(help), vbox);
397 text = gtk_label_new(
398 "The format of a command is:\n"
399 "CHANGE, CHANGE, ...\n"
400 "Each CHANGE is:\n"
401 "WHO HOW PERMISSIONS\n"
402 "WHO is some combination of u, g and o which determines whether to\n"
403 "change the permissions for the User (owner), Group or Others.\n"
404 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
405 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
407 "Examples:\n"
408 "u+rw (the file owner gains read and write permission)\n"
409 "g=u (the group permissions are set to be the same as the user's)\n"
410 "o=u-w (others get the same permissions as the owner, but without "
411 "write permission)\n"
412 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
413 "a+X (directories become accessable by everyone; files which were\n"
414 "executable by anyone become executable by everyone)\n"
415 "u+rw, go+r (two commands at once!)\n"
416 "u+s (set the SetUID bit - often has no effect on script files)\n"
417 "755 (set the permissions directly)\n"
419 "\nSee the chmod(1) man page for full details.");
420 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
421 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
423 hbox = gtk_hbox_new(FALSE, 20);
424 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
426 sep = gtk_hseparator_new();
427 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
428 button = gtk_button_new_with_label("Close");
429 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
430 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
431 gtk_widget_hide, GTK_OBJECT(help));
433 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
434 gtk_widget_hide, GTK_OBJECT(help));
437 if (GTK_WIDGET_VISIBLE(help))
438 gtk_widget_hide(help);
439 gtk_widget_show_all(help);
442 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
443 * (or the two are the same directory)
445 static gboolean is_sub_dir(char *sub, char *parent)
447 int parent_len;
448 guchar *real_sub, *real_parent;
449 gboolean retval;
451 real_sub = pathdup(sub);
452 real_parent = pathdup(parent);
454 parent_len = strlen(real_parent);
455 if (strncmp(real_parent, real_sub, parent_len))
456 retval = FALSE;
457 else
459 /* real_sub is at least as long as real_parent and all
460 * characters upto real_parent's length match.
463 retval = real_sub[parent_len] == '\0' ||
464 real_sub[parent_len] == '/';
467 g_free(real_sub);
468 g_free(real_parent);
470 return retval;
473 static gboolean display_dir(gpointer data)
475 GUIside *gui_side = (GUIside *) data;
477 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
478 g_free(gui_side->next_dir);
479 gui_side->next_dir = NULL;
481 return FALSE;
484 /* Called when the child sends us a message */
485 static void message_from_child(gpointer data,
486 gint source,
487 GdkInputCondition condition)
489 char buf[5];
490 GUIside *gui_side = (GUIside *) data;
491 GtkWidget *log = gui_side->log;
493 if (read_exact(source, buf, 4))
495 ssize_t message_len;
496 char *buffer;
498 buf[4] = '\0';
499 message_len = strtol(buf, NULL, 16);
500 buffer = g_malloc(message_len + 1);
501 if (message_len > 0 && read_exact(source, buffer, message_len))
503 buffer[message_len] = '\0';
504 if (*buffer == '?')
505 SENSITIVE_YESNO(gui_side, TRUE);
506 else if (*buffer == '+')
508 refresh_dirs(buffer + 1);
509 g_free(buffer);
510 return;
512 else if (*buffer == 'm')
514 filer_check_mounted(buffer + 1);
515 g_free(buffer);
516 return;
518 else if (*buffer == '/')
520 if (gui_side->next_dir)
521 g_free(gui_side->next_dir);
522 else
523 gui_side->next_timer =
524 gtk_timeout_add(500,
525 display_dir,
526 gui_side);
527 gui_side->next_dir = buffer;
528 return;
530 else if (*buffer == '!')
531 gui_side->errors++;
533 gtk_text_insert(GTK_TEXT(log),
534 NULL,
535 *buffer == '!' ? &red : NULL,
536 NULL,
537 buffer + 1, message_len - 1);
538 g_free(buffer);
539 return;
541 g_print("Child died in the middle of a message.\n");
544 /* The child is dead */
545 gui_side->child = 0;
547 fclose(gui_side->to_child);
548 gui_side->to_child = NULL;
549 close(gui_side->from_child);
550 gdk_input_remove(gui_side->input_tag);
551 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
553 if (gui_side->errors)
555 GString *report;
556 report = g_string_new(NULL);
557 g_string_sprintf(report, "There %s %d error%s.\n",
558 gui_side->errors == 1 ? "was" : "were",
559 gui_side->errors,
560 gui_side->errors == 1 ? "" : "s");
561 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
562 report->str, report->len);
564 g_string_free(report, TRUE);
566 else if (gui_side->show_info == FALSE)
567 gtk_widget_destroy(gui_side->window);
570 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
571 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
573 DIR *d;
574 struct dirent *ent;
575 GSList *list = NULL, *next;
577 d = opendir(src_dir);
578 if (!d)
580 send_error();
581 return;
584 send_dir(src_dir);
586 while ((ent = readdir(d)))
588 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
589 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
590 continue;
591 list = g_slist_append(list, g_strdup(make_path(src_dir,
592 ent->d_name)->str));
594 closedir(d);
596 if (!list)
597 return;
599 next = list;
601 while (next)
603 if (cb((char *) next->data, dest_path))
605 g_string_sprintf(message, "+%s", dest_path);
606 send();
609 g_free(next->data);
610 next = next->next;
612 g_slist_free(list);
613 return;
616 /* Read this many bytes into the buffer. TRUE on success. */
617 static gboolean read_exact(int source, char *buffer, ssize_t len)
619 while (len > 0)
621 ssize_t got;
622 got = read(source, buffer, len);
623 if (got < 1)
624 return FALSE;
625 len -= got;
626 buffer += got;
628 return TRUE;
631 /* Send 'message' to our parent process. TRUE on success. */
632 static gboolean send()
634 char len_buffer[5];
635 ssize_t len;
637 g_return_val_if_fail(message->len < 0xffff, FALSE);
639 sprintf(len_buffer, "%04x", message->len);
640 fwrite(len_buffer, 1, 4, to_parent);
641 len = fwrite(message->str, 1, message->len, to_parent);
642 fflush(to_parent);
643 return len == message->len;
646 /* Set the directory indicator at the top of the window */
647 static gboolean send_dir(char *dir)
649 g_string_sprintf(message, "/%s", dir);
650 return send();
653 static gboolean send_error()
655 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
656 return send();
659 static void button_reply(GtkWidget *button, GUIside *gui_side)
661 char *text;
663 if (!gui_side->to_child)
664 return;
666 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
667 g_return_if_fail(text != NULL);
668 fputc(*text, gui_side->to_child);
669 fflush(gui_side->to_child);
671 if (*text == 'Y' || *text == 'N' || *text == 'Q')
672 SENSITIVE_YESNO(gui_side, FALSE);
675 static void process_flag(char flag)
677 switch (flag)
679 case 'Q':
680 quiet = !quiet;
681 break;
682 case 'F':
683 o_force = !o_force;
684 break;
685 case 'R':
686 o_recurse = !o_recurse;
687 break;
688 case 'B':
689 o_brief = !o_brief;
690 break;
691 default:
692 g_string_sprintf(message,
693 "!ERROR: Bad message '%c'\n", flag);
694 send();
695 break;
699 /* If the parent has sent any flag toggles, read them */
700 static void check_flags(void)
702 fd_set set;
703 int got;
704 char retval;
705 struct timeval tv;
707 FD_ZERO(&set);
709 while (1)
711 FD_SET(from_parent, &set);
712 tv.tv_sec = 0;
713 tv.tv_usec = 0;
714 got = select(from_parent + 1, &set, NULL, NULL, &tv);
716 if (got == -1)
717 g_error("select() failed: %s\n", g_strerror(errno));
718 else if (!got)
719 return;
721 got = read(from_parent, &retval, 1);
722 if (got != 1)
723 g_error("read() error: %s\n", g_strerror(errno));
725 process_flag(retval);
729 static void read_new_entry_text(void)
731 int len;
732 char c;
733 GString *new;
735 new = g_string_new(NULL);
737 for (;;)
739 len = read(from_parent, &c, 1);
740 if (len != 1)
742 fprintf(stderr, "read() error: %s\n",
743 g_strerror(errno));
744 _exit(1); /* Parent died? */
747 if (c == '\n')
748 break;
749 g_string_append_c(new, c);
752 g_free(new_entry_string);
753 new_entry_string = new->str;
754 g_string_free(new, FALSE);
757 /* Read until the user sends a reply. If ignore_quiet is TRUE then
758 * the user MUST click Yes or No, else treat quiet on as Yes.
759 * If the user needs prompting then does send().
761 static gboolean reply(int fd, gboolean ignore_quiet)
763 ssize_t len;
764 char retval;
765 gboolean asked = FALSE;
767 while (ignore_quiet || !quiet)
769 if (!asked)
771 send();
772 asked = TRUE;
775 len = read(fd, &retval, 1);
776 if (len != 1)
778 fprintf(stderr, "read() error: %s\n",
779 g_strerror(errno));
780 _exit(1); /* Parent died? */
783 switch (retval)
785 case 'Q':
786 quiet = !quiet;
787 if (ignore_quiet)
789 g_string_assign(message, "?");
790 send();
792 break;
793 case 'Y':
794 g_string_assign(message, "' Yes\n");
795 send();
796 return TRUE;
797 case 'N':
798 g_string_assign(message, "' No\n");
799 send();
800 return FALSE;
801 case 'E':
802 read_new_entry_text();
803 break;
804 default:
805 process_flag(retval);
806 break;
810 if (asked)
812 g_string_assign(message, "' Quiet\n");
813 send();
815 return TRUE;
818 static void destroy_action_window(GtkWidget *widget, gpointer data)
820 GUIside *gui_side = (GUIside *) data;
822 if (gui_side->child)
824 kill(gui_side->child, SIGTERM);
825 fclose(gui_side->to_child);
826 close(gui_side->from_child);
827 gdk_input_remove(gui_side->input_tag);
830 if (gui_side->next_dir)
832 gtk_timeout_remove(gui_side->next_timer);
833 g_free(gui_side->next_dir);
835 g_free(gui_side);
837 if (--number_of_windows < 1)
838 gtk_main_quit();
841 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
842 * (NULL on failure). The child calls func().
844 * If autoq then automatically selects 'Quiet'.
846 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
848 int filedes[4]; /* 0 and 2 are for reading */
849 GUIside *gui_side;
850 int child;
851 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
852 struct sigaction act;
854 if (pipe(filedes))
856 report_error("ROX-Filer", g_strerror(errno));
857 return NULL;
860 if (pipe(filedes + 2))
862 close(filedes[0]);
863 close(filedes[1]);
864 report_error("ROX-Filer", g_strerror(errno));
865 return NULL;
868 child = fork();
869 switch (child)
871 case -1:
872 report_error("ROX-Filer", g_strerror(errno));
873 return NULL;
874 case 0:
875 /* We are the child */
877 quiet = autoq;
879 /* Reset the SIGCHLD handler */
880 act.sa_handler = SIG_DFL;
881 sigemptyset(&act.sa_mask);
882 act.sa_flags = 0;
883 sigaction(SIGCHLD, &act, NULL);
885 message = g_string_new(NULL);
886 close(filedes[0]);
887 close(filedes[3]);
888 to_parent = fdopen(filedes[1], "wb");
889 from_parent = filedes[2];
890 func(data);
891 _exit(0);
894 /* We are the parent */
895 close(filedes[1]);
896 close(filedes[2]);
897 gui_side = g_malloc(sizeof(GUIside));
898 gui_side->from_child = filedes[0];
899 gui_side->to_child = fdopen(filedes[3], "wb");
900 gui_side->log = NULL;
901 gui_side->child = child;
902 gui_side->errors = 0;
903 gui_side->show_info = FALSE;
904 gui_side->entry = NULL;
905 gui_side->default_string = NULL;
907 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
908 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
909 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
910 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
911 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
913 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
914 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
916 gui_side->dir = gtk_label_new("<dir>");
917 gui_side->next_dir = NULL;
918 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
919 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
921 hbox = gtk_hbox_new(FALSE, 0);
922 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4);
924 gui_side->log = gtk_text_new(NULL, NULL);
925 gtk_widget_set_usize(gui_side->log, 400, 100);
926 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
927 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
928 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
930 actions = gtk_hbox_new(TRUE, 4);
931 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
933 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
934 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
935 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
936 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
937 gtk_signal_connect(GTK_OBJECT(button), "clicked",
938 button_reply, gui_side);
939 gui_side->yes = button = gtk_button_new_with_label("Yes");
940 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
941 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
942 gtk_signal_connect(GTK_OBJECT(button), "clicked",
943 button_reply, gui_side);
944 gui_side->no = button = gtk_button_new_with_label("No");
945 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
946 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
947 gtk_signal_connect(GTK_OBJECT(button), "clicked",
948 button_reply, gui_side);
949 SENSITIVE_YESNO(gui_side, FALSE);
951 button = gtk_button_new_with_label("Abort");
952 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
953 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
954 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
956 gui_side->input_tag = gdk_input_add(gui_side->from_child,
957 GDK_INPUT_READ,
958 message_from_child,
959 gui_side);
961 return gui_side;
964 /* ACTIONS ON ONE ITEM */
966 /* These may call themselves recursively, or ask questions, etc.
967 * TRUE iff the directory containing dest_path needs to be rescanned.
970 /* dest_path is the dir containing src_path.
971 * Updates the global size_tally.
973 static gboolean do_usage(char *src_path, char *dest_path)
975 struct stat info;
977 check_flags();
979 if (lstat(src_path, &info))
981 g_string_sprintf(message, "'%s:\n", src_path);
982 send();
983 send_error();
984 return FALSE;
987 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
988 size_tally += info.st_size;
989 else if (S_ISDIR(info.st_mode))
991 g_string_sprintf(message, "?Count contents of %s?",
992 src_path);
993 if (reply(from_parent, FALSE))
995 char *safe_path;
996 safe_path = g_strdup(src_path);
997 for_dir_contents(do_usage, safe_path, safe_path);
998 g_free(safe_path);
1002 return FALSE;
1005 /* dest_path is the dir containing src_path */
1006 static gboolean do_delete(char *src_path, char *dest_path)
1008 struct stat info;
1009 gboolean write_prot;
1011 check_flags();
1013 if (lstat(src_path, &info))
1015 send_error();
1016 return FALSE;
1019 write_prot = S_ISLNK(info.st_mode) ? FALSE
1020 : access(src_path, W_OK) != 0;
1021 if (write_prot || !quiet)
1023 g_string_sprintf(message, "?Delete %s'%s'?",
1024 write_prot ? "WRITE-PROTECTED " : " ",
1025 src_path);
1026 if (!reply(from_parent, write_prot && !o_force))
1027 return FALSE;
1029 else if (!o_brief)
1031 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
1032 send();
1035 if (S_ISDIR(info.st_mode))
1037 char *safe_path;
1038 safe_path = g_strdup(src_path);
1039 for_dir_contents(do_delete, safe_path, safe_path);
1040 if (rmdir(safe_path))
1042 g_free(safe_path);
1043 send_error();
1044 return FALSE;
1046 g_string_sprintf(message, "'Directory '%s' deleted\n",
1047 safe_path);
1048 send();
1049 g_string_sprintf(message, "m%s", safe_path);
1050 send();
1051 g_free(safe_path);
1053 else if (unlink(src_path))
1055 send_error();
1056 return FALSE;
1059 return TRUE;
1062 /* path is the item to check. If is is a directory then we may recurse
1063 * (unless prune is used).
1065 static gboolean do_find(char *path, char *dummy)
1067 struct stat info;
1069 check_flags();
1071 if (!quiet)
1073 g_string_sprintf(message, "?Check '%s'?", path);
1074 if (!reply(from_parent, FALSE))
1075 return FALSE;
1078 for (;;)
1080 if (new_entry_string)
1082 if (find_condition)
1083 find_condition_free(find_condition);
1084 find_condition = find_compile(new_entry_string);
1085 g_free(new_entry_string);
1086 new_entry_string = NULL;
1089 if (find_condition)
1090 break;
1092 g_string_assign(message,
1093 "!Invalid find condition - change it and try again\n");
1094 send();
1095 g_string_sprintf(message, "?Check '%s'?", path);
1096 if (!reply(from_parent, TRUE))
1097 return FALSE;
1100 if (lstat(path, &info))
1102 send_error();
1103 g_string_sprintf(message, "'(while checking '%s')\n", path);
1104 send();
1105 return FALSE;
1108 if (find_test_condition(find_condition, path))
1110 g_string_sprintf(message, "'Found '%s'\n", path);
1111 send();
1114 if (S_ISDIR(info.st_mode))
1116 char *safe_path;
1117 safe_path = g_strdup(path);
1118 for_dir_contents(do_find, safe_path, safe_path);
1119 g_free(safe_path);
1122 return FALSE;
1125 static gboolean do_chmod(char *path, char *dummy)
1127 struct stat info;
1128 mode_t new_mode;
1130 check_flags();
1132 if (lstat(path, &info))
1134 send_error();
1135 return FALSE;
1137 if (S_ISLNK(info.st_mode))
1138 return FALSE;
1140 if (!quiet)
1142 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1143 if (!reply(from_parent, FALSE))
1144 return FALSE;
1146 else if (!o_brief)
1148 g_string_sprintf(message, "'Changing permissions of '%s'\n",
1149 path);
1150 send();
1153 for (;;)
1155 if (new_entry_string)
1157 if (mode_change)
1158 mode_free(mode_change);
1159 mode_change = mode_compile(new_entry_string,
1160 MODE_MASK_ALL);
1161 g_free(new_entry_string);
1162 new_entry_string = NULL;
1165 if (mode_change)
1166 break;
1168 g_string_assign(message,
1169 "!Invalid mode command - change it and try again\n");
1170 send();
1171 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1172 if (!reply(from_parent, TRUE))
1173 return FALSE;
1176 if (lstat(path, &info))
1178 send_error();
1179 return FALSE;
1181 if (S_ISLNK(info.st_mode))
1182 return FALSE;
1184 new_mode = mode_adjust(info.st_mode, mode_change);
1185 if (chmod(path, new_mode))
1187 send_error();
1188 return FALSE;
1191 if (o_recurse && S_ISDIR(info.st_mode))
1193 char *safe_path;
1194 safe_path = g_strdup(path);
1195 for_dir_contents(do_chmod, safe_path, safe_path);
1196 g_free(safe_path);
1199 return TRUE;
1202 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1203 * is set then that is the new leafname, otherwise the leafname stays
1204 * the same.
1206 static char *make_dest_path(char *object, char *dir)
1208 char *leaf;
1210 if (action_leaf)
1211 leaf = action_leaf;
1212 else
1214 leaf = strrchr(object, '/');
1215 if (!leaf)
1216 leaf = object; /* Error? */
1217 else
1218 leaf++;
1221 return make_path(dir, leaf)->str;
1224 /* If action_leaf is not NULL it specifies the new leaf name
1226 static gboolean do_copy2(char *path, char *dest)
1228 char *dest_path;
1229 struct stat info;
1230 struct stat dest_info;
1231 gboolean retval = TRUE;
1233 check_flags();
1235 dest_path = make_dest_path(path, dest);
1237 if (lstat(path, &info))
1239 send_error();
1240 return FALSE;
1243 if (lstat(dest_path, &dest_info) == 0)
1245 int err;
1246 gboolean merge;
1248 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1250 g_string_sprintf(message, "?'%s' already exists - %s?",
1251 dest_path,
1252 merge ? "merge contents" : "overwrite");
1254 if (!reply(from_parent, TRUE))
1255 return FALSE;
1257 if (!merge)
1259 if (S_ISDIR(dest_info.st_mode))
1260 err = rmdir(dest_path);
1261 else
1262 err = unlink(dest_path);
1264 if (err)
1266 send_error();
1267 if (errno != ENOENT)
1268 return FALSE;
1269 g_string_sprintf(message,
1270 "'Trying copy anyway...\n");
1271 send();
1275 else if (!quiet)
1277 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1278 if (!reply(from_parent, FALSE))
1279 return FALSE;
1281 else
1283 g_string_sprintf(message, "'Copying %s as %s\n", path,
1284 dest_path);
1285 send();
1288 if (S_ISDIR(info.st_mode))
1290 char *safe_path, *safe_dest;
1291 struct stat dest_info;
1292 gboolean exists;
1294 /* (we will do the update ourselves now, rather than
1295 * afterwards)
1297 retval = FALSE;
1299 safe_path = g_strdup(path);
1300 safe_dest = g_strdup(dest_path);
1302 exists = !lstat(dest_path, &dest_info);
1304 if (exists && !S_ISDIR(dest_info.st_mode))
1306 g_string_sprintf(message,
1307 "!ERROR: Destination already exists, "
1308 "but is not a directory\n");
1310 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1311 send_error();
1312 else
1314 if (!exists)
1316 /* (just been created then) */
1317 g_string_sprintf(message, "+%s", dest);
1318 send();
1321 action_leaf = NULL;
1322 for_dir_contents(do_copy2, safe_path, safe_dest);
1323 /* Note: dest_path now invalid... */
1326 g_free(safe_path);
1327 g_free(safe_dest);
1329 else if (S_ISLNK(info.st_mode))
1331 char target[MAXPATHLEN + 1];
1332 int count;
1334 /* Not all versions of cp(1) can make symlinks,
1335 * so we special-case it.
1338 count = readlink(path, target, sizeof(target) - 1);
1339 if (count < 0)
1341 send_error();
1342 retval = FALSE;
1344 else
1346 target[count] = '\0';
1347 if (symlink(target, dest_path))
1349 send_error();
1350 retval = FALSE;
1354 else
1356 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
1358 argv[2] = path;
1359 argv[3] = dest_path;
1361 if (fork_exec_wait(argv))
1363 g_string_sprintf(message, "!ERROR: Copy failed\n");
1364 send();
1365 retval = FALSE;
1369 return retval;
1372 /* Copy path to dest.
1373 * Check that path not copied into itself.
1375 static gboolean do_copy(char *path, char *dest)
1377 if (is_sub_dir(make_dest_path(path, dest), path))
1379 g_string_sprintf(message,
1380 "!ERROR: Can't copy directory into itself\n");
1381 send();
1382 return FALSE;
1384 return do_copy2(path, dest);
1387 /* Move path to dest.
1388 * Check that path not moved into itself.
1390 static gboolean do_move(char *path, char *dest)
1392 char *dest_path;
1393 char *leaf;
1394 gboolean retval = TRUE;
1395 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1396 struct stat info2;
1397 gboolean is_dir;
1399 if (is_sub_dir(dest, path))
1401 g_string_sprintf(message,
1402 "!ERROR: Can't move directory into itself\n");
1403 send();
1404 return FALSE;
1407 check_flags();
1409 leaf = strrchr(path, '/');
1410 if (!leaf)
1411 leaf = path; /* Error? */
1412 else
1413 leaf++;
1415 dest_path = make_path(dest, leaf)->str;
1417 is_dir = lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1419 if (access(dest_path, F_OK) == 0)
1421 struct stat info;
1422 int err;
1424 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1425 dest_path);
1426 if (!reply(from_parent, TRUE))
1427 return FALSE;
1429 if (lstat(dest_path, &info))
1431 send_error();
1432 return FALSE;
1435 if (S_ISDIR(info.st_mode))
1436 err = rmdir(dest_path);
1437 else
1438 err = unlink(dest_path);
1440 if (err)
1442 send_error();
1443 if (errno != ENOENT)
1444 return FALSE;
1445 g_string_sprintf(message,
1446 "'Trying move anyway...\n");
1447 send();
1450 else if (!quiet)
1452 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1453 if (!reply(from_parent, FALSE))
1454 return FALSE;
1456 else
1458 g_string_sprintf(message, "'Moving %s as %s\n", path,
1459 dest_path);
1460 send();
1463 argv[2] = path;
1464 argv[3] = dest;
1466 if (fork_exec_wait(argv) == 0)
1468 g_string_sprintf(message, "+%s", path);
1469 g_string_truncate(message, leaf - path);
1470 send();
1471 if (is_dir) {
1472 g_string_sprintf(message, "m%s", path);
1473 send();
1476 else
1478 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1479 path, dest_path);
1480 send();
1481 retval = FALSE;
1484 return retval;
1487 static gboolean do_link(char *path, char *dest)
1489 char *dest_path;
1490 char *leaf;
1492 check_flags();
1494 leaf = strrchr(path, '/');
1495 if (!leaf)
1496 leaf = path; /* Error? */
1497 else
1498 leaf++;
1500 dest_path = make_path(dest, leaf)->str;
1502 if (quiet)
1504 g_string_sprintf(message, "'Linking %s as %s\n", path,
1505 dest_path);
1506 send();
1508 else
1510 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1511 if (!reply(from_parent, FALSE))
1512 return FALSE;
1515 if (symlink(path, dest_path))
1517 send_error();
1518 return FALSE;
1521 return TRUE;
1524 /* Mount/umount this item */
1525 static void do_mount(FilerWindow *filer_window, DirItem *item)
1527 char *argv[3] = {NULL, NULL, NULL};
1528 char *action;
1530 check_flags();
1532 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1533 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1534 argv[1] = make_path(filer_window->path, item->leafname)->str;
1536 if (quiet)
1538 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1539 send();
1541 else
1543 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1544 if (!reply(from_parent, FALSE))
1545 return;
1548 if (fork_exec_wait(argv) == 0)
1550 g_string_sprintf(message, "+%s", filer_window->path);
1551 send();
1552 g_string_sprintf(message, "m%s", argv[1]);
1553 send();
1555 else
1557 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1558 send();
1562 /* CHILD MAIN LOOPS */
1564 /* After forking, the child calls one of these functions */
1566 static void usage_cb(gpointer data)
1568 FilerWindow *filer_window = (FilerWindow *) data;
1569 Collection *collection = filer_window->collection;
1570 DirItem *item;
1571 int left = collection->number_selected;
1572 int i = -1;
1573 off_t total_size = 0;
1575 send_dir(filer_window->path);
1577 while (left > 0)
1579 i++;
1580 if (!collection->items[i].selected)
1581 continue;
1582 item = (DirItem *) collection->items[i].data;
1583 size_tally = 0;
1584 do_usage(make_path(filer_window->path,
1585 item->leafname)->str,
1586 filer_window->path);
1587 g_string_sprintf(message, "'%s: %s\n",
1588 item->leafname,
1589 format_size((unsigned long) size_tally));
1590 send();
1591 total_size += size_tally;
1592 left--;
1595 g_string_sprintf(message, "'\nTotal: %s\n",
1596 format_size((unsigned long) total_size));
1597 send();
1600 #ifdef DO_MOUNT_POINTS
1601 static void mount_cb(gpointer data)
1603 FilerWindow *filer_window = (FilerWindow *) data;
1604 Collection *collection = filer_window->collection;
1605 DirItem *item;
1606 int i;
1607 gboolean mount_points = FALSE;
1609 send_dir(filer_window->path);
1611 if (mount_item)
1612 do_mount(filer_window, mount_item);
1613 else
1615 for (i = 0; i < collection->number_of_items; i++)
1617 if (!collection->items[i].selected)
1618 continue;
1619 item = (DirItem *) collection->items[i].data;
1620 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1621 continue;
1622 mount_points = TRUE;
1624 do_mount(filer_window, item);
1627 if (!mount_points)
1629 g_string_sprintf(message,
1630 "!No mount points selected!\n");
1631 send();
1635 g_string_sprintf(message, "'\nDone\n");
1636 send();
1638 #endif
1640 static void delete_cb(gpointer data)
1642 FilerWindow *filer_window = (FilerWindow *) data;
1643 Collection *collection = filer_window->collection;
1644 DirItem *item;
1645 int left = collection->number_selected;
1646 int i = -1;
1648 send_dir(filer_window->path);
1650 while (left > 0)
1652 i++;
1653 if (!collection->items[i].selected)
1654 continue;
1655 item = (DirItem *) collection->items[i].data;
1656 if (do_delete(make_path(filer_window->path,
1657 item->leafname)->str,
1658 filer_window->path))
1660 g_string_sprintf(message, "+%s", filer_window->path);
1661 send();
1663 left--;
1666 g_string_sprintf(message, "'\nDone\n");
1667 send();
1670 static void find_cb(gpointer data)
1672 FilerWindow *filer_window = (FilerWindow *) data;
1673 Collection *collection = filer_window->collection;
1674 DirItem *item;
1675 int left = collection->number_selected;
1676 int i = -1;
1678 send_dir(filer_window->path);
1680 while (left > 0)
1682 i++;
1683 if (!collection->items[i].selected)
1684 continue;
1685 item = (DirItem *) collection->items[i].data;
1686 do_find(make_path(filer_window->path,
1687 item->leafname)->str,
1688 NULL);
1689 left--;
1692 g_string_sprintf(message, "'\nDone\n");
1693 send();
1696 static void chmod_cb(gpointer data)
1698 FilerWindow *filer_window = (FilerWindow *) data;
1699 Collection *collection = filer_window->collection;
1700 DirItem *item;
1701 int left = collection->number_selected;
1702 int i = -1;
1704 send_dir(filer_window->path);
1706 while (left > 0)
1708 i++;
1709 if (!collection->items[i].selected)
1710 continue;
1711 item = (DirItem *) collection->items[i].data;
1712 if (item->flags & ITEM_FLAG_SYMLINK)
1714 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1715 item->leafname);
1716 send();
1718 else if (do_chmod(make_path(filer_window->path,
1719 item->leafname)->str, NULL))
1721 g_string_sprintf(message, "+%s", filer_window->path);
1722 send();
1724 left--;
1727 g_string_sprintf(message, "'\nDone\n");
1728 send();
1731 static void list_cb(gpointer data)
1733 GSList *paths = (GSList *) data;
1735 while (paths)
1737 send_dir((char *) paths->data);
1739 if (action_do_func((char *) paths->data, action_dest))
1741 g_string_sprintf(message, "+%s", action_dest);
1742 send();
1745 paths = paths->next;
1748 g_string_sprintf(message, "'\nDone\n");
1749 send();
1752 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1754 GtkWidget *check;
1756 check = gtk_check_button_new_with_label(label);
1757 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1758 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1759 button_reply, gui_side);
1760 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1764 /* EXTERNAL INTERFACE */
1766 void action_find(FilerWindow *filer_window)
1768 GUIside *gui_side;
1769 Collection *collection;
1770 GtkWidget *hbox, *label, *button;
1772 collection = filer_window->collection;
1774 if (collection->number_selected < 1)
1776 report_error("ROX-Filer", "You need to select some items "
1777 "to search through");
1778 return;
1781 if (!last_find_string)
1782 last_find_string = g_strdup("'core'");
1784 new_entry_string = last_find_string;
1785 gui_side = start_action(filer_window, find_cb, FALSE);
1786 if (!gui_side)
1787 return;
1789 gui_side->show_info = TRUE;
1791 hbox = gtk_hbox_new(FALSE, 0);
1792 label = gtk_label_new("Expression:");
1793 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1794 gui_side->default_string = &last_find_string;
1795 gui_side->entry = gtk_entry_new();
1796 gtk_widget_set_style(gui_side->entry, fixed_style);
1797 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1798 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1799 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1800 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1801 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1802 entry_changed, gui_side);
1803 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1804 button = gtk_button_new_with_label("Show expression reference");
1805 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1806 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1807 show_condition_help, NULL);
1809 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Find");
1810 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1811 number_of_windows++;
1812 gtk_widget_show_all(gui_side->window);
1815 /* Count disk space used by selected items */
1816 void action_usage(FilerWindow *filer_window)
1818 GUIside *gui_side;
1819 Collection *collection;
1821 collection = filer_window->collection;
1823 if (collection->number_selected < 1)
1825 report_error("ROX-Filer", "You need to select some items "
1826 "to count");
1827 return;
1830 gui_side = start_action(filer_window, usage_cb, TRUE);
1831 if (!gui_side)
1832 return;
1834 gui_side->show_info = TRUE;
1836 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1837 number_of_windows++;
1838 gtk_widget_show_all(gui_side->window);
1841 /* Mount/unmount 'item', or all selected mount points if NULL. */
1842 void action_mount(FilerWindow *filer_window, DirItem *item)
1844 #ifdef DO_MOUNT_POINTS
1845 GUIside *gui_side;
1846 Collection *collection;
1848 collection = filer_window->collection;
1850 if (item == NULL && collection->number_selected < 1)
1852 report_error("ROX-Filer", "You need to select some items "
1853 "to mount or unmount");
1854 return;
1857 mount_item = item;
1858 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1859 if (!gui_side)
1860 return;
1862 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1863 number_of_windows++;
1864 gtk_widget_show_all(gui_side->window);
1865 #else
1866 report_error("ROX-Filer",
1867 "ROX-Filer does not yet support mount points on your "
1868 "system. Sorry.");
1869 #endif /* DO_MOUNT_POINTS */
1872 /* Deletes all selected items in the window */
1873 void action_delete(FilerWindow *filer_window)
1875 GUIside *gui_side;
1876 Collection *collection;
1878 collection = filer_window->collection;
1880 if (collection->number_selected < 1)
1882 report_error("ROX-Filer", "You need to select some items "
1883 "to delete");
1884 return;
1887 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1888 if (!gui_side)
1889 return;
1891 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1892 add_toggle(gui_side,
1893 "Force - don't confirm deletion of non-writeable items", "F");
1894 add_toggle(gui_side,
1895 "Brief - only log directories being deleted", "B");
1897 number_of_windows++;
1898 gtk_widget_show_all(gui_side->window);
1901 /* Change the permissions of the selected items */
1902 void action_chmod(FilerWindow *filer_window)
1904 GUIside *gui_side;
1905 Collection *collection;
1906 GtkWidget *hbox, *label, *button;
1908 collection = filer_window->collection;
1910 if (collection->number_selected < 1)
1912 report_error("ROX-Filer", "You need to select the items "
1913 "whose permissions you want to change");
1914 return;
1917 if (!last_chmod_string)
1918 last_chmod_string = g_strdup("a+x");
1919 new_entry_string = last_chmod_string;
1920 gui_side = start_action(filer_window, chmod_cb, FALSE);
1921 if (!gui_side)
1922 return;
1924 add_toggle(gui_side,
1925 "Brief - don't list processed files", "B");
1926 add_toggle(gui_side,
1927 "Recurse - also change contents of subdirectories", "R");
1928 hbox = gtk_hbox_new(FALSE, 0);
1929 label = gtk_label_new("Command:");
1930 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1931 gui_side->default_string = &last_chmod_string;
1932 gui_side->entry = gtk_entry_new();
1933 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
1934 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1935 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1936 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1937 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1938 entry_changed, gui_side);
1939 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
1940 button = gtk_button_new_with_label("Show command reference");
1941 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1942 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1943 show_chmod_help, NULL);
1945 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1946 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
1948 number_of_windows++;
1949 gtk_widget_show_all(gui_side->window);
1952 void action_copy(GSList *paths, char *dest, char *leaf)
1954 GUIside *gui_side;
1956 action_dest = dest;
1957 action_leaf = leaf;
1958 action_do_func = do_copy;
1959 gui_side = start_action(paths, list_cb, o_auto_copy);
1960 if (!gui_side)
1961 return;
1963 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1964 number_of_windows++;
1965 gtk_widget_show_all(gui_side->window);
1968 void action_move(GSList *paths, char *dest)
1970 GUIside *gui_side;
1972 action_dest = dest;
1973 action_do_func = do_move;
1974 gui_side = start_action(paths, list_cb, o_auto_move);
1975 if (!gui_side)
1976 return;
1978 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1979 number_of_windows++;
1980 gtk_widget_show_all(gui_side->window);
1983 void action_link(GSList *paths, char *dest)
1985 GUIside *gui_side;
1987 action_dest = dest;
1988 action_do_func = do_link;
1989 gui_side = start_action(paths, list_cb, o_auto_link);
1990 if (!gui_side)
1991 return;
1993 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1994 number_of_windows++;
1995 gtk_widget_show_all(gui_side->window);
1998 void action_init(void)
2000 options_sections = g_slist_prepend(options_sections, &options);
2001 option_register("action_auto_quiet", action_auto_quiet);