r228: Added a nice list displaying the found files. Clicking opens a preview window
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob430ab89162a0bf8afbea75a32ff8ca8c37d658d1
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, *log_hbox;
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;
117 /* Used by Find */
118 FilerWindow *preview;
119 GtkWidget *results;
122 /* These don't need to be in a structure because we fork() before
123 * using them again.
125 static int from_parent = 0;
126 static FILE *to_parent = NULL;
127 static gboolean quiet = FALSE;
128 static GString *message = NULL;
129 static char *action_dest = NULL;
130 static char *action_leaf = NULL;
131 static gboolean (*action_do_func)(char *source, char *dest);
132 static size_t size_tally; /* For Disk Usage */
133 static DirItem *mount_item;
135 static struct mode_change *mode_change = NULL; /* For Permissions */
136 static FindCondition *find_condition = NULL; /* For Find */
138 static gboolean o_force = FALSE;
139 static gboolean o_brief = FALSE;
140 static gboolean o_recurse = FALSE;
142 /* Whenever the text in these boxes is changed we store a copy of the new
143 * string to be used as the default next time.
145 static guchar *last_chmod_string = NULL;
146 static guchar *last_find_string = NULL;
148 /* Set to one of the above before forking. This may change over a call to
149 * reply(). It is reset to NULL once the text is parsed.
151 static guchar *new_entry_string = NULL;
153 /* Static prototypes */
154 static gboolean send();
155 static gboolean send_error();
156 static gboolean send_dir(char *dir);
157 static gboolean read_exact(int source, char *buffer, ssize_t len);
158 static void do_mount(FilerWindow *filer_window, DirItem *item);
159 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
160 static gboolean reply(int fd, gboolean ignore_quiet);
162 /* OPTIONS */
164 /* Build up some option widgets to go in the options dialog, but don't
165 * fill them in yet.
167 static GtkWidget *create_options()
169 GtkWidget *vbox, *hbox, *label;
171 vbox = gtk_vbox_new(FALSE, 0);
172 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
174 label = gtk_label_new("Auto-start (Quiet) these actions:");
175 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
177 hbox = gtk_hbox_new(TRUE, 0);
178 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
180 w_auto_copy = gtk_check_button_new_with_label("Copy");
181 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
182 w_auto_move = gtk_check_button_new_with_label("Move");
183 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
184 w_auto_link = gtk_check_button_new_with_label("Link");
185 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
186 w_auto_delete = gtk_check_button_new_with_label("Delete");
187 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
188 w_auto_mount = gtk_check_button_new_with_label("Mount");
189 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
191 return vbox;
194 /* Reflect current state by changing the widgets in the options box */
195 static void update_options()
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
198 o_auto_copy);
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
200 o_auto_move);
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
202 o_auto_link);
203 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
204 o_auto_delete);
205 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
206 o_auto_mount);
209 /* Set current values by reading the states of the widgets in the options box */
210 static void set_options()
212 o_auto_copy = gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_copy));
214 o_auto_move = gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_move));
216 o_auto_link = gtk_toggle_button_get_active(
217 GTK_TOGGLE_BUTTON(w_auto_link));
218 o_auto_delete = gtk_toggle_button_get_active(
219 GTK_TOGGLE_BUTTON(w_auto_delete));
220 o_auto_mount = gtk_toggle_button_get_active(
221 GTK_TOGGLE_BUTTON(w_auto_mount));
224 static void save_options()
226 guchar str[] = "cmldt";
228 if (o_auto_copy)
229 str[0] = 'C';
230 if (o_auto_move)
231 str[1] = 'M';
232 if (o_auto_link)
233 str[2] = 'L';
234 if (o_auto_delete)
235 str[3] = 'D';
236 if (o_auto_mount)
237 str[4] = 'T';
239 option_write("action_auto_quiet", str);
242 static char *action_auto_quiet(char *data)
244 while (*data)
246 char c = *data++;
247 gboolean state;
249 state = isupper(c);
251 switch (tolower(c))
253 case 'c':
254 o_auto_copy = state;
255 break;
256 case 'm':
257 o_auto_move = state;
258 break;
259 case 'l':
260 o_auto_link = state;
261 break;
262 case 'd':
263 o_auto_delete = state;
264 break;
265 case 't':
266 o_auto_mount = state;
267 break;
268 default:
269 return "Unknown flag";
273 return NULL;
276 /* SUPPORT */
278 static void preview_closed(GtkWidget *window, GUIside *gui_side)
280 gui_side->preview = NULL;
283 static void select_row_callback(GtkWidget *widget,
284 gint row, gint column,
285 GdkEventButton *event,
286 GUIside *gui_side)
288 gchar *leaf, *dir;
290 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
291 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
293 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
295 if (gui_side->preview)
297 if (strcmp(gui_side->preview->path, dir) == 0)
298 filer_set_autoselect(gui_side->preview, leaf);
299 else
300 filer_change_to(gui_side->preview, dir, leaf);
301 return;
304 gui_side->preview = filer_opendir(dir, PANEL_NO);
305 filer_set_autoselect(gui_side->preview, leaf);
306 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window), "destroy",
307 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 static 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 regualr file)\n"
365 "mtime after 1 day ago and size > 1Mb (big, and recently modified)");
366 gtk_widget_set_style(text, fixed_style);
367 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
368 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
369 gtk_container_add(GTK_CONTAINER(frame), text);
370 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
372 frame = gtk_frame_new("Simple Tests");
373 text = gtk_label_new(
374 "IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
375 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
376 "\n"
377 "IsEmpty, IsMine\n"
378 "\n"
379 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
380 "contains a slash then the match is agaist the full path; otherwise it is \n"
381 "agaist the leafname only.");
382 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
383 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
384 gtk_container_add(GTK_CONTAINER(frame), text);
385 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
387 frame = gtk_frame_new("Comparisons");
388 text = gtk_label_new(
389 "<, <=, =, !=, >, >=, After, Before (compare two values)\n"
390 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
391 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
392 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)");
393 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
394 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
395 gtk_container_add(GTK_CONTAINER(frame), text);
396 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
398 hbox = gtk_hbox_new(FALSE, 20);
399 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
401 text = gtk_label_new(
402 "See the ROX-Filer manual for full details.");
403 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
404 button = gtk_button_new_with_label("Close");
405 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
406 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
407 gtk_widget_hide, GTK_OBJECT(help));
409 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
410 gtk_widget_hide, GTK_OBJECT(help));
413 if (GTK_WIDGET_VISIBLE(help))
414 gtk_widget_hide(help);
415 gtk_widget_show_all(help);
418 static void show_chmod_help(gpointer data)
420 static GtkWidget *help = NULL;
422 if (!help)
424 GtkWidget *text, *vbox, *button, *hbox, *sep;
426 help = gtk_window_new(GTK_WINDOW_DIALOG);
427 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
428 gtk_window_set_title(GTK_WINDOW(help),
429 "Permissions command reference");
431 vbox = gtk_vbox_new(FALSE, 0);
432 gtk_container_add(GTK_CONTAINER(help), vbox);
434 text = gtk_label_new(
435 "The format of a command is:\n"
436 "CHANGE, CHANGE, ...\n"
437 "Each CHANGE is:\n"
438 "WHO HOW PERMISSIONS\n"
439 "WHO is some combination of u, g and o which determines whether to\n"
440 "change the permissions for the User (owner), Group or Others.\n"
441 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
442 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
444 "Examples:\n"
445 "u+rw (the file owner gains read and write permission)\n"
446 "g=u (the group permissions are set to be the same as the user's)\n"
447 "o=u-w (others get the same permissions as the owner, but without "
448 "write permission)\n"
449 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
450 "a+X (directories become accessable by everyone; files which were\n"
451 "executable by anyone become executable by everyone)\n"
452 "u+rw, go+r (two commands at once!)\n"
453 "u+s (set the SetUID bit - often has no effect on script files)\n"
454 "755 (set the permissions directly)\n"
456 "\nSee the chmod(1) man page for full details.");
457 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
458 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
460 hbox = gtk_hbox_new(FALSE, 20);
461 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
463 sep = gtk_hseparator_new();
464 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
465 button = gtk_button_new_with_label("Close");
466 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
467 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
468 gtk_widget_hide, GTK_OBJECT(help));
470 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
471 gtk_widget_hide, GTK_OBJECT(help));
474 if (GTK_WIDGET_VISIBLE(help))
475 gtk_widget_hide(help);
476 gtk_widget_show_all(help);
479 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
480 * (or the two are the same directory)
482 static gboolean is_sub_dir(char *sub, char *parent)
484 int parent_len;
485 guchar *real_sub, *real_parent;
486 gboolean retval;
488 real_sub = pathdup(sub);
489 real_parent = pathdup(parent);
491 parent_len = strlen(real_parent);
492 if (strncmp(real_parent, real_sub, parent_len))
493 retval = FALSE;
494 else
496 /* real_sub is at least as long as real_parent and all
497 * characters upto real_parent's length match.
500 retval = real_sub[parent_len] == '\0' ||
501 real_sub[parent_len] == '/';
504 g_free(real_sub);
505 g_free(real_parent);
507 return retval;
510 static gboolean display_dir(gpointer data)
512 GUIside *gui_side = (GUIside *) data;
514 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
515 g_free(gui_side->next_dir);
516 gui_side->next_dir = NULL;
518 return FALSE;
521 static void add_to_results(GUIside *gui_side, gchar *path)
523 gchar *row[] = {"Leaf", "Dir"};
524 gchar *slash;
525 int len;
527 slash = strrchr(path, '/');
528 g_return_if_fail(slash != NULL);
530 len = slash - path;
531 row[1] = g_strndup(path, MAX(len, 1));
532 row[0] = slash + 1;
534 gtk_clist_append(GTK_CLIST(gui_side->results), row);
536 g_free(row[1]);
539 /* Called when the child sends us a message */
540 static void message_from_child(gpointer data,
541 gint source,
542 GdkInputCondition condition)
544 char buf[5];
545 GUIside *gui_side = (GUIside *) data;
546 GtkWidget *log = gui_side->log;
548 if (read_exact(source, buf, 4))
550 ssize_t message_len;
551 char *buffer;
553 buf[4] = '\0';
554 message_len = strtol(buf, NULL, 16);
555 buffer = g_malloc(message_len + 1);
556 if (message_len > 0 && read_exact(source, buffer, message_len))
558 buffer[message_len] = '\0';
559 if (*buffer == '?')
560 SENSITIVE_YESNO(gui_side, TRUE);
561 else if (*buffer == '+')
563 refresh_dirs(buffer + 1);
564 g_free(buffer);
565 return;
567 else if (*buffer == '=')
569 add_to_results(gui_side, buffer + 1);
570 g_free(buffer);
571 return;
573 else if (*buffer == 'm')
575 filer_check_mounted(buffer + 1);
576 g_free(buffer);
577 return;
579 else if (*buffer == '/')
581 if (gui_side->next_dir)
582 g_free(gui_side->next_dir);
583 else
584 gui_side->next_timer =
585 gtk_timeout_add(500,
586 display_dir,
587 gui_side);
588 gui_side->next_dir = buffer;
589 return;
591 else if (*buffer == '!')
592 gui_side->errors++;
594 gtk_text_insert(GTK_TEXT(log),
595 NULL,
596 *buffer == '!' ? &red : NULL,
597 NULL,
598 buffer + 1, message_len - 1);
599 g_free(buffer);
600 return;
602 g_print("Child died in the middle of a message.\n");
605 /* The child is dead */
606 gui_side->child = 0;
608 fclose(gui_side->to_child);
609 gui_side->to_child = NULL;
610 close(gui_side->from_child);
611 gdk_input_remove(gui_side->input_tag);
612 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
614 if (gui_side->errors)
616 GString *report;
617 report = g_string_new(NULL);
618 g_string_sprintf(report, "There %s %d error%s.\n",
619 gui_side->errors == 1 ? "was" : "were",
620 gui_side->errors,
621 gui_side->errors == 1 ? "" : "s");
622 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
623 report->str, report->len);
625 g_string_free(report, TRUE);
627 else if (gui_side->show_info == FALSE)
628 gtk_widget_destroy(gui_side->window);
631 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
632 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
634 DIR *d;
635 struct dirent *ent;
636 GSList *list = NULL, *next;
638 d = opendir(src_dir);
639 if (!d)
641 send_error();
642 return;
645 send_dir(src_dir);
647 while ((ent = readdir(d)))
649 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
650 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
651 continue;
652 list = g_slist_append(list, g_strdup(make_path(src_dir,
653 ent->d_name)->str));
655 closedir(d);
657 if (!list)
658 return;
660 next = list;
662 while (next)
664 if (cb((char *) next->data, dest_path))
666 g_string_sprintf(message, "+%s", dest_path);
667 send();
670 g_free(next->data);
671 next = next->next;
673 g_slist_free(list);
674 return;
677 /* Read this many bytes into the buffer. TRUE on success. */
678 static gboolean read_exact(int source, char *buffer, ssize_t len)
680 while (len > 0)
682 ssize_t got;
683 got = read(source, buffer, len);
684 if (got < 1)
685 return FALSE;
686 len -= got;
687 buffer += got;
689 return TRUE;
692 /* Send 'message' to our parent process. TRUE on success. */
693 static gboolean send()
695 char len_buffer[5];
696 ssize_t len;
698 g_return_val_if_fail(message->len < 0xffff, FALSE);
700 sprintf(len_buffer, "%04x", message->len);
701 fwrite(len_buffer, 1, 4, to_parent);
702 len = fwrite(message->str, 1, message->len, to_parent);
703 fflush(to_parent);
704 return len == message->len;
707 /* Set the directory indicator at the top of the window */
708 static gboolean send_dir(char *dir)
710 g_string_sprintf(message, "/%s", dir);
711 return send();
714 static gboolean send_error()
716 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
717 return send();
720 static void button_reply(GtkWidget *button, GUIside *gui_side)
722 char *text;
724 if (!gui_side->to_child)
725 return;
727 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
728 g_return_if_fail(text != NULL);
729 fputc(*text, gui_side->to_child);
730 fflush(gui_side->to_child);
732 if (*text == 'Y' || *text == 'N' || *text == 'Q')
733 SENSITIVE_YESNO(gui_side, FALSE);
736 static void process_flag(char flag)
738 switch (flag)
740 case 'Q':
741 quiet = !quiet;
742 break;
743 case 'F':
744 o_force = !o_force;
745 break;
746 case 'R':
747 o_recurse = !o_recurse;
748 break;
749 case 'B':
750 o_brief = !o_brief;
751 break;
752 default:
753 g_string_sprintf(message,
754 "!ERROR: Bad message '%c'\n", flag);
755 send();
756 break;
760 /* If the parent has sent any flag toggles, read them */
761 static void check_flags(void)
763 fd_set set;
764 int got;
765 char retval;
766 struct timeval tv;
768 FD_ZERO(&set);
770 while (1)
772 FD_SET(from_parent, &set);
773 tv.tv_sec = 0;
774 tv.tv_usec = 0;
775 got = select(from_parent + 1, &set, NULL, NULL, &tv);
777 if (got == -1)
778 g_error("select() failed: %s\n", g_strerror(errno));
779 else if (!got)
780 return;
782 got = read(from_parent, &retval, 1);
783 if (got != 1)
784 g_error("read() error: %s\n", g_strerror(errno));
786 process_flag(retval);
790 static void read_new_entry_text(void)
792 int len;
793 char c;
794 GString *new;
796 new = g_string_new(NULL);
798 for (;;)
800 len = read(from_parent, &c, 1);
801 if (len != 1)
803 fprintf(stderr, "read() error: %s\n",
804 g_strerror(errno));
805 _exit(1); /* Parent died? */
808 if (c == '\n')
809 break;
810 g_string_append_c(new, c);
813 g_free(new_entry_string);
814 new_entry_string = new->str;
815 g_string_free(new, FALSE);
818 /* Read until the user sends a reply. If ignore_quiet is TRUE then
819 * the user MUST click Yes or No, else treat quiet on as Yes.
820 * If the user needs prompting then does send().
822 static gboolean reply(int fd, gboolean ignore_quiet)
824 ssize_t len;
825 char retval;
826 gboolean asked = FALSE;
828 while (ignore_quiet || !quiet)
830 if (!asked)
832 send();
833 asked = TRUE;
836 len = read(fd, &retval, 1);
837 if (len != 1)
839 fprintf(stderr, "read() error: %s\n",
840 g_strerror(errno));
841 _exit(1); /* Parent died? */
844 switch (retval)
846 case 'Q':
847 quiet = !quiet;
848 if (ignore_quiet)
850 g_string_assign(message, "?");
851 send();
853 break;
854 case 'Y':
855 g_string_assign(message, "' Yes\n");
856 send();
857 return TRUE;
858 case 'N':
859 g_string_assign(message, "' No\n");
860 send();
861 return FALSE;
862 case 'E':
863 read_new_entry_text();
864 break;
865 default:
866 process_flag(retval);
867 break;
871 if (asked)
873 g_string_assign(message, "' Quiet\n");
874 send();
876 return TRUE;
879 static void destroy_action_window(GtkWidget *widget, gpointer data)
881 GUIside *gui_side = (GUIside *) data;
883 if (gui_side->child)
885 kill(gui_side->child, SIGTERM);
886 fclose(gui_side->to_child);
887 close(gui_side->from_child);
888 gdk_input_remove(gui_side->input_tag);
891 if (gui_side->next_dir)
893 gtk_timeout_remove(gui_side->next_timer);
894 g_free(gui_side->next_dir);
897 if (gui_side->preview)
899 gtk_signal_disconnect_by_data(
900 GTK_OBJECT(gui_side->preview->window),
901 gui_side);
902 g_print("[ forget preview ]\n");
903 gui_side->preview = NULL;
906 g_free(gui_side);
908 if (--number_of_windows < 1)
909 gtk_main_quit();
912 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
913 * (NULL on failure). The child calls func().
915 * If autoq then automatically selects 'Quiet'.
917 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
919 int filedes[4]; /* 0 and 2 are for reading */
920 GUIside *gui_side;
921 int child;
922 GtkWidget *vbox, *button, *scrollbar, *actions;
923 struct sigaction act;
925 if (pipe(filedes))
927 report_error("ROX-Filer", g_strerror(errno));
928 return NULL;
931 if (pipe(filedes + 2))
933 close(filedes[0]);
934 close(filedes[1]);
935 report_error("ROX-Filer", g_strerror(errno));
936 return NULL;
939 child = fork();
940 switch (child)
942 case -1:
943 report_error("ROX-Filer", g_strerror(errno));
944 return NULL;
945 case 0:
946 /* We are the child */
948 quiet = autoq;
950 /* Reset the SIGCHLD handler */
951 act.sa_handler = SIG_DFL;
952 sigemptyset(&act.sa_mask);
953 act.sa_flags = 0;
954 sigaction(SIGCHLD, &act, NULL);
956 message = g_string_new(NULL);
957 close(filedes[0]);
958 close(filedes[3]);
959 to_parent = fdopen(filedes[1], "wb");
960 from_parent = filedes[2];
961 func(data);
962 _exit(0);
965 /* We are the parent */
966 close(filedes[1]);
967 close(filedes[2]);
968 gui_side = g_malloc(sizeof(GUIside));
969 gui_side->from_child = filedes[0];
970 gui_side->to_child = fdopen(filedes[3], "wb");
971 gui_side->log = NULL;
972 gui_side->child = child;
973 gui_side->errors = 0;
974 gui_side->show_info = FALSE;
975 gui_side->preview = NULL;
976 gui_side->results = NULL;
977 gui_side->entry = NULL;
978 gui_side->default_string = NULL;
980 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
981 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
982 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
983 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
984 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
986 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
987 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
989 gui_side->dir = gtk_label_new("<dir>");
990 gui_side->next_dir = NULL;
991 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
992 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
994 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
995 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
997 gui_side->log = gtk_text_new(NULL, NULL);
998 gtk_widget_set_usize(gui_side->log, 400, 100);
999 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1000 gui_side->log, TRUE, TRUE, 0);
1001 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
1002 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
1003 scrollbar, FALSE, TRUE, 0);
1005 actions = gtk_hbox_new(TRUE, 4);
1006 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
1008 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
1009 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
1010 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
1011 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1012 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1013 button_reply, gui_side);
1014 gui_side->yes = button = gtk_button_new_with_label("Yes");
1015 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
1016 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1017 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1018 button_reply, gui_side);
1019 gui_side->no = button = gtk_button_new_with_label("No");
1020 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
1021 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1022 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1023 button_reply, gui_side);
1024 SENSITIVE_YESNO(gui_side, FALSE);
1026 button = gtk_button_new_with_label("Abort");
1027 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
1028 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1029 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
1031 gui_side->input_tag = gdk_input_add(gui_side->from_child,
1032 GDK_INPUT_READ,
1033 message_from_child,
1034 gui_side);
1036 return gui_side;
1039 /* ACTIONS ON ONE ITEM */
1041 /* These may call themselves recursively, or ask questions, etc.
1042 * TRUE iff the directory containing dest_path needs to be rescanned.
1045 /* dest_path is the dir containing src_path.
1046 * Updates the global size_tally.
1048 static gboolean do_usage(char *src_path, char *dest_path)
1050 struct stat info;
1052 check_flags();
1054 if (lstat(src_path, &info))
1056 g_string_sprintf(message, "'%s:\n", src_path);
1057 send();
1058 send_error();
1059 return FALSE;
1062 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
1063 size_tally += info.st_size;
1064 else if (S_ISDIR(info.st_mode))
1066 g_string_sprintf(message, "?Count contents of %s?",
1067 src_path);
1068 if (reply(from_parent, FALSE))
1070 char *safe_path;
1071 safe_path = g_strdup(src_path);
1072 for_dir_contents(do_usage, safe_path, safe_path);
1073 g_free(safe_path);
1077 return FALSE;
1080 /* dest_path is the dir containing src_path */
1081 static gboolean do_delete(char *src_path, char *dest_path)
1083 struct stat info;
1084 gboolean write_prot;
1086 check_flags();
1088 if (lstat(src_path, &info))
1090 send_error();
1091 return FALSE;
1094 write_prot = S_ISLNK(info.st_mode) ? FALSE
1095 : access(src_path, W_OK) != 0;
1096 if (write_prot || !quiet)
1098 g_string_sprintf(message, "?Delete %s'%s'?",
1099 write_prot ? "WRITE-PROTECTED " : " ",
1100 src_path);
1101 if (!reply(from_parent, write_prot && !o_force))
1102 return FALSE;
1104 else if (!o_brief)
1106 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
1107 send();
1110 if (S_ISDIR(info.st_mode))
1112 char *safe_path;
1113 safe_path = g_strdup(src_path);
1114 for_dir_contents(do_delete, safe_path, safe_path);
1115 if (rmdir(safe_path))
1117 g_free(safe_path);
1118 send_error();
1119 return FALSE;
1121 g_string_sprintf(message, "'Directory '%s' deleted\n",
1122 safe_path);
1123 send();
1124 g_string_sprintf(message, "m%s", safe_path);
1125 send();
1126 g_free(safe_path);
1128 else if (unlink(src_path))
1130 send_error();
1131 return FALSE;
1134 return TRUE;
1137 /* path is the item to check. If is is a directory then we may recurse
1138 * (unless prune is used).
1140 static gboolean do_find(char *path, char *dummy)
1142 struct stat info;
1144 check_flags();
1146 if (!quiet)
1148 g_string_sprintf(message, "?Check '%s'?", path);
1149 if (!reply(from_parent, FALSE))
1150 return FALSE;
1153 for (;;)
1155 if (new_entry_string)
1157 if (find_condition)
1158 find_condition_free(find_condition);
1159 find_condition = find_compile(new_entry_string);
1160 g_free(new_entry_string);
1161 new_entry_string = NULL;
1164 if (find_condition)
1165 break;
1167 g_string_assign(message,
1168 "!Invalid find condition - change it and try again\n");
1169 send();
1170 g_string_sprintf(message, "?Check '%s'?", path);
1171 if (!reply(from_parent, TRUE))
1172 return FALSE;
1175 if (lstat(path, &info))
1177 send_error();
1178 g_string_sprintf(message, "'(while checking '%s')\n", path);
1179 send();
1180 return FALSE;
1183 if (find_test_condition(find_condition, path))
1185 g_string_sprintf(message, "=%s", path);
1186 send();
1189 if (S_ISDIR(info.st_mode))
1191 char *safe_path;
1192 safe_path = g_strdup(path);
1193 for_dir_contents(do_find, safe_path, safe_path);
1194 g_free(safe_path);
1197 return FALSE;
1200 static gboolean do_chmod(char *path, char *dummy)
1202 struct stat info;
1203 mode_t new_mode;
1205 check_flags();
1207 if (lstat(path, &info))
1209 send_error();
1210 return FALSE;
1212 if (S_ISLNK(info.st_mode))
1213 return FALSE;
1215 if (!quiet)
1217 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1218 if (!reply(from_parent, FALSE))
1219 return FALSE;
1221 else if (!o_brief)
1223 g_string_sprintf(message, "'Changing permissions of '%s'\n",
1224 path);
1225 send();
1228 for (;;)
1230 if (new_entry_string)
1232 if (mode_change)
1233 mode_free(mode_change);
1234 mode_change = mode_compile(new_entry_string,
1235 MODE_MASK_ALL);
1236 g_free(new_entry_string);
1237 new_entry_string = NULL;
1240 if (mode_change)
1241 break;
1243 g_string_assign(message,
1244 "!Invalid mode command - change it and try again\n");
1245 send();
1246 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1247 if (!reply(from_parent, TRUE))
1248 return FALSE;
1251 if (lstat(path, &info))
1253 send_error();
1254 return FALSE;
1256 if (S_ISLNK(info.st_mode))
1257 return FALSE;
1259 new_mode = mode_adjust(info.st_mode, mode_change);
1260 if (chmod(path, new_mode))
1262 send_error();
1263 return FALSE;
1266 if (o_recurse && S_ISDIR(info.st_mode))
1268 char *safe_path;
1269 safe_path = g_strdup(path);
1270 for_dir_contents(do_chmod, safe_path, safe_path);
1271 g_free(safe_path);
1274 return TRUE;
1277 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1278 * is set then that is the new leafname, otherwise the leafname stays
1279 * the same.
1281 static char *make_dest_path(char *object, char *dir)
1283 char *leaf;
1285 if (action_leaf)
1286 leaf = action_leaf;
1287 else
1289 leaf = strrchr(object, '/');
1290 if (!leaf)
1291 leaf = object; /* Error? */
1292 else
1293 leaf++;
1296 return make_path(dir, leaf)->str;
1299 /* If action_leaf is not NULL it specifies the new leaf name
1301 static gboolean do_copy2(char *path, char *dest)
1303 char *dest_path;
1304 struct stat info;
1305 struct stat dest_info;
1306 gboolean retval = TRUE;
1308 check_flags();
1310 dest_path = make_dest_path(path, dest);
1312 if (lstat(path, &info))
1314 send_error();
1315 return FALSE;
1318 if (lstat(dest_path, &dest_info) == 0)
1320 int err;
1321 gboolean merge;
1323 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1325 g_string_sprintf(message, "?'%s' already exists - %s?",
1326 dest_path,
1327 merge ? "merge contents" : "overwrite");
1329 if (!reply(from_parent, TRUE))
1330 return FALSE;
1332 if (!merge)
1334 if (S_ISDIR(dest_info.st_mode))
1335 err = rmdir(dest_path);
1336 else
1337 err = unlink(dest_path);
1339 if (err)
1341 send_error();
1342 if (errno != ENOENT)
1343 return FALSE;
1344 g_string_sprintf(message,
1345 "'Trying copy anyway...\n");
1346 send();
1350 else if (!quiet)
1352 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1353 if (!reply(from_parent, FALSE))
1354 return FALSE;
1356 else
1358 g_string_sprintf(message, "'Copying %s as %s\n", path,
1359 dest_path);
1360 send();
1363 if (S_ISDIR(info.st_mode))
1365 char *safe_path, *safe_dest;
1366 struct stat dest_info;
1367 gboolean exists;
1369 /* (we will do the update ourselves now, rather than
1370 * afterwards)
1372 retval = FALSE;
1374 safe_path = g_strdup(path);
1375 safe_dest = g_strdup(dest_path);
1377 exists = !lstat(dest_path, &dest_info);
1379 if (exists && !S_ISDIR(dest_info.st_mode))
1381 g_string_sprintf(message,
1382 "!ERROR: Destination already exists, "
1383 "but is not a directory\n");
1385 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1386 send_error();
1387 else
1389 if (!exists)
1391 /* (just been created then) */
1392 g_string_sprintf(message, "+%s", dest);
1393 send();
1396 action_leaf = NULL;
1397 for_dir_contents(do_copy2, safe_path, safe_dest);
1398 /* Note: dest_path now invalid... */
1401 g_free(safe_path);
1402 g_free(safe_dest);
1404 else if (S_ISLNK(info.st_mode))
1406 char target[MAXPATHLEN + 1];
1407 int count;
1409 /* Not all versions of cp(1) can make symlinks,
1410 * so we special-case it.
1413 count = readlink(path, target, sizeof(target) - 1);
1414 if (count < 0)
1416 send_error();
1417 retval = FALSE;
1419 else
1421 target[count] = '\0';
1422 if (symlink(target, dest_path))
1424 send_error();
1425 retval = FALSE;
1429 else
1431 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
1433 argv[2] = path;
1434 argv[3] = dest_path;
1436 if (fork_exec_wait(argv))
1438 g_string_sprintf(message, "!ERROR: Copy failed\n");
1439 send();
1440 retval = FALSE;
1444 return retval;
1447 /* Copy path to dest.
1448 * Check that path not copied into itself.
1450 static gboolean do_copy(char *path, char *dest)
1452 if (is_sub_dir(make_dest_path(path, dest), path))
1454 g_string_sprintf(message,
1455 "!ERROR: Can't copy directory into itself\n");
1456 send();
1457 return FALSE;
1459 return do_copy2(path, dest);
1462 /* Move path to dest.
1463 * Check that path not moved into itself.
1465 static gboolean do_move(char *path, char *dest)
1467 char *dest_path;
1468 char *leaf;
1469 gboolean retval = TRUE;
1470 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1471 struct stat info2;
1472 gboolean is_dir;
1474 if (is_sub_dir(dest, path))
1476 g_string_sprintf(message,
1477 "!ERROR: Can't move directory into itself\n");
1478 send();
1479 return FALSE;
1482 check_flags();
1484 leaf = strrchr(path, '/');
1485 if (!leaf)
1486 leaf = path; /* Error? */
1487 else
1488 leaf++;
1490 dest_path = make_path(dest, leaf)->str;
1492 is_dir = lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1494 if (access(dest_path, F_OK) == 0)
1496 struct stat info;
1497 int err;
1499 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1500 dest_path);
1501 if (!reply(from_parent, TRUE))
1502 return FALSE;
1504 if (lstat(dest_path, &info))
1506 send_error();
1507 return FALSE;
1510 if (S_ISDIR(info.st_mode))
1511 err = rmdir(dest_path);
1512 else
1513 err = unlink(dest_path);
1515 if (err)
1517 send_error();
1518 if (errno != ENOENT)
1519 return FALSE;
1520 g_string_sprintf(message,
1521 "'Trying move anyway...\n");
1522 send();
1525 else if (!quiet)
1527 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1528 if (!reply(from_parent, FALSE))
1529 return FALSE;
1531 else
1533 g_string_sprintf(message, "'Moving %s as %s\n", path,
1534 dest_path);
1535 send();
1538 argv[2] = path;
1539 argv[3] = dest;
1541 if (fork_exec_wait(argv) == 0)
1543 g_string_sprintf(message, "+%s", path);
1544 g_string_truncate(message, leaf - path);
1545 send();
1546 if (is_dir) {
1547 g_string_sprintf(message, "m%s", path);
1548 send();
1551 else
1553 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1554 path, dest_path);
1555 send();
1556 retval = FALSE;
1559 return retval;
1562 static gboolean do_link(char *path, char *dest)
1564 char *dest_path;
1565 char *leaf;
1567 check_flags();
1569 leaf = strrchr(path, '/');
1570 if (!leaf)
1571 leaf = path; /* Error? */
1572 else
1573 leaf++;
1575 dest_path = make_path(dest, leaf)->str;
1577 if (quiet)
1579 g_string_sprintf(message, "'Linking %s as %s\n", path,
1580 dest_path);
1581 send();
1583 else
1585 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1586 if (!reply(from_parent, FALSE))
1587 return FALSE;
1590 if (symlink(path, dest_path))
1592 send_error();
1593 return FALSE;
1596 return TRUE;
1599 /* Mount/umount this item */
1600 static void do_mount(FilerWindow *filer_window, DirItem *item)
1602 char *argv[3] = {NULL, NULL, NULL};
1603 char *action;
1605 check_flags();
1607 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1608 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1609 argv[1] = make_path(filer_window->path, item->leafname)->str;
1611 if (quiet)
1613 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1614 send();
1616 else
1618 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1619 if (!reply(from_parent, FALSE))
1620 return;
1623 if (fork_exec_wait(argv) == 0)
1625 g_string_sprintf(message, "+%s", filer_window->path);
1626 send();
1627 g_string_sprintf(message, "m%s", argv[1]);
1628 send();
1630 else
1632 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1633 send();
1637 /* CHILD MAIN LOOPS */
1639 /* After forking, the child calls one of these functions */
1641 static void usage_cb(gpointer data)
1643 FilerWindow *filer_window = (FilerWindow *) data;
1644 Collection *collection = filer_window->collection;
1645 DirItem *item;
1646 int left = collection->number_selected;
1647 int i = -1;
1648 off_t total_size = 0;
1650 send_dir(filer_window->path);
1652 while (left > 0)
1654 i++;
1655 if (!collection->items[i].selected)
1656 continue;
1657 item = (DirItem *) collection->items[i].data;
1658 size_tally = 0;
1659 do_usage(make_path(filer_window->path,
1660 item->leafname)->str,
1661 filer_window->path);
1662 g_string_sprintf(message, "'%s: %s\n",
1663 item->leafname,
1664 format_size((unsigned long) size_tally));
1665 send();
1666 total_size += size_tally;
1667 left--;
1670 g_string_sprintf(message, "'\nTotal: %s\n",
1671 format_size((unsigned long) total_size));
1672 send();
1675 #ifdef DO_MOUNT_POINTS
1676 static void mount_cb(gpointer data)
1678 FilerWindow *filer_window = (FilerWindow *) data;
1679 Collection *collection = filer_window->collection;
1680 DirItem *item;
1681 int i;
1682 gboolean mount_points = FALSE;
1684 send_dir(filer_window->path);
1686 if (mount_item)
1687 do_mount(filer_window, mount_item);
1688 else
1690 for (i = 0; i < collection->number_of_items; i++)
1692 if (!collection->items[i].selected)
1693 continue;
1694 item = (DirItem *) collection->items[i].data;
1695 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1696 continue;
1697 mount_points = TRUE;
1699 do_mount(filer_window, item);
1702 if (!mount_points)
1704 g_string_sprintf(message,
1705 "!No mount points selected!\n");
1706 send();
1710 g_string_sprintf(message, "'\nDone\n");
1711 send();
1713 #endif
1715 static void delete_cb(gpointer data)
1717 FilerWindow *filer_window = (FilerWindow *) data;
1718 Collection *collection = filer_window->collection;
1719 DirItem *item;
1720 int left = collection->number_selected;
1721 int i = -1;
1723 send_dir(filer_window->path);
1725 while (left > 0)
1727 i++;
1728 if (!collection->items[i].selected)
1729 continue;
1730 item = (DirItem *) collection->items[i].data;
1731 if (do_delete(make_path(filer_window->path,
1732 item->leafname)->str,
1733 filer_window->path))
1735 g_string_sprintf(message, "+%s", filer_window->path);
1736 send();
1738 left--;
1741 g_string_sprintf(message, "'\nDone\n");
1742 send();
1745 static void find_cb(gpointer data)
1747 FilerWindow *filer_window = (FilerWindow *) data;
1748 Collection *collection = filer_window->collection;
1749 DirItem *item;
1750 int left = collection->number_selected;
1751 int i = -1;
1753 send_dir(filer_window->path);
1755 while (left > 0)
1757 i++;
1758 if (!collection->items[i].selected)
1759 continue;
1760 item = (DirItem *) collection->items[i].data;
1761 do_find(make_path(filer_window->path,
1762 item->leafname)->str,
1763 NULL);
1764 left--;
1767 g_string_sprintf(message, "'\nDone\n");
1768 send();
1771 static void chmod_cb(gpointer data)
1773 FilerWindow *filer_window = (FilerWindow *) data;
1774 Collection *collection = filer_window->collection;
1775 DirItem *item;
1776 int left = collection->number_selected;
1777 int i = -1;
1779 send_dir(filer_window->path);
1781 while (left > 0)
1783 i++;
1784 if (!collection->items[i].selected)
1785 continue;
1786 item = (DirItem *) collection->items[i].data;
1787 if (item->flags & ITEM_FLAG_SYMLINK)
1789 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1790 item->leafname);
1791 send();
1793 else if (do_chmod(make_path(filer_window->path,
1794 item->leafname)->str, NULL))
1796 g_string_sprintf(message, "+%s", filer_window->path);
1797 send();
1799 left--;
1802 g_string_sprintf(message, "'\nDone\n");
1803 send();
1806 static void list_cb(gpointer data)
1808 GSList *paths = (GSList *) data;
1810 while (paths)
1812 send_dir((char *) paths->data);
1814 if (action_do_func((char *) paths->data, action_dest))
1816 g_string_sprintf(message, "+%s", action_dest);
1817 send();
1820 paths = paths->next;
1823 g_string_sprintf(message, "'\nDone\n");
1824 send();
1827 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1829 GtkWidget *check;
1831 check = gtk_check_button_new_with_label(label);
1832 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1833 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1834 button_reply, gui_side);
1835 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1839 /* EXTERNAL INTERFACE */
1841 void action_find(FilerWindow *filer_window)
1843 GUIside *gui_side;
1844 Collection *collection;
1845 GtkWidget *hbox, *label, *button, *scroller;
1846 gchar *titles[] = {"Name", "Directory"};
1848 collection = filer_window->collection;
1850 if (collection->number_selected < 1)
1852 report_error("ROX-Filer", "You need to select some items "
1853 "to search through");
1854 return;
1857 if (!last_find_string)
1858 last_find_string = g_strdup("'core'");
1860 new_entry_string = last_find_string;
1861 gui_side = start_action(filer_window, find_cb, FALSE);
1862 if (!gui_side)
1863 return;
1865 gui_side->show_info = TRUE;
1867 scroller = gtk_scrolled_window_new(NULL, NULL);
1868 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1869 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1870 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1871 gui_side->results = gtk_clist_new_with_titles(
1872 sizeof(titles) / sizeof(*titles), titles);
1873 gtk_widget_set_usize(gui_side->results, 100, 100);
1874 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1875 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1876 GTK_SELECTION_SINGLE);
1877 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1878 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1879 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1880 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1881 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1883 hbox = gtk_hbox_new(FALSE, 0);
1884 label = gtk_label_new("Expression:");
1885 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1886 gui_side->default_string = &last_find_string;
1887 gui_side->entry = gtk_entry_new();
1888 gtk_widget_set_style(gui_side->entry, fixed_style);
1889 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1890 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1891 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1892 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1893 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1894 entry_changed, gui_side);
1895 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1896 button = gtk_button_new_with_label("Show expression reference");
1897 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1898 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1899 show_condition_help, NULL);
1901 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Find");
1902 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1903 number_of_windows++;
1904 gtk_widget_show_all(gui_side->window);
1907 /* Count disk space used by selected items */
1908 void action_usage(FilerWindow *filer_window)
1910 GUIside *gui_side;
1911 Collection *collection;
1913 collection = filer_window->collection;
1915 if (collection->number_selected < 1)
1917 report_error("ROX-Filer", "You need to select some items "
1918 "to count");
1919 return;
1922 gui_side = start_action(filer_window, usage_cb, TRUE);
1923 if (!gui_side)
1924 return;
1926 gui_side->show_info = TRUE;
1928 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1929 number_of_windows++;
1930 gtk_widget_show_all(gui_side->window);
1933 /* Mount/unmount 'item', or all selected mount points if NULL. */
1934 void action_mount(FilerWindow *filer_window, DirItem *item)
1936 #ifdef DO_MOUNT_POINTS
1937 GUIside *gui_side;
1938 Collection *collection;
1940 collection = filer_window->collection;
1942 if (item == NULL && collection->number_selected < 1)
1944 report_error("ROX-Filer", "You need to select some items "
1945 "to mount or unmount");
1946 return;
1949 mount_item = item;
1950 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1951 if (!gui_side)
1952 return;
1954 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1955 number_of_windows++;
1956 gtk_widget_show_all(gui_side->window);
1957 #else
1958 report_error("ROX-Filer",
1959 "ROX-Filer does not yet support mount points on your "
1960 "system. Sorry.");
1961 #endif /* DO_MOUNT_POINTS */
1964 /* Deletes all selected items in the window */
1965 void action_delete(FilerWindow *filer_window)
1967 GUIside *gui_side;
1968 Collection *collection;
1970 collection = filer_window->collection;
1972 if (collection->number_selected < 1)
1974 report_error("ROX-Filer", "You need to select some items "
1975 "to delete");
1976 return;
1979 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1980 if (!gui_side)
1981 return;
1983 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1984 add_toggle(gui_side,
1985 "Force - don't confirm deletion of non-writeable items", "F");
1986 add_toggle(gui_side,
1987 "Brief - only log directories being deleted", "B");
1989 number_of_windows++;
1990 gtk_widget_show_all(gui_side->window);
1993 /* Change the permissions of the selected items */
1994 void action_chmod(FilerWindow *filer_window)
1996 GUIside *gui_side;
1997 Collection *collection;
1998 GtkWidget *hbox, *label, *button;
2000 collection = filer_window->collection;
2002 if (collection->number_selected < 1)
2004 report_error("ROX-Filer", "You need to select the items "
2005 "whose permissions you want to change");
2006 return;
2009 if (!last_chmod_string)
2010 last_chmod_string = g_strdup("a+x");
2011 new_entry_string = last_chmod_string;
2012 gui_side = start_action(filer_window, chmod_cb, FALSE);
2013 if (!gui_side)
2014 return;
2016 add_toggle(gui_side,
2017 "Brief - don't list processed files", "B");
2018 add_toggle(gui_side,
2019 "Recurse - also change contents of subdirectories", "R");
2020 hbox = gtk_hbox_new(FALSE, 0);
2021 label = gtk_label_new("Command:");
2022 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2023 gui_side->default_string = &last_chmod_string;
2024 gui_side->entry = gtk_entry_new();
2025 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2026 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2027 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2028 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
2029 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2030 entry_changed, gui_side);
2031 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2032 button = gtk_button_new_with_label("Show command reference");
2033 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
2034 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
2035 show_chmod_help, NULL);
2037 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2038 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
2040 number_of_windows++;
2041 gtk_widget_show_all(gui_side->window);
2044 void action_copy(GSList *paths, char *dest, char *leaf)
2046 GUIside *gui_side;
2048 action_dest = dest;
2049 action_leaf = leaf;
2050 action_do_func = do_copy;
2051 gui_side = start_action(paths, list_cb, o_auto_copy);
2052 if (!gui_side)
2053 return;
2055 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
2056 number_of_windows++;
2057 gtk_widget_show_all(gui_side->window);
2060 void action_move(GSList *paths, char *dest)
2062 GUIside *gui_side;
2064 action_dest = dest;
2065 action_do_func = do_move;
2066 gui_side = start_action(paths, list_cb, o_auto_move);
2067 if (!gui_side)
2068 return;
2070 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
2071 number_of_windows++;
2072 gtk_widget_show_all(gui_side->window);
2075 void action_link(GSList *paths, char *dest)
2077 GUIside *gui_side;
2079 action_dest = dest;
2080 action_do_func = do_link;
2081 gui_side = start_action(paths, list_cb, o_auto_link);
2082 if (!gui_side)
2083 return;
2085 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
2086 number_of_windows++;
2087 gtk_widget_show_all(gui_side->window);
2090 void action_init(void)
2092 options_sections = g_slist_prepend(options_sections, &options);
2093 option_register("action_auto_quiet", action_auto_quiet);