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)
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
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.
31 #include <sys/param.h>
34 #include <sys/types.h>
43 #include "gui_support.h"
47 #include "modechange.h"
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",
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) \
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);\
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
);
102 int from_child
; /* File descriptor */
104 int input_tag
; /* gdk_input_add() */
105 GtkWidget
*vbox
, *log
, *window
, *dir
;
106 GtkWidget
*quiet
, *yes
, *no
;
107 int child
; /* Process ID */
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 */
118 /* These don't need to be in a structure because we fork() before
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
);
160 /* Build up some option widgets to go in the options dialog, but don't
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);
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
),
195 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move
),
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link
),
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete
),
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_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";
235 option_write("action_auto_quiet", str
);
238 static char *action_auto_quiet(char *data
)
259 o_auto_delete
= state
;
262 o_auto_mount
= state
;
265 return "Unknown flag";
274 /* This is called whenever the user edits the entry box (if any) - send the
277 static void entry_changed(GtkEntry
*entry
, GUIside
*gui_side
)
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
)
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
;
303 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
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 text
= gtk_label_new(
314 "An expression is a series of cases which are checked from left \n"
315 "to right. As soon as one matches, the expression is TRUE. If none \n"
316 "match then the expression is FALSE.\n"
318 "Cases are separated by commas:\n"
319 "'*.htm', '*.html' (finds HTML files)\n"
321 "Each case is a list of conditions which must all be met for the \n"
323 "IsReg 'core' (finds a regular file called 'core')\n"
324 "Preceeding a condition by ! inverts the result of the condition:\n"
325 "IsDev ! '/dev/*' (finds device files not in /dev)\n"
327 "Simple tests are: IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev,\n"
328 "IsPipe, IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsEmpty\n"
329 "IsExecutable, IsMine\n"
331 "A pattern in single quotes is a shell-style wildcard pattern to \n"
332 "match. If it contains a slash then the match is agaist the full \n"
333 "path; otherwise it is agaist the leafname only.\n"
335 "Read the ROX-Filer manual for full details.");
336 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
337 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
339 hbox
= gtk_hbox_new(FALSE
, 20);
340 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
342 sep
= gtk_hseparator_new();
343 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
344 button
= gtk_button_new_with_label("Close");
345 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
346 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
347 gtk_widget_hide
, GTK_OBJECT(help
));
349 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
350 gtk_widget_hide
, GTK_OBJECT(help
));
353 if (GTK_WIDGET_VISIBLE(help
))
354 gtk_widget_hide(help
);
355 gtk_widget_show_all(help
);
358 static void show_chmod_help(gpointer data
)
360 static GtkWidget
*help
= NULL
;
364 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
366 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
367 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
368 gtk_window_set_title(GTK_WINDOW(help
),
369 "Permissions command reference");
371 vbox
= gtk_vbox_new(FALSE
, 0);
372 gtk_container_add(GTK_CONTAINER(help
), vbox
);
374 text
= gtk_label_new(
375 "The format of a command is:\n"
376 "CHANGE, CHANGE, ...\n"
378 "WHO HOW PERMISSIONS\n"
379 "WHO is some combination of u, g and o which determines whether to\n"
380 "change the permissions for the User (owner), Group or Others.\n"
381 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
382 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
385 "u+rw (the file owner gains read and write permission)\n"
386 "g=u (the group permissions are set to be the same as the user's)\n"
387 "o=u-w (others get the same permissions as the owner, but without "
388 "write permission)\n"
389 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
390 "a+X (directories become accessable by everyone; files which were\n"
391 "executable by anyone become executable by everyone)\n"
392 "u+rw, go+r (two commands at once!)\n"
393 "u+s (set the SetUID bit - often has no effect on script files)\n"
394 "755 (set the permissions directly)\n"
396 "\nSee the chmod(1) man page for full details.");
397 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
398 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
400 hbox
= gtk_hbox_new(FALSE
, 20);
401 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
403 sep
= gtk_hseparator_new();
404 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
405 button
= gtk_button_new_with_label("Close");
406 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
407 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
408 gtk_widget_hide
, GTK_OBJECT(help
));
410 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
411 gtk_widget_hide
, GTK_OBJECT(help
));
414 if (GTK_WIDGET_VISIBLE(help
))
415 gtk_widget_hide(help
);
416 gtk_widget_show_all(help
);
419 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
420 * (or the two are the same directory)
422 static gboolean
is_sub_dir(char *sub
, char *parent
)
425 guchar
*real_sub
, *real_parent
;
428 real_sub
= pathdup(sub
);
429 real_parent
= pathdup(parent
);
431 parent_len
= strlen(real_parent
);
432 if (strncmp(real_parent
, real_sub
, parent_len
))
436 /* real_sub is at least as long as real_parent and all
437 * characters upto real_parent's length match.
440 retval
= real_sub
[parent_len
] == '\0' ||
441 real_sub
[parent_len
] == '/';
450 static gboolean
display_dir(gpointer data
)
452 GUIside
*gui_side
= (GUIside
*) data
;
454 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
455 g_free(gui_side
->next_dir
);
456 gui_side
->next_dir
= NULL
;
461 /* Called when the child sends us a message */
462 static void message_from_child(gpointer data
,
464 GdkInputCondition condition
)
467 GUIside
*gui_side
= (GUIside
*) data
;
468 GtkWidget
*log
= gui_side
->log
;
470 if (read_exact(source
, buf
, 4))
476 message_len
= strtol(buf
, NULL
, 16);
477 buffer
= g_malloc(message_len
+ 1);
478 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
480 buffer
[message_len
] = '\0';
482 SENSITIVE_YESNO(gui_side
, TRUE
);
483 else if (*buffer
== '+')
485 refresh_dirs(buffer
+ 1);
489 else if (*buffer
== 'm')
491 filer_check_mounted(buffer
+ 1);
495 else if (*buffer
== '/')
497 if (gui_side
->next_dir
)
498 g_free(gui_side
->next_dir
);
500 gui_side
->next_timer
=
504 gui_side
->next_dir
= buffer
;
507 else if (*buffer
== '!')
510 gtk_text_insert(GTK_TEXT(log
),
512 *buffer
== '!' ? &red
: NULL
,
514 buffer
+ 1, message_len
- 1);
518 g_print("Child died in the middle of a message.\n");
521 /* The child is dead */
524 fclose(gui_side
->to_child
);
525 gui_side
->to_child
= NULL
;
526 close(gui_side
->from_child
);
527 gdk_input_remove(gui_side
->input_tag
);
528 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
530 if (gui_side
->errors
)
533 report
= g_string_new(NULL
);
534 g_string_sprintf(report
, "There %s %d error%s.\n",
535 gui_side
->errors
== 1 ? "was" : "were",
537 gui_side
->errors
== 1 ? "" : "s");
538 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
539 report
->str
, report
->len
);
541 g_string_free(report
, TRUE
);
543 else if (gui_side
->show_info
== FALSE
)
544 gtk_widget_destroy(gui_side
->window
);
547 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
548 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
552 GSList
*list
= NULL
, *next
;
554 d
= opendir(src_dir
);
563 while ((ent
= readdir(d
)))
565 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
566 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
568 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
580 if (cb((char *) next
->data
, dest_path
))
582 g_string_sprintf(message
, "+%s", dest_path
);
593 /* Read this many bytes into the buffer. TRUE on success. */
594 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
599 got
= read(source
, buffer
, len
);
608 /* Send 'message' to our parent process. TRUE on success. */
609 static gboolean
send()
614 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
616 sprintf(len_buffer
, "%04x", message
->len
);
617 fwrite(len_buffer
, 1, 4, to_parent
);
618 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
620 return len
== message
->len
;
623 /* Set the directory indicator at the top of the window */
624 static gboolean
send_dir(char *dir
)
626 g_string_sprintf(message
, "/%s", dir
);
630 static gboolean
send_error()
632 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
636 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
640 if (!gui_side
->to_child
)
643 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
644 g_return_if_fail(text
!= NULL
);
645 fputc(*text
, gui_side
->to_child
);
646 fflush(gui_side
->to_child
);
648 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
649 SENSITIVE_YESNO(gui_side
, FALSE
);
652 static void process_flag(char flag
)
663 o_recurse
= !o_recurse
;
669 g_string_sprintf(message
,
670 "!ERROR: Bad message '%c'\n", flag
);
676 /* If the parent has sent any flag toggles, read them */
677 static void check_flags(void)
688 FD_SET(from_parent
, &set
);
691 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
694 g_error("select() failed: %s\n", g_strerror(errno
));
698 got
= read(from_parent
, &retval
, 1);
700 g_error("read() error: %s\n", g_strerror(errno
));
702 process_flag(retval
);
706 static void read_new_entry_text(void)
712 new = g_string_new(NULL
);
716 len
= read(from_parent
, &c
, 1);
719 fprintf(stderr
, "read() error: %s\n",
721 _exit(1); /* Parent died? */
726 g_string_append_c(new, c
);
729 g_free(new_entry_string
);
730 new_entry_string
= new->str
;
731 g_string_free(new, FALSE
);
734 /* Read until the user sends a reply. If ignore_quiet is TRUE then
735 * the user MUST click Yes or No, else treat quiet on as Yes.
736 * If the user needs prompting then does send().
738 static gboolean
reply(int fd
, gboolean ignore_quiet
)
742 gboolean asked
= FALSE
;
744 while (ignore_quiet
|| !quiet
)
752 len
= read(fd
, &retval
, 1);
755 fprintf(stderr
, "read() error: %s\n",
757 _exit(1); /* Parent died? */
766 g_string_assign(message
, "?");
771 g_string_assign(message
, "' Yes\n");
775 g_string_assign(message
, "' No\n");
779 read_new_entry_text();
782 process_flag(retval
);
789 g_string_assign(message
, "' Quiet\n");
795 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
797 GUIside
*gui_side
= (GUIside
*) data
;
801 kill(gui_side
->child
, SIGTERM
);
802 fclose(gui_side
->to_child
);
803 close(gui_side
->from_child
);
804 gdk_input_remove(gui_side
->input_tag
);
807 if (gui_side
->next_dir
)
809 gtk_timeout_remove(gui_side
->next_timer
);
810 g_free(gui_side
->next_dir
);
814 if (--number_of_windows
< 1)
818 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
819 * (NULL on failure). The child calls func().
821 * If autoq then automatically selects 'Quiet'.
823 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
825 int filedes
[4]; /* 0 and 2 are for reading */
828 GtkWidget
*vbox
, *button
, *hbox
, *scrollbar
, *actions
;
829 struct sigaction act
;
833 report_error("ROX-Filer", g_strerror(errno
));
837 if (pipe(filedes
+ 2))
841 report_error("ROX-Filer", g_strerror(errno
));
849 report_error("ROX-Filer", g_strerror(errno
));
852 /* We are the child */
856 /* Reset the SIGCHLD handler */
857 act
.sa_handler
= SIG_DFL
;
858 sigemptyset(&act
.sa_mask
);
860 sigaction(SIGCHLD
, &act
, NULL
);
862 message
= g_string_new(NULL
);
865 to_parent
= fdopen(filedes
[1], "wb");
866 from_parent
= filedes
[2];
871 /* We are the parent */
874 gui_side
= g_malloc(sizeof(GUIside
));
875 gui_side
->from_child
= filedes
[0];
876 gui_side
->to_child
= fdopen(filedes
[3], "wb");
877 gui_side
->log
= NULL
;
878 gui_side
->child
= child
;
879 gui_side
->errors
= 0;
880 gui_side
->show_info
= FALSE
;
881 gui_side
->entry
= NULL
;
882 gui_side
->default_string
= NULL
;
884 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
885 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
886 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
887 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
888 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
890 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
891 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
893 gui_side
->dir
= gtk_label_new("<dir>");
894 gui_side
->next_dir
= NULL
;
895 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
896 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
898 hbox
= gtk_hbox_new(FALSE
, 0);
899 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 4);
901 gui_side
->log
= gtk_text_new(NULL
, NULL
);
902 gtk_widget_set_usize(gui_side
->log
, 400, 100);
903 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->log
, TRUE
, TRUE
, 0);
904 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
905 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
907 actions
= gtk_hbox_new(TRUE
, 4);
908 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
910 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label("Quiet");
911 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
912 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
913 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
914 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
915 button_reply
, gui_side
);
916 gui_side
->yes
= button
= gtk_button_new_with_label("Yes");
917 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
918 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
919 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
920 button_reply
, gui_side
);
921 gui_side
->no
= button
= gtk_button_new_with_label("No");
922 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
923 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
924 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
925 button_reply
, gui_side
);
926 SENSITIVE_YESNO(gui_side
, FALSE
);
928 button
= gtk_button_new_with_label("Abort");
929 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
930 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
931 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
933 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
941 /* ACTIONS ON ONE ITEM */
943 /* These may call themselves recursively, or ask questions, etc.
944 * TRUE iff the directory containing dest_path needs to be rescanned.
947 /* dest_path is the dir containing src_path.
948 * Updates the global size_tally.
950 static gboolean
do_usage(char *src_path
, char *dest_path
)
956 if (lstat(src_path
, &info
))
958 g_string_sprintf(message
, "'%s:\n", src_path
);
964 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
965 size_tally
+= info
.st_size
;
966 else if (S_ISDIR(info
.st_mode
))
968 g_string_sprintf(message
, "?Count contents of %s?",
970 if (reply(from_parent
, FALSE
))
973 safe_path
= g_strdup(src_path
);
974 for_dir_contents(do_usage
, safe_path
, safe_path
);
982 /* dest_path is the dir containing src_path */
983 static gboolean
do_delete(char *src_path
, char *dest_path
)
990 if (lstat(src_path
, &info
))
996 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
997 : access(src_path
, W_OK
) != 0;
998 if (write_prot
|| !quiet
)
1000 g_string_sprintf(message
, "?Delete %s'%s'?",
1001 write_prot
? "WRITE-PROTECTED " : " ",
1003 if (!reply(from_parent
, write_prot
&& !o_force
))
1008 g_string_sprintf(message
, "'Deleting '%s'\n", src_path
);
1012 if (S_ISDIR(info
.st_mode
))
1015 safe_path
= g_strdup(src_path
);
1016 for_dir_contents(do_delete
, safe_path
, safe_path
);
1017 if (rmdir(safe_path
))
1023 g_string_sprintf(message
, "'Directory '%s' deleted\n",
1026 g_string_sprintf(message
, "m%s", safe_path
);
1030 else if (unlink(src_path
))
1039 /* path is the item to check. If is is a directory then we may recurse
1040 * (unless prune is used).
1042 static gboolean
do_find(char *path
, char *dummy
)
1050 g_string_sprintf(message
, "?Check '%s'?", path
);
1051 if (!reply(from_parent
, FALSE
))
1057 if (new_entry_string
)
1060 find_condition_free(find_condition
);
1061 find_condition
= find_compile(new_entry_string
);
1062 g_free(new_entry_string
);
1063 new_entry_string
= NULL
;
1069 g_string_assign(message
,
1070 "!Invalid find condition - change it and try again\n");
1072 g_string_sprintf(message
, "?Check '%s'?", path
);
1073 if (!reply(from_parent
, TRUE
))
1077 if (lstat(path
, &info
))
1080 g_string_sprintf(message
, "'(while checking '%s')\n", path
);
1085 if (find_test_condition(find_condition
, path
))
1087 g_string_sprintf(message
, "'Found '%s'\n", path
);
1091 if (S_ISDIR(info
.st_mode
))
1094 safe_path
= g_strdup(path
);
1095 for_dir_contents(do_find
, safe_path
, safe_path
);
1102 static gboolean
do_chmod(char *path
, char *dummy
)
1109 if (lstat(path
, &info
))
1114 if (S_ISLNK(info
.st_mode
))
1119 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
1120 if (!reply(from_parent
, FALSE
))
1125 g_string_sprintf(message
, "'Changing permissions of '%s'\n",
1132 if (new_entry_string
)
1135 mode_free(mode_change
);
1136 mode_change
= mode_compile(new_entry_string
,
1138 g_free(new_entry_string
);
1139 new_entry_string
= NULL
;
1145 g_string_assign(message
,
1146 "!Invalid mode command - change it and try again\n");
1148 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
1149 if (!reply(from_parent
, TRUE
))
1153 if (lstat(path
, &info
))
1158 if (S_ISLNK(info
.st_mode
))
1161 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1162 if (chmod(path
, new_mode
))
1168 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1171 safe_path
= g_strdup(path
);
1172 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1179 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1180 * is set then that is the new leafname, otherwise the leafname stays
1183 static char *make_dest_path(char *object
, char *dir
)
1191 leaf
= strrchr(object
, '/');
1193 leaf
= object
; /* Error? */
1198 return make_path(dir
, leaf
)->str
;
1201 /* If action_leaf is not NULL it specifies the new leaf name
1203 static gboolean
do_copy2(char *path
, char *dest
)
1207 struct stat dest_info
;
1208 gboolean retval
= TRUE
;
1212 dest_path
= make_dest_path(path
, dest
);
1214 if (lstat(path
, &info
))
1220 if (lstat(dest_path
, &dest_info
) == 0)
1225 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1227 g_string_sprintf(message
, "?'%s' already exists - %s?",
1229 merge
? "merge contents" : "overwrite");
1231 if (!reply(from_parent
, TRUE
))
1236 if (S_ISDIR(dest_info
.st_mode
))
1237 err
= rmdir(dest_path
);
1239 err
= unlink(dest_path
);
1244 if (errno
!= ENOENT
)
1246 g_string_sprintf(message
,
1247 "'Trying copy anyway...\n");
1254 g_string_sprintf(message
, "?Copy %s as %s?", path
, dest_path
);
1255 if (!reply(from_parent
, FALSE
))
1260 g_string_sprintf(message
, "'Copying %s as %s\n", path
,
1265 if (S_ISDIR(info
.st_mode
))
1267 char *safe_path
, *safe_dest
;
1268 struct stat dest_info
;
1271 /* (we will do the update ourselves now, rather than
1276 safe_path
= g_strdup(path
);
1277 safe_dest
= g_strdup(dest_path
);
1279 exists
= !lstat(dest_path
, &dest_info
);
1281 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1283 g_string_sprintf(message
,
1284 "!ERROR: Destination already exists, "
1285 "but is not a directory\n");
1287 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
1293 /* (just been created then) */
1294 g_string_sprintf(message
, "+%s", dest
);
1299 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1300 /* Note: dest_path now invalid... */
1306 else if (S_ISLNK(info
.st_mode
))
1308 char target
[MAXPATHLEN
+ 1];
1311 /* Not all versions of cp(1) can make symlinks,
1312 * so we special-case it.
1315 count
= readlink(path
, target
, sizeof(target
) - 1);
1323 target
[count
] = '\0';
1324 if (symlink(target
, dest_path
))
1333 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
1336 argv
[3] = dest_path
;
1338 if (fork_exec_wait(argv
))
1340 g_string_sprintf(message
, "!ERROR: Copy failed\n");
1349 /* Copy path to dest.
1350 * Check that path not copied into itself.
1352 static gboolean
do_copy(char *path
, char *dest
)
1354 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1356 g_string_sprintf(message
,
1357 "!ERROR: Can't copy directory into itself\n");
1361 return do_copy2(path
, dest
);
1364 /* Move path to dest.
1365 * Check that path not moved into itself.
1367 static gboolean
do_move(char *path
, char *dest
)
1371 gboolean retval
= TRUE
;
1372 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1376 if (is_sub_dir(dest
, path
))
1378 g_string_sprintf(message
,
1379 "!ERROR: Can't move directory into itself\n");
1386 leaf
= strrchr(path
, '/');
1388 leaf
= path
; /* Error? */
1392 dest_path
= make_path(dest
, leaf
)->str
;
1394 is_dir
= lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1396 if (access(dest_path
, F_OK
) == 0)
1401 g_string_sprintf(message
, "?'%s' already exists - overwrite?",
1403 if (!reply(from_parent
, TRUE
))
1406 if (lstat(dest_path
, &info
))
1412 if (S_ISDIR(info
.st_mode
))
1413 err
= rmdir(dest_path
);
1415 err
= unlink(dest_path
);
1420 if (errno
!= ENOENT
)
1422 g_string_sprintf(message
,
1423 "'Trying move anyway...\n");
1429 g_string_sprintf(message
, "?Move %s as %s?", path
, dest_path
);
1430 if (!reply(from_parent
, FALSE
))
1435 g_string_sprintf(message
, "'Moving %s as %s\n", path
,
1443 if (fork_exec_wait(argv
) == 0)
1445 g_string_sprintf(message
, "+%s", path
);
1446 g_string_truncate(message
, leaf
- path
);
1449 g_string_sprintf(message
, "m%s", path
);
1455 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
1464 static gboolean
do_link(char *path
, char *dest
)
1471 leaf
= strrchr(path
, '/');
1473 leaf
= path
; /* Error? */
1477 dest_path
= make_path(dest
, leaf
)->str
;
1481 g_string_sprintf(message
, "'Linking %s as %s\n", path
,
1487 g_string_sprintf(message
, "?Link %s as %s?", path
, dest_path
);
1488 if (!reply(from_parent
, FALSE
))
1492 if (symlink(path
, dest_path
))
1501 /* Mount/umount this item */
1502 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
)
1504 char *argv
[3] = {NULL
, NULL
, NULL
};
1509 argv
[0] = item
->flags
& ITEM_FLAG_MOUNTED
? "umount" : "mount";
1510 action
= item
->flags
& ITEM_FLAG_MOUNTED
? "Unmount" : "Mount";
1511 argv
[1] = make_path(filer_window
->path
, item
->leafname
)->str
;
1515 g_string_sprintf(message
, "'%sing %s\n", action
, argv
[1]);
1520 g_string_sprintf(message
, "?%s %s?", action
, argv
[1]);
1521 if (!reply(from_parent
, FALSE
))
1525 if (fork_exec_wait(argv
) == 0)
1527 g_string_sprintf(message
, "+%s", filer_window
->path
);
1529 g_string_sprintf(message
, "m%s", argv
[1]);
1534 g_string_sprintf(message
, "!ERROR: %s failed\n", action
);
1539 /* CHILD MAIN LOOPS */
1541 /* After forking, the child calls one of these functions */
1543 static void usage_cb(gpointer data
)
1545 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1546 Collection
*collection
= filer_window
->collection
;
1548 int left
= collection
->number_selected
;
1550 off_t total_size
= 0;
1552 send_dir(filer_window
->path
);
1557 if (!collection
->items
[i
].selected
)
1559 item
= (DirItem
*) collection
->items
[i
].data
;
1561 do_usage(make_path(filer_window
->path
,
1562 item
->leafname
)->str
,
1563 filer_window
->path
);
1564 g_string_sprintf(message
, "'%s: %s\n",
1566 format_size((unsigned long) size_tally
));
1568 total_size
+= size_tally
;
1572 g_string_sprintf(message
, "'\nTotal: %s\n",
1573 format_size((unsigned long) total_size
));
1577 #ifdef DO_MOUNT_POINTS
1578 static void mount_cb(gpointer data
)
1580 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1581 Collection
*collection
= filer_window
->collection
;
1584 gboolean mount_points
= FALSE
;
1586 send_dir(filer_window
->path
);
1589 do_mount(filer_window
, mount_item
);
1592 for (i
= 0; i
< collection
->number_of_items
; i
++)
1594 if (!collection
->items
[i
].selected
)
1596 item
= (DirItem
*) collection
->items
[i
].data
;
1597 if (!(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
1599 mount_points
= TRUE
;
1601 do_mount(filer_window
, item
);
1606 g_string_sprintf(message
,
1607 "!No mount points selected!\n");
1612 g_string_sprintf(message
, "'\nDone\n");
1617 static void delete_cb(gpointer data
)
1619 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1620 Collection
*collection
= filer_window
->collection
;
1622 int left
= collection
->number_selected
;
1625 send_dir(filer_window
->path
);
1630 if (!collection
->items
[i
].selected
)
1632 item
= (DirItem
*) collection
->items
[i
].data
;
1633 if (do_delete(make_path(filer_window
->path
,
1634 item
->leafname
)->str
,
1635 filer_window
->path
))
1637 g_string_sprintf(message
, "+%s", filer_window
->path
);
1643 g_string_sprintf(message
, "'\nDone\n");
1647 static void find_cb(gpointer data
)
1649 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1650 Collection
*collection
= filer_window
->collection
;
1652 int left
= collection
->number_selected
;
1655 send_dir(filer_window
->path
);
1660 if (!collection
->items
[i
].selected
)
1662 item
= (DirItem
*) collection
->items
[i
].data
;
1663 do_find(make_path(filer_window
->path
,
1664 item
->leafname
)->str
,
1669 g_string_sprintf(message
, "'\nDone\n");
1673 static void chmod_cb(gpointer data
)
1675 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1676 Collection
*collection
= filer_window
->collection
;
1678 int left
= collection
->number_selected
;
1681 send_dir(filer_window
->path
);
1686 if (!collection
->items
[i
].selected
)
1688 item
= (DirItem
*) collection
->items
[i
].data
;
1689 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1691 g_string_sprintf(message
, "!'%s' is a symbolic link\n",
1695 else if (do_chmod(make_path(filer_window
->path
,
1696 item
->leafname
)->str
, NULL
))
1698 g_string_sprintf(message
, "+%s", filer_window
->path
);
1704 g_string_sprintf(message
, "'\nDone\n");
1708 static void list_cb(gpointer data
)
1710 GSList
*paths
= (GSList
*) data
;
1714 send_dir((char *) paths
->data
);
1716 if (action_do_func((char *) paths
->data
, action_dest
))
1718 g_string_sprintf(message
, "+%s", action_dest
);
1722 paths
= paths
->next
;
1725 g_string_sprintf(message
, "'\nDone\n");
1729 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1733 check
= gtk_check_button_new_with_label(label
);
1734 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1735 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1736 button_reply
, gui_side
);
1737 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1741 /* EXTERNAL INTERFACE */
1743 void action_find(FilerWindow
*filer_window
)
1746 Collection
*collection
;
1747 GtkWidget
*hbox
, *label
, *button
;
1749 collection
= filer_window
->collection
;
1751 if (collection
->number_selected
< 1)
1753 report_error("ROX-Filer", "You need to select some items "
1754 "to search through");
1758 if (!last_find_string
)
1759 last_find_string
= g_strdup("'core'");
1761 new_entry_string
= last_find_string
;
1762 gui_side
= start_action(filer_window
, find_cb
, FALSE
);
1766 gui_side
->show_info
= TRUE
;
1768 hbox
= gtk_hbox_new(FALSE
, 0);
1769 label
= gtk_label_new("Expression:");
1770 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1771 gui_side
->default_string
= &last_find_string
;
1772 gui_side
->entry
= gtk_entry_new();
1773 gtk_widget_set_style(gui_side
->entry
, fixed_style
);
1774 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_find_string
);
1775 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1776 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1777 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1778 entry_changed
, gui_side
);
1779 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 4);
1780 button
= gtk_button_new_with_label("Show expression reference");
1781 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 4);
1782 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1783 show_condition_help
, NULL
);
1785 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Find");
1786 number_of_windows
++;
1787 gtk_widget_show_all(gui_side
->window
);
1790 /* Count disk space used by selected items */
1791 void action_usage(FilerWindow
*filer_window
)
1794 Collection
*collection
;
1796 collection
= filer_window
->collection
;
1798 if (collection
->number_selected
< 1)
1800 report_error("ROX-Filer", "You need to select some items "
1805 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1809 gui_side
->show_info
= TRUE
;
1811 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Disk Usage");
1812 number_of_windows
++;
1813 gtk_widget_show_all(gui_side
->window
);
1816 /* Mount/unmount 'item', or all selected mount points if NULL. */
1817 void action_mount(FilerWindow
*filer_window
, DirItem
*item
)
1819 #ifdef DO_MOUNT_POINTS
1821 Collection
*collection
;
1823 collection
= filer_window
->collection
;
1825 if (item
== NULL
&& collection
->number_selected
< 1)
1827 report_error("ROX-Filer", "You need to select some items "
1828 "to mount or unmount");
1833 gui_side
= start_action(filer_window
, mount_cb
, o_auto_mount
);
1837 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Mount / Unmount");
1838 number_of_windows
++;
1839 gtk_widget_show_all(gui_side
->window
);
1841 report_error("ROX-Filer",
1842 "ROX-Filer does not yet support mount points on your "
1844 #endif /* DO_MOUNT_POINTS */
1847 /* Deletes all selected items in the window */
1848 void action_delete(FilerWindow
*filer_window
)
1851 Collection
*collection
;
1853 collection
= filer_window
->collection
;
1855 if (collection
->number_selected
< 1)
1857 report_error("ROX-Filer", "You need to select some items "
1862 gui_side
= start_action(filer_window
, delete_cb
, o_auto_delete
);
1866 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Delete");
1867 add_toggle(gui_side
,
1868 "Force - don't confirm deletion of non-writeable items", "F");
1869 add_toggle(gui_side
,
1870 "Brief - only log directories being deleted", "B");
1872 number_of_windows
++;
1873 gtk_widget_show_all(gui_side
->window
);
1876 /* Change the permissions of the selected items */
1877 void action_chmod(FilerWindow
*filer_window
)
1880 Collection
*collection
;
1881 GtkWidget
*hbox
, *label
, *button
;
1883 collection
= filer_window
->collection
;
1885 if (collection
->number_selected
< 1)
1887 report_error("ROX-Filer", "You need to select the items "
1888 "whose permissions you want to change");
1892 if (!last_chmod_string
)
1893 last_chmod_string
= g_strdup("a+x");
1894 new_entry_string
= last_chmod_string
;
1895 gui_side
= start_action(filer_window
, chmod_cb
, FALSE
);
1899 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Permissions");
1900 add_toggle(gui_side
,
1901 "Brief - don't list processed files", "B");
1902 add_toggle(gui_side
,
1903 "Recurse - also change contents of subdirectories", "R");
1904 hbox
= gtk_hbox_new(FALSE
, 0);
1905 label
= gtk_label_new("Command:");
1906 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1907 gui_side
->default_string
= &last_chmod_string
;
1908 gui_side
->entry
= gtk_entry_new();
1909 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_chmod_string
);
1910 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1911 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1912 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1913 entry_changed
, gui_side
);
1914 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
1915 button
= gtk_button_new_with_label("Show command reference");
1916 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 4);
1917 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1918 show_chmod_help
, NULL
);
1920 number_of_windows
++;
1921 gtk_widget_show_all(gui_side
->window
);
1924 void action_copy(GSList
*paths
, char *dest
, char *leaf
)
1930 action_do_func
= do_copy
;
1931 gui_side
= start_action(paths
, list_cb
, o_auto_copy
);
1935 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Copy");
1936 number_of_windows
++;
1937 gtk_widget_show_all(gui_side
->window
);
1940 void action_move(GSList
*paths
, char *dest
)
1945 action_do_func
= do_move
;
1946 gui_side
= start_action(paths
, list_cb
, o_auto_move
);
1950 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Move");
1951 number_of_windows
++;
1952 gtk_widget_show_all(gui_side
->window
);
1955 void action_link(GSList
*paths
, char *dest
)
1960 action_do_func
= do_link
;
1961 gui_side
= start_action(paths
, list_cb
, o_auto_link
);
1965 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Link");
1966 number_of_windows
++;
1967 gtk_widget_show_all(gui_side
->window
);
1970 void action_init(void)
1972 options_sections
= g_slist_prepend(options_sections
, &options
);
1973 option_register("action_auto_quiet", action_auto_quiet
);