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"
50 static GtkWidget
*create_options();
51 static void update_options();
52 static void set_options();
53 static void save_options();
54 static char *action_auto_quiet(char *data
);
56 static OptionsSection options
=
58 "Action window options",
65 static gboolean o_auto_copy
= TRUE
;
66 static gboolean o_auto_move
= TRUE
;
67 static gboolean o_auto_link
= TRUE
;
68 static gboolean o_auto_delete
= FALSE
;
69 static gboolean o_auto_mount
= TRUE
;
71 static GtkWidget
*w_auto_copy
= NULL
;
72 static GtkWidget
*w_auto_move
= NULL
;
73 static GtkWidget
*w_auto_link
= NULL
;
74 static GtkWidget
*w_auto_delete
= NULL
;
75 static GtkWidget
*w_auto_mount
= NULL
;
77 static GdkColor red
= {0, 0xffff, 0, 0};
79 /* Parent->Child messages are one character each:
81 * Q/Y/N Quiet/Yes/No button clicked
82 * F Force deletion of non-writeable items
85 #define SENSITIVE_YESNO(gui_side, state) \
87 gtk_widget_set_sensitive((gui_side)->yes, state); \
88 gtk_widget_set_sensitive((gui_side)->no, state); \
89 if ((gui_side)->entry) \
90 gtk_widget_set_sensitive((gui_side)->entry, state);\
93 #define ON(flag) ((flag) ? "on" : "off")
95 typedef struct _GUIside GUIside
;
96 typedef void ActionChild(gpointer data
);
97 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
101 int from_child
; /* File descriptor */
103 int input_tag
; /* gdk_input_add() */
104 GtkWidget
*vbox
, *log
, *window
, *dir
;
105 GtkWidget
*quiet
, *yes
, *no
;
106 GtkWidget
*entry
; /* May be NULL */
107 int child
; /* Process ID */
109 gboolean show_info
; /* For Disk Usage */
111 char *next_dir
; /* NULL => no timer active */
115 /* These don't need to be in a structure because we fork() before
118 static int from_parent
= 0;
119 static FILE *to_parent
= NULL
;
120 static gboolean quiet
= FALSE
;
121 static GString
*message
= NULL
;
122 static char *action_dest
= NULL
;
123 static char *action_leaf
= NULL
;
124 static gboolean (*action_do_func
)(char *source
, char *dest
);
125 static size_t size_tally
; /* For Disk Usage */
126 static DirItem
*mount_item
;
127 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
129 static gboolean o_force
= FALSE
;
130 static gboolean o_brief
= FALSE
;
131 static gboolean o_recurse
= FALSE
;
133 /* This is the last chmod command set, so that bringing up a new
134 * chmod box defaults to the last used command.
136 static guchar
*chmod_command
= NULL
;
138 /* Static prototypes */
139 static gboolean
send();
140 static gboolean
send_error();
141 static gboolean
send_dir(char *dir
);
142 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
143 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
);
144 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
);
145 static gboolean
reply(int fd
, gboolean ignore_quiet
);
149 /* Build up some option widgets to go in the options dialog, but don't
152 static GtkWidget
*create_options()
154 GtkWidget
*vbox
, *hbox
, *label
;
156 vbox
= gtk_vbox_new(FALSE
, 0);
157 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
159 label
= gtk_label_new("Auto-start (Quiet) these actions:");
160 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, TRUE
, 0);
162 hbox
= gtk_hbox_new(TRUE
, 0);
163 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
165 w_auto_copy
= gtk_check_button_new_with_label("Copy");
166 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_copy
, FALSE
, TRUE
, 0);
167 w_auto_move
= gtk_check_button_new_with_label("Move");
168 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_move
, FALSE
, TRUE
, 0);
169 w_auto_link
= gtk_check_button_new_with_label("Link");
170 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_link
, FALSE
, TRUE
, 0);
171 w_auto_delete
= gtk_check_button_new_with_label("Delete");
172 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_delete
, FALSE
, TRUE
, 0);
173 w_auto_mount
= gtk_check_button_new_with_label("Mount");
174 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_mount
, FALSE
, TRUE
, 0);
179 /* Reflect current state by changing the widgets in the options box */
180 static void update_options()
182 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy
),
184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move
),
186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link
),
188 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete
),
190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount
),
194 /* Set current values by reading the states of the widgets in the options box */
195 static void set_options()
197 o_auto_copy
= gtk_toggle_button_get_active(
198 GTK_TOGGLE_BUTTON(w_auto_copy
));
199 o_auto_move
= gtk_toggle_button_get_active(
200 GTK_TOGGLE_BUTTON(w_auto_move
));
201 o_auto_link
= gtk_toggle_button_get_active(
202 GTK_TOGGLE_BUTTON(w_auto_link
));
203 o_auto_delete
= gtk_toggle_button_get_active(
204 GTK_TOGGLE_BUTTON(w_auto_delete
));
205 o_auto_mount
= gtk_toggle_button_get_active(
206 GTK_TOGGLE_BUTTON(w_auto_mount
));
209 static void save_options()
211 guchar str
[] = "cmldt";
224 option_write("action_auto_quiet", str
);
227 static char *action_auto_quiet(char *data
)
248 o_auto_delete
= state
;
251 o_auto_mount
= state
;
254 return "Unknown flag";
263 /* This is called whenever the user edits the command - send the new command
264 * to the child process.
266 static void chmod_command_changed(GtkEntry
*entry
, GUIside
*gui_side
)
268 g_free(chmod_command
);
269 chmod_command
= g_strdup(gtk_entry_get_text(entry
));
271 if (!gui_side
->to_child
)
274 fputc('P', gui_side
->to_child
);
275 fputs(chmod_command
, gui_side
->to_child
);
276 fputc('\n', gui_side
->to_child
);
277 fflush(gui_side
->to_child
);
280 static void show_chmod_help(gpointer data
)
282 static GtkWidget
*help
= NULL
;
286 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
288 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
289 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
290 gtk_window_set_title(GTK_WINDOW(help
),
291 "Permissions command reference");
293 vbox
= gtk_vbox_new(FALSE
, 0);
294 gtk_container_add(GTK_CONTAINER(help
), vbox
);
296 text
= gtk_label_new(
297 "The format of a command is:\n"
298 "CHANGE, CHANGE, ...\n"
300 "WHO HOW PERMISSIONS\n"
301 "WHO is some combination of u, g and o which determines whether to\n"
302 "change the permissions for the User (owner), Group or Others.\n"
303 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
304 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
307 "u+rw (the file owner gains read and write permission)\n"
308 "g=u (the group permissions are set to be the same as the user's)\n"
309 "o=u-w (others get the same permissions as the owner, but without "
310 "write permission)\n"
311 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
312 "a+X (directories become accessable by everyone; files which were\n"
313 "executable by anyone become executable by everyone)\n"
314 "u+rw, go+r (two commands at once!)\n"
315 "u+s (set the SetUID bit - often has no effect on script files)\n"
316 "755 (set the permissions directly)\n"
318 "\nSee the chmod(1) man page for full details.");
319 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
320 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
322 hbox
= gtk_hbox_new(FALSE
, 20);
323 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
325 sep
= gtk_hseparator_new();
326 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
327 button
= gtk_button_new_with_label("Close");
328 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
329 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
330 gtk_widget_hide
, GTK_OBJECT(help
));
332 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
333 gtk_widget_hide
, GTK_OBJECT(help
));
336 if (GTK_WIDGET_VISIBLE(help
))
337 gtk_widget_hide(help
);
338 gtk_widget_show_all(help
);
341 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
342 * (or the two are the same directory)
344 static gboolean
is_sub_dir(char *sub
, char *parent
)
347 guchar
*real_sub
, *real_parent
;
350 real_sub
= pathdup(sub
);
351 real_parent
= pathdup(parent
);
353 parent_len
= strlen(real_parent
);
354 if (strncmp(real_parent
, real_sub
, parent_len
))
358 /* real_sub is at least as long as real_parent and all
359 * characters upto real_parent's length match.
362 retval
= real_sub
[parent_len
] == '\0' ||
363 real_sub
[parent_len
] == '/';
372 static gboolean
display_dir(gpointer data
)
374 GUIside
*gui_side
= (GUIside
*) data
;
376 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
377 g_free(gui_side
->next_dir
);
378 gui_side
->next_dir
= NULL
;
383 /* Called when the child sends us a message */
384 static void message_from_child(gpointer data
,
386 GdkInputCondition condition
)
389 GUIside
*gui_side
= (GUIside
*) data
;
390 GtkWidget
*log
= gui_side
->log
;
392 if (read_exact(source
, buf
, 4))
398 message_len
= strtol(buf
, NULL
, 16);
399 buffer
= g_malloc(message_len
+ 1);
400 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
402 buffer
[message_len
] = '\0';
404 SENSITIVE_YESNO(gui_side
, TRUE
);
405 else if (*buffer
== '+')
407 refresh_dirs(buffer
+ 1);
411 else if (*buffer
== 'm')
413 filer_check_mounted(buffer
+ 1);
417 else if (*buffer
== '/')
419 if (gui_side
->next_dir
)
420 g_free(gui_side
->next_dir
);
422 gui_side
->next_timer
=
426 gui_side
->next_dir
= buffer
;
429 else if (*buffer
== '!')
432 gtk_text_insert(GTK_TEXT(log
),
434 *buffer
== '!' ? &red
: NULL
,
436 buffer
+ 1, message_len
- 1);
440 g_print("Child died in the middle of a message.\n");
443 /* The child is dead */
446 fclose(gui_side
->to_child
);
447 gui_side
->to_child
= NULL
;
448 close(gui_side
->from_child
);
449 gdk_input_remove(gui_side
->input_tag
);
450 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
452 if (gui_side
->errors
)
455 report
= g_string_new(NULL
);
456 g_string_sprintf(report
, "There %s %d error%s.\n",
457 gui_side
->errors
== 1 ? "was" : "were",
459 gui_side
->errors
== 1 ? "" : "s");
460 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
461 report
->str
, report
->len
);
463 g_string_free(report
, TRUE
);
465 else if (gui_side
->show_info
== FALSE
)
466 gtk_widget_destroy(gui_side
->window
);
469 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
470 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
474 GSList
*list
= NULL
, *next
;
476 d
= opendir(src_dir
);
485 while ((ent
= readdir(d
)))
487 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
488 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
490 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
502 if (cb((char *) next
->data
, dest_path
))
504 g_string_sprintf(message
, "+%s", dest_path
);
515 /* Read this many bytes into the buffer. TRUE on success. */
516 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
521 got
= read(source
, buffer
, len
);
530 /* Send 'message' to our parent process. TRUE on success. */
531 static gboolean
send()
536 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
538 sprintf(len_buffer
, "%04x", message
->len
);
539 fwrite(len_buffer
, 1, 4, to_parent
);
540 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
542 return len
== message
->len
;
545 /* Set the directory indicator at the top of the window */
546 static gboolean
send_dir(char *dir
)
548 g_string_sprintf(message
, "/%s", dir
);
552 static gboolean
send_error()
554 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
558 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
562 if (!gui_side
->to_child
)
565 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
566 g_return_if_fail(text
!= NULL
);
567 fputc(*text
, gui_side
->to_child
);
568 fflush(gui_side
->to_child
);
570 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
571 SENSITIVE_YESNO(gui_side
, FALSE
);
574 static void process_flag(char flag
)
585 o_recurse
= !o_recurse
;
591 g_string_sprintf(message
,
592 "!ERROR: Bad message '%c'\n", flag
);
598 /* If the parent has sent any flag toggles, read them */
599 static void check_flags(void)
610 FD_SET(from_parent
, &set
);
613 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
616 g_error("select() failed: %s\n", g_strerror(errno
));
620 got
= read(from_parent
, &retval
, 1);
622 g_error("read() error: %s\n", g_strerror(errno
));
624 process_flag(retval
);
628 static void read_new_chmod_command(void)
634 new = g_string_new(NULL
);
638 len
= read(from_parent
, &c
, 1);
641 fprintf(stderr
, "read() error: %s\n",
643 _exit(1); /* Parent died? */
648 g_string_append_c(new, c
);
652 mode_free(mode_change
);
653 mode_change
= mode_compile(new->str
, MODE_MASK_ALL
);
654 g_string_free(new, TRUE
);
657 /* Read until the user sends a reply. If ignore_quiet is TRUE then
658 * the user MUST click Yes or No, else treat quiet on as Yes.
659 * If the user needs prompting then does send().
661 static gboolean
reply(int fd
, gboolean ignore_quiet
)
665 gboolean asked
= FALSE
;
667 while (ignore_quiet
|| !quiet
)
675 len
= read(fd
, &retval
, 1);
678 fprintf(stderr
, "read() error: %s\n",
680 _exit(1); /* Parent died? */
689 g_string_assign(message
, "?");
694 g_string_assign(message
, "' Yes\n");
698 g_string_assign(message
, "' No\n");
702 read_new_chmod_command();
705 process_flag(retval
);
712 g_string_assign(message
, "' Quiet\n");
718 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
720 GUIside
*gui_side
= (GUIside
*) data
;
724 kill(gui_side
->child
, SIGTERM
);
725 fclose(gui_side
->to_child
);
726 close(gui_side
->from_child
);
727 gdk_input_remove(gui_side
->input_tag
);
730 if (gui_side
->next_dir
)
732 gtk_timeout_remove(gui_side
->next_timer
);
733 g_free(gui_side
->next_dir
);
737 if (--number_of_windows
< 1)
741 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
742 * (NULL on failure). The child calls func().
744 * If autoq then automatically selects 'Quiet'.
746 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
748 int filedes
[4]; /* 0 and 2 are for reading */
751 GtkWidget
*vbox
, *button
, *hbox
, *scrollbar
, *actions
;
752 struct sigaction act
;
756 report_error("ROX-Filer", g_strerror(errno
));
760 if (pipe(filedes
+ 2))
764 report_error("ROX-Filer", g_strerror(errno
));
772 report_error("ROX-Filer", g_strerror(errno
));
775 /* We are the child */
779 /* Reset the SIGCHLD handler */
780 act
.sa_handler
= SIG_DFL
;
781 sigemptyset(&act
.sa_mask
);
783 sigaction(SIGCHLD
, &act
, NULL
);
785 message
= g_string_new(NULL
);
788 to_parent
= fdopen(filedes
[1], "wb");
789 from_parent
= filedes
[2];
794 /* We are the parent */
797 gui_side
= g_malloc(sizeof(GUIside
));
798 gui_side
->from_child
= filedes
[0];
799 gui_side
->to_child
= fdopen(filedes
[3], "wb");
800 gui_side
->log
= NULL
;
801 gui_side
->child
= child
;
802 gui_side
->errors
= 0;
803 gui_side
->show_info
= FALSE
;
804 gui_side
->entry
= NULL
;
806 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
807 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
808 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
809 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
810 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
812 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
813 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
815 gui_side
->dir
= gtk_label_new("<dir>");
816 gui_side
->next_dir
= NULL
;
817 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
818 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
820 hbox
= gtk_hbox_new(FALSE
, 0);
821 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 4);
823 gui_side
->log
= gtk_text_new(NULL
, NULL
);
824 gtk_widget_set_usize(gui_side
->log
, 400, 100);
825 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->log
, TRUE
, TRUE
, 0);
826 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
827 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
829 actions
= gtk_hbox_new(TRUE
, 4);
830 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
832 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label("Quiet");
833 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
834 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
835 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
836 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
837 button_reply
, gui_side
);
838 gui_side
->yes
= button
= gtk_button_new_with_label("Yes");
839 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
840 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
841 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
842 button_reply
, gui_side
);
843 gui_side
->no
= button
= gtk_button_new_with_label("No");
844 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
845 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
846 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
847 button_reply
, gui_side
);
848 SENSITIVE_YESNO(gui_side
, FALSE
);
850 button
= gtk_button_new_with_label("Abort");
851 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
852 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
853 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
855 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
863 /* ACTIONS ON ONE ITEM */
865 /* These may call themselves recursively, or ask questions, etc.
866 * TRUE iff the directory containing dest_path needs to be rescanned.
869 /* dest_path is the dir containing src_path.
870 * Updates the global size_tally.
872 static gboolean
do_usage(char *src_path
, char *dest_path
)
878 if (lstat(src_path
, &info
))
880 g_string_sprintf(message
, "'%s:\n", src_path
);
886 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
887 size_tally
+= info
.st_size
;
888 else if (S_ISDIR(info
.st_mode
))
890 g_string_sprintf(message
, "?Count contents of %s?",
892 if (reply(from_parent
, FALSE
))
895 safe_path
= g_strdup(src_path
);
896 for_dir_contents(do_usage
, safe_path
, safe_path
);
904 /* dest_path is the dir containing src_path */
905 static gboolean
do_delete(char *src_path
, char *dest_path
)
912 if (lstat(src_path
, &info
))
918 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
919 : access(src_path
, W_OK
) != 0;
920 if (write_prot
|| !quiet
)
922 g_string_sprintf(message
, "?Delete %s'%s'?",
923 write_prot
? "WRITE-PROTECTED " : " ",
925 if (!reply(from_parent
, write_prot
&& !o_force
))
930 g_string_sprintf(message
, "'Deleting '%s'\n", src_path
);
934 if (S_ISDIR(info
.st_mode
))
937 safe_path
= g_strdup(src_path
);
938 for_dir_contents(do_delete
, safe_path
, safe_path
);
939 if (rmdir(safe_path
))
945 g_string_sprintf(message
, "'Directory '%s' deleted\n",
948 g_string_sprintf(message
, "m%s", safe_path
);
952 else if (unlink(src_path
))
961 /* path is the item to change. If is is a directory then we may recurse
962 * (depending on the recurse flag).
964 static gboolean
do_chmod(char *path
, char *dummy
)
971 if (lstat(path
, &info
))
976 if (S_ISLNK(info
.st_mode
))
981 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
982 if (!reply(from_parent
, FALSE
))
987 g_string_sprintf(message
, "'Changing permissions of '%s'\n",
994 g_string_assign(message
,
995 "!Invalid mode command - change it and try again\n");
997 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
998 if (!reply(from_parent
, TRUE
))
1002 if (lstat(path
, &info
))
1007 if (S_ISLNK(info
.st_mode
))
1010 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1011 if (chmod(path
, new_mode
))
1017 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1020 safe_path
= g_strdup(path
);
1021 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1028 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1029 * is set then that is the new leafname, otherwise the leafname stays
1032 static char *make_dest_path(char *object
, char *dir
)
1040 leaf
= strrchr(object
, '/');
1042 leaf
= object
; /* Error? */
1047 return make_path(dir
, leaf
)->str
;
1050 /* If action_leaf is not NULL it specifies the new leaf name
1052 static gboolean
do_copy2(char *path
, char *dest
)
1056 struct stat dest_info
;
1057 gboolean retval
= TRUE
;
1061 dest_path
= make_dest_path(path
, dest
);
1063 if (lstat(path
, &info
))
1069 if (lstat(dest_path
, &dest_info
) == 0)
1074 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1076 g_string_sprintf(message
, "?'%s' already exists - %s?",
1078 merge
? "merge contents" : "overwrite");
1080 if (!reply(from_parent
, TRUE
))
1085 if (S_ISDIR(dest_info
.st_mode
))
1086 err
= rmdir(dest_path
);
1088 err
= unlink(dest_path
);
1093 if (errno
!= ENOENT
)
1095 g_string_sprintf(message
,
1096 "'Trying copy anyway...\n");
1103 g_string_sprintf(message
, "?Copy %s as %s?", path
, dest_path
);
1104 if (!reply(from_parent
, FALSE
))
1109 g_string_sprintf(message
, "'Copying %s as %s\n", path
,
1114 if (S_ISDIR(info
.st_mode
))
1116 char *safe_path
, *safe_dest
;
1117 struct stat dest_info
;
1120 /* (we will do the update ourselves now, rather than
1125 safe_path
= g_strdup(path
);
1126 safe_dest
= g_strdup(dest_path
);
1128 exists
= !lstat(dest_path
, &dest_info
);
1130 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1132 g_string_sprintf(message
,
1133 "!ERROR: Destination already exists, "
1134 "but is not a directory\n");
1136 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
1142 /* (just been created then) */
1143 g_string_sprintf(message
, "+%s", dest
);
1148 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1149 /* Note: dest_path now invalid... */
1155 else if (S_ISLNK(info
.st_mode
))
1157 char target
[MAXPATHLEN
+ 1];
1160 /* Not all versions of cp(1) can make symlinks,
1161 * so we special-case it.
1164 count
= readlink(path
, target
, sizeof(target
) - 1);
1172 target
[count
] = '\0';
1173 if (symlink(target
, dest_path
))
1182 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
1185 argv
[3] = dest_path
;
1187 if (fork_exec_wait(argv
))
1189 g_string_sprintf(message
, "!ERROR: Copy failed\n");
1198 /* Copy path to dest.
1199 * Check that path not copied into itself.
1201 static gboolean
do_copy(char *path
, char *dest
)
1203 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1205 g_string_sprintf(message
,
1206 "!ERROR: Can't copy directory into itself\n");
1210 return do_copy2(path
, dest
);
1213 /* Move path to dest.
1214 * Check that path not moved into itself.
1216 static gboolean
do_move(char *path
, char *dest
)
1220 gboolean retval
= TRUE
;
1221 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1225 if (is_sub_dir(dest
, path
))
1227 g_string_sprintf(message
,
1228 "!ERROR: Can't move directory into itself\n");
1235 leaf
= strrchr(path
, '/');
1237 leaf
= path
; /* Error? */
1241 dest_path
= make_path(dest
, leaf
)->str
;
1243 is_dir
= lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1245 if (access(dest_path
, F_OK
) == 0)
1250 g_string_sprintf(message
, "?'%s' already exists - overwrite?",
1252 if (!reply(from_parent
, TRUE
))
1255 if (lstat(dest_path
, &info
))
1261 if (S_ISDIR(info
.st_mode
))
1262 err
= rmdir(dest_path
);
1264 err
= unlink(dest_path
);
1269 if (errno
!= ENOENT
)
1271 g_string_sprintf(message
,
1272 "'Trying move anyway...\n");
1278 g_string_sprintf(message
, "?Move %s as %s?", path
, dest_path
);
1279 if (!reply(from_parent
, FALSE
))
1284 g_string_sprintf(message
, "'Moving %s as %s\n", path
,
1292 if (fork_exec_wait(argv
) == 0)
1294 g_string_sprintf(message
, "+%s", path
);
1295 g_string_truncate(message
, leaf
- path
);
1298 g_string_sprintf(message
, "m%s", path
);
1304 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
1313 static gboolean
do_link(char *path
, char *dest
)
1320 leaf
= strrchr(path
, '/');
1322 leaf
= path
; /* Error? */
1326 dest_path
= make_path(dest
, leaf
)->str
;
1330 g_string_sprintf(message
, "'Linking %s as %s\n", path
,
1336 g_string_sprintf(message
, "?Link %s as %s?", path
, dest_path
);
1337 if (!reply(from_parent
, FALSE
))
1341 if (symlink(path
, dest_path
))
1350 /* Mount/umount this item */
1351 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
)
1353 char *argv
[3] = {NULL
, NULL
, NULL
};
1358 argv
[0] = item
->flags
& ITEM_FLAG_MOUNTED
? "umount" : "mount";
1359 action
= item
->flags
& ITEM_FLAG_MOUNTED
? "Unmount" : "Mount";
1360 argv
[1] = make_path(filer_window
->path
, item
->leafname
)->str
;
1364 g_string_sprintf(message
, "'%sing %s\n", action
, argv
[1]);
1369 g_string_sprintf(message
, "?%s %s?", action
, argv
[1]);
1370 if (!reply(from_parent
, FALSE
))
1374 if (fork_exec_wait(argv
) == 0)
1376 g_string_sprintf(message
, "+%s", filer_window
->path
);
1378 g_string_sprintf(message
, "m%s", argv
[1]);
1383 g_string_sprintf(message
, "!ERROR: %s failed\n", action
);
1388 /* CHILD MAIN LOOPS */
1390 /* After forking, the child calls one of these functions */
1392 static void usage_cb(gpointer data
)
1394 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1395 Collection
*collection
= filer_window
->collection
;
1397 int left
= collection
->number_selected
;
1399 off_t total_size
= 0;
1401 send_dir(filer_window
->path
);
1406 if (!collection
->items
[i
].selected
)
1408 item
= (DirItem
*) collection
->items
[i
].data
;
1410 do_usage(make_path(filer_window
->path
,
1411 item
->leafname
)->str
,
1412 filer_window
->path
);
1413 g_string_sprintf(message
, "'%s: %s\n",
1415 format_size((unsigned long) size_tally
));
1417 total_size
+= size_tally
;
1421 g_string_sprintf(message
, "'\nTotal: %s\n",
1422 format_size((unsigned long) total_size
));
1426 #ifdef DO_MOUNT_POINTS
1427 static void mount_cb(gpointer data
)
1429 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1430 Collection
*collection
= filer_window
->collection
;
1433 gboolean mount_points
= FALSE
;
1435 send_dir(filer_window
->path
);
1438 do_mount(filer_window
, mount_item
);
1441 for (i
= 0; i
< collection
->number_of_items
; i
++)
1443 if (!collection
->items
[i
].selected
)
1445 item
= (DirItem
*) collection
->items
[i
].data
;
1446 if (!(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
1448 mount_points
= TRUE
;
1450 do_mount(filer_window
, item
);
1455 g_string_sprintf(message
,
1456 "!No mount points selected!\n");
1461 g_string_sprintf(message
, "'\nDone\n");
1466 static void delete_cb(gpointer data
)
1468 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1469 Collection
*collection
= filer_window
->collection
;
1471 int left
= collection
->number_selected
;
1474 send_dir(filer_window
->path
);
1479 if (!collection
->items
[i
].selected
)
1481 item
= (DirItem
*) collection
->items
[i
].data
;
1482 if (do_delete(make_path(filer_window
->path
,
1483 item
->leafname
)->str
,
1484 filer_window
->path
))
1486 g_string_sprintf(message
, "+%s", filer_window
->path
);
1492 g_string_sprintf(message
, "'\nDone\n");
1496 static void chmod_cb(gpointer data
)
1498 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1499 Collection
*collection
= filer_window
->collection
;
1501 int left
= collection
->number_selected
;
1504 send_dir(filer_window
->path
);
1506 mode_change
= mode_compile(chmod_command
, MODE_MASK_ALL
);
1511 if (!collection
->items
[i
].selected
)
1513 item
= (DirItem
*) collection
->items
[i
].data
;
1514 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1516 g_string_sprintf(message
, "!'%s' is a symbolic link\n",
1520 else if (do_chmod(make_path(filer_window
->path
,
1521 item
->leafname
)->str
, NULL
))
1523 g_string_sprintf(message
, "+%s", filer_window
->path
);
1529 g_string_sprintf(message
, "'\nDone\n");
1533 static void list_cb(gpointer data
)
1535 GSList
*paths
= (GSList
*) data
;
1539 send_dir((char *) paths
->data
);
1541 if (action_do_func((char *) paths
->data
, action_dest
))
1543 g_string_sprintf(message
, "+%s", action_dest
);
1547 paths
= paths
->next
;
1550 g_string_sprintf(message
, "'\nDone\n");
1554 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1558 check
= gtk_check_button_new_with_label(label
);
1559 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1560 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1561 button_reply
, gui_side
);
1562 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1566 /* EXTERNAL INTERFACE */
1568 /* Count disk space used by selected items */
1569 void action_usage(FilerWindow
*filer_window
)
1572 Collection
*collection
;
1574 collection
= filer_window
->collection
;
1576 if (collection
->number_selected
< 1)
1578 report_error("ROX-Filer", "You need to select some items "
1583 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1587 gui_side
->show_info
= TRUE
;
1589 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Disk Usage");
1590 number_of_windows
++;
1591 gtk_widget_show_all(gui_side
->window
);
1594 /* Mount/unmount 'item', or all selected mount points if NULL. */
1595 void action_mount(FilerWindow
*filer_window
, DirItem
*item
)
1597 #ifdef DO_MOUNT_POINTS
1599 Collection
*collection
;
1601 collection
= filer_window
->collection
;
1603 if (item
== NULL
&& collection
->number_selected
< 1)
1605 report_error("ROX-Filer", "You need to select some items "
1606 "to mount or unmount");
1611 gui_side
= start_action(filer_window
, mount_cb
, o_auto_mount
);
1615 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Mount / Unmount");
1616 number_of_windows
++;
1617 gtk_widget_show_all(gui_side
->window
);
1619 report_error("ROX-Filer",
1620 "ROX-Filer does not yet support mount points on your "
1622 #endif /* DO_MOUNT_POINTS */
1625 /* Deletes all selected items in the window */
1626 void action_delete(FilerWindow
*filer_window
)
1629 Collection
*collection
;
1631 collection
= filer_window
->collection
;
1633 if (collection
->number_selected
< 1)
1635 report_error("ROX-Filer", "You need to select some items "
1640 gui_side
= start_action(filer_window
, delete_cb
, o_auto_delete
);
1644 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Delete");
1645 add_toggle(gui_side
,
1646 "Force - don't confirm deletion of non-writeable items", "F");
1647 add_toggle(gui_side
,
1648 "Brief - only log directories being deleted", "B");
1650 number_of_windows
++;
1651 gtk_widget_show_all(gui_side
->window
);
1654 /* Change the permissions of the selected items */
1655 void action_chmod(FilerWindow
*filer_window
)
1658 Collection
*collection
;
1659 GtkWidget
*hbox
, *label
, *button
;
1661 collection
= filer_window
->collection
;
1663 if (collection
->number_selected
< 1)
1665 report_error("ROX-Filer", "You need to select the items "
1666 "whose permissions you want to change");
1671 chmod_command
= g_strdup("a+x");
1672 gui_side
= start_action(filer_window
, chmod_cb
, FALSE
);
1676 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Permissions");
1677 add_toggle(gui_side
,
1678 "Brief - don't list processed files", "B");
1679 add_toggle(gui_side
,
1680 "Recurse - also change contents of subdirectories", "R");
1681 hbox
= gtk_hbox_new(FALSE
, 0);
1682 label
= gtk_label_new("Command:");
1683 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1684 gui_side
->entry
= gtk_entry_new();
1685 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), chmod_command
);
1686 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1687 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1688 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1689 chmod_command_changed
, gui_side
);
1690 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
1691 button
= gtk_button_new_with_label("Show command reference");
1692 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 4);
1693 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1694 show_chmod_help
, NULL
);
1696 number_of_windows
++;
1697 gtk_widget_show_all(gui_side
->window
);
1700 void action_copy(GSList
*paths
, char *dest
, char *leaf
)
1706 action_do_func
= do_copy
;
1707 gui_side
= start_action(paths
, list_cb
, o_auto_copy
);
1711 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Copy");
1712 number_of_windows
++;
1713 gtk_widget_show_all(gui_side
->window
);
1716 void action_move(GSList
*paths
, char *dest
)
1721 action_do_func
= do_move
;
1722 gui_side
= start_action(paths
, list_cb
, o_auto_move
);
1726 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Move");
1727 number_of_windows
++;
1728 gtk_widget_show_all(gui_side
->window
);
1731 void action_link(GSList
*paths
, char *dest
)
1736 action_do_func
= do_link
;
1737 gui_side
= start_action(paths
, list_cb
, o_auto_link
);
1741 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Link");
1742 number_of_windows
++;
1743 gtk_widget_show_all(gui_side
->window
);
1746 void action_init(void)
1748 options_sections
= g_slist_prepend(options_sections
, &options
);
1749 option_register("action_auto_quiet", action_auto_quiet
);