4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
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>
43 #include "gui_support.h"
48 #include "modechange.h"
54 /* Parent->Child messages are one character each:
56 * Y/N Yes/No button clicked
57 * F Force deletion of non-writeable items
59 * E Entry text changed
63 typedef struct _GUIside GUIside
;
64 typedef void ActionChild(gpointer data
);
65 typedef void ForDirCB(const char *path
, const char *dest_path
);
69 ABox
*abox
; /* The action window widget */
71 int from_child
; /* File descriptor */
73 int input_tag
; /* gdk_input_add() */
74 pid_t child
; /* Process ID */
75 int errors
; /* Number of errors so far */
76 gboolean show_info
; /* For Disk Usage */
78 guchar
**default_string
; /* Changed when the entry changes */
79 void (*entry_string_func
)(GtkWidget
*widget
,
80 const guchar
*string
);
83 /* These don't need to be in a structure because we fork() before
86 static gboolean mount_open_dir
= FALSE
;
87 static int from_parent
= 0;
88 static FILE *to_parent
= NULL
;
89 static gboolean quiet
= FALSE
;
90 static GString
*message
= NULL
;
91 static const char *action_dest
= NULL
;
92 static const char *action_leaf
= NULL
;
93 static void (*action_do_func
)(const char *source
, const char *dest
);
94 static double size_tally
; /* For Disk Usage */
95 static unsigned long dir_counter
; /* For Disk Usage */
96 static unsigned long file_counter
; /* For Disk Usage */
98 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
99 static FindCondition
*find_condition
= NULL
; /* For Find */
101 /* Only used by child */
102 static gboolean o_force
= FALSE
;
103 static gboolean o_brief
= FALSE
;
104 static gboolean o_recurse
= FALSE
;
105 static gboolean o_newer
= FALSE
;
107 static Option o_action_copy
, o_action_move
, o_action_link
;
108 static Option o_action_delete
, o_action_mount
;
109 static Option o_action_force
, o_action_brief
, o_action_recurse
;
110 static Option o_action_newer
;
112 /* Whenever the text in these boxes is changed we store a copy of the new
113 * string to be used as the default next time.
115 static guchar
*last_chmod_string
= NULL
;
116 static guchar
*last_find_string
= NULL
;
118 /* Set to one of the above before forking. This may change over a call to
119 * reply(). It is reset to NULL once the text is parsed.
121 static guchar
*new_entry_string
= NULL
;
123 /* Static prototypes */
124 static gboolean
send();
125 static gboolean
send_error();
126 static gboolean
send_dir(const char *dir
);
127 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
128 static void do_mount(guchar
*path
, gboolean mount
);
129 static gboolean
reply(int fd
, gboolean ignore_quiet
);
130 static gboolean
remove_pinned_ok(GList
*paths
);
135 /* This is called whenever the user edits the entry box (if any) - send the
138 static void entry_changed(GtkEditable
*entry
, GUIside
*gui_side
)
142 g_return_if_fail(gui_side
->default_string
!= NULL
);
144 text
= gtk_editable_get_chars(entry
, 0, -1);
146 if (gui_side
->entry_string_func
)
147 gui_side
->entry_string_func(GTK_WIDGET(entry
), text
);
149 g_free(*(gui_side
->default_string
));
150 *(gui_side
->default_string
) = text
; /* Gets text's ref */
152 if (!gui_side
->to_child
)
155 fputc('E', gui_side
->to_child
);
156 fputs(text
, gui_side
->to_child
);
157 fputc('\n', gui_side
->to_child
);
158 fflush(gui_side
->to_child
);
161 void show_condition_help(gpointer data
)
163 static GtkWidget
*help
= NULL
;
167 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
169 help
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
170 gtk_window_set_type_hint(GTK_WINDOW(help
),
171 GDK_WINDOW_TYPE_HINT_DIALOG
);
172 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
173 gtk_window_set_title(GTK_WINDOW(help
),
174 _("Find expression reference"));
176 vbox
= gtk_vbox_new(FALSE
, 0);
177 gtk_container_add(GTK_CONTAINER(help
), vbox
);
179 frame
= gtk_frame_new(_("Quick Start"));
180 text
= gtk_label_new(
181 _("Just put the name of the file you're looking for in single quotes:\n"
182 "'index.html' (to find a file called 'index.html')"));
183 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
184 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
185 gtk_container_add(GTK_CONTAINER(frame
), text
);
186 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
188 frame
= gtk_frame_new(_("Examples"));
189 text
= gtk_label_new(
190 _("'*.htm', '*.html' (finds HTML files)\n"
191 "IsDir 'lib' (finds directories called 'lib')\n"
192 "IsReg 'core' (finds a regular file called 'core')\n"
193 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
194 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
195 "'CVS' prune, isreg (a regular file not in CVS)\n"
196 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
197 gtk_widget_set_name(text
, "fixed-style");
198 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
199 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
200 gtk_container_add(GTK_CONTAINER(frame
), text
);
201 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
203 frame
= gtk_frame_new(_("Simple Tests"));
204 text
= gtk_label_new(
205 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
206 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
210 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
211 "contains a slash then the match is against the full path; otherwise it is \n"
212 "against the leafname only."));
213 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
214 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
215 gtk_container_add(GTK_CONTAINER(frame
), text
);
216 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
218 frame
= gtk_frame_new(_("Comparisons"));
219 text
= gtk_label_new(
220 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
221 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
222 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
223 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
224 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
225 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
226 gtk_container_add(GTK_CONTAINER(frame
), text
);
227 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
229 frame
= gtk_frame_new(_("Specials"));
230 text
= gtk_label_new(
231 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
232 "in 'command' is replaced with the path of the current file)\n"
233 "prune (false, and prevents searching the contents of a directory).")
235 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
236 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
237 gtk_container_add(GTK_CONTAINER(frame
), text
);
238 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
240 hbox
= gtk_hbox_new(FALSE
, 20);
241 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
243 text
= gtk_label_new(
244 _("See the ROX-Filer manual for full details."));
245 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
246 button
= gtk_button_new_with_label(_("Close"));
247 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
248 g_signal_connect_swapped(button
, "clicked",
249 G_CALLBACK(gtk_widget_hide
), help
);
251 g_signal_connect_swapped(help
, "delete_event",
252 G_CALLBACK(gtk_widget_hide
), help
);
255 if (GTK_WIDGET_VISIBLE(help
))
256 gtk_widget_hide(help
);
257 gtk_widget_show_all(help
);
260 static void show_chmod_help(gpointer data
)
262 static GtkWidget
*help
= NULL
;
266 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
268 help
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
269 gtk_window_set_type_hint(GTK_WINDOW(help
),
270 GDK_WINDOW_TYPE_HINT_DIALOG
);
271 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
272 gtk_window_set_title(GTK_WINDOW(help
),
273 _("Permissions command reference"));
275 vbox
= gtk_vbox_new(FALSE
, 0);
276 gtk_container_add(GTK_CONTAINER(help
), vbox
);
278 text
= gtk_label_new(
279 _("Normally, you can just select a command from the menu (click \n"
280 "on the arrow beside the command box). Sometimes, you need more...\n"
282 "The format of a command is:\n"
283 "CHANGE, CHANGE, ...\n"
285 "WHO HOW PERMISSIONS\n"
286 "WHO is some combination of u, g and o which determines whether to\n"
287 "change the permissions for the User (owner), Group or Others.\n"
288 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
289 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
291 "Bracketed text and spaces are ignored.\n\n"
294 "u+rw (the file owner gains read and write permission)\n"
295 "g=u (the group permissions are set to be the same as the user's)\n"
296 "o=u-w (others get the same permissions as the owner, but without "
297 "write permission)\n"
298 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
299 "a+X (directories become accessable by everyone; files which were\n"
300 "executable by anyone become executable by everyone)\n"
301 "u+rw, go+r (two commands at once!)\n"
302 "u+s (set the SetUID bit - often has no effect on script files)\n"
303 "755 (set the permissions directly)\n"
305 "\nSee the chmod(1) man page for full details."));
306 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
307 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
309 hbox
= gtk_hbox_new(FALSE
, 20);
310 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
312 sep
= gtk_hseparator_new();
313 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
314 button
= gtk_button_new_with_label(_("Close"));
315 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
316 g_signal_connect_swapped(button
, "clicked",
317 G_CALLBACK(gtk_widget_hide
), help
);
319 g_signal_connect_swapped(help
, "delete_event",
320 G_CALLBACK(gtk_widget_hide
), help
);
323 if (GTK_WIDGET_VISIBLE(help
))
324 gtk_widget_hide(help
);
325 gtk_widget_show_all(help
);
328 static void process_message(GUIside
*gui_side
, const gchar
*buffer
)
330 ABox
*abox
= gui_side
->abox
;
333 abox_ask(abox
, buffer
+ 1);
334 else if (*buffer
== 's')
335 dir_check_this(buffer
+ 1); /* Update this item */
336 else if (*buffer
== '=')
337 abox_add_filename(abox
, buffer
+ 1);
338 else if (*buffer
== '#')
339 abox_clear_results(abox
);
340 else if (*buffer
== 'm' || *buffer
== 'M')
342 /* Mount / major changes to this path */
345 filer_check_mounted(buffer
+ 1);
347 else if (*buffer
== '/')
348 abox_set_current_object(abox
, buffer
+ 1);
349 else if (*buffer
== 'o')
350 filer_opendir(buffer
+ 1, NULL
);
351 else if (*buffer
== '!')
354 abox_log(abox
, buffer
+ 1, "error");
357 abox_log(abox
, buffer
+ 1, NULL
);
360 /* Called when the child sends us a message */
361 static void message_from_child(gpointer data
,
363 GdkInputCondition condition
)
366 GUIside
*gui_side
= (GUIside
*) data
;
367 ABox
*abox
= gui_side
->abox
;
368 GtkTextBuffer
*text_buffer
;
370 text_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox
->log
));
372 if (read_exact(source
, buf
, 4))
378 message_len
= strtol(buf
, NULL
, 16);
379 buffer
= g_malloc(message_len
+ 1);
380 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
382 buffer
[message_len
] = '\0';
383 process_message(gui_side
, buffer
);
387 g_printerr("Child died in the middle of a message.\n");
390 /* The child is dead */
393 fclose(gui_side
->to_child
);
394 gui_side
->to_child
= NULL
;
395 close(gui_side
->from_child
);
396 g_source_remove(gui_side
->input_tag
);
397 abox_cancel_ask(gui_side
->abox
);
399 if (gui_side
->errors
)
403 if (gui_side
->errors
== 1)
404 report
= g_strdup(_("There was one error.\n"));
406 report
= g_strdup_printf(_("There were %d errors.\n"),
409 gtk_text_buffer_insert_at_cursor(text_buffer
, report
, -1);
413 else if (gui_side
->show_info
== FALSE
)
414 gtk_widget_destroy(GTK_WIDGET(gui_side
->abox
));
417 /* Scans src_dir, calling cb(item, dest_path) for each item */
418 static void for_dir_contents(ForDirCB
*cb
,
420 const char *dest_path
)
424 GList
*list
= NULL
, *next
;
426 d
= mc_opendir(src_dir
);
429 /* Message displayed is "ERROR reading 'path': message" */
430 g_string_sprintf(message
, "!%s '%s': %s\n",
432 src_dir
, g_strerror(errno
));
439 while ((ent
= mc_readdir(d
)))
441 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
442 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
444 list
= g_list_append(list
, g_strdup(make_path(src_dir
,
456 cb((char *) next
->data
, dest_path
);
464 /* Read this many bytes into the buffer. TRUE on success. */
465 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
470 got
= read(source
, buffer
, len
);
479 /* Send 'message' to our parent process. TRUE on success. */
480 static gboolean
send(void)
485 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
487 sprintf(len_buffer
, "%04x", message
->len
);
488 fwrite(len_buffer
, 1, 4, to_parent
);
489 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
491 return len
== message
->len
;
494 /* Set the directory indicator at the top of the window */
495 static gboolean
send_dir(const char *dir
)
497 g_string_sprintf(message
, "/%s", dir
);
501 static gboolean
send_error(void)
503 g_string_sprintf(message
, "!%s: %s\n", _("ERROR"), g_strerror(errno
));
507 /* Send 'Quiet' if possible, 'Yes' otherwise */
509 static void find_return_pressed(GtkWidget
*button
, GUIside
*gui_side
)
511 if (GTK_WIDGET_SENSITIVE(gui_side
->quiet
))
512 gtk_button_clicked(GTK_BUTTON(gui_side
->quiet
));
513 else if (GTK_WIDGET_SENSITIVE(gui_side
->yes
))
514 gtk_button_clicked(GTK_BUTTON(gui_side
->yes
));
520 static void response(GtkDialog
*dialog
, gint response
, GUIside
*gui_side
)
524 if (!gui_side
->to_child
)
527 if (response
== GTK_RESPONSE_YES
)
529 else if (response
== GTK_RESPONSE_NO
)
534 fputc(code
, gui_side
->to_child
);
535 fflush(gui_side
->to_child
);
538 static void flag_toggled(ABox
*abox
, gint flag
, GUIside
*gui_side
)
540 if (!gui_side
->to_child
)
543 fputc(flag
, gui_side
->to_child
);
544 fflush(gui_side
->to_child
);
547 static void read_new_entry_text(void)
553 new = g_string_new(NULL
);
557 len
= read(from_parent
, &c
, 1);
560 fprintf(stderr
, "read() error: %s\n",
562 _exit(1); /* Parent died? */
567 g_string_append_c(new, c
);
570 g_free(new_entry_string
);
571 new_entry_string
= new->str
;
572 g_string_free(new, FALSE
);
575 static void process_flag(char flag
)
586 o_recurse
= !o_recurse
;
595 read_new_entry_text();
598 g_string_sprintf(message
,
599 "!ERROR: Bad message '%c'\n", flag
);
605 /* If the parent has sent any flag toggles, read them */
606 static void check_flags(void)
617 FD_SET(from_parent
, &set
);
620 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
623 g_error("select() failed: %s\n", g_strerror(errno
));
627 got
= read(from_parent
, &retval
, 1);
629 g_error("read() error: %s\n", g_strerror(errno
));
631 process_flag(retval
);
635 /* Read until the user sends a reply. If ignore_quiet is TRUE then
636 * the user MUST click Yes or No, else treat quiet on as Yes.
637 * If the user needs prompting then does send().
639 static gboolean
reply(int fd
, gboolean ignore_quiet
)
644 if (quiet
&& !ignore_quiet
)
651 len
= read(fd
, &retval
, 1);
654 fprintf(stderr
, "read() error: %s\n",
656 _exit(1); /* Parent died? */
662 g_string_sprintf(message
, "' %s\n", _("Yes"));
666 g_string_sprintf(message
, "' %s\n", _("No"));
670 process_flag(retval
);
676 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
678 GUIside
*gui_side
= (GUIside
*) data
;
682 kill(gui_side
->child
, SIGTERM
);
683 fclose(gui_side
->to_child
);
684 close(gui_side
->from_child
);
685 g_source_remove(gui_side
->input_tag
);
690 if (--number_of_windows
< 1)
694 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
695 * (NULL on failure). The child calls func().
697 static GUIside
*start_action(GtkWidget
*abox
, ActionChild
*func
, gpointer data
,
698 int force
, int brief
, int recurse
, int newer
)
701 int filedes
[4]; /* 0 and 2 are for reading */
704 struct sigaction act
;
708 report_error("pipe: %s", g_strerror(errno
));
709 gtk_widget_destroy(abox
);
713 if (pipe(filedes
+ 2))
717 report_error("pipe: %s", g_strerror(errno
));
718 gtk_widget_destroy(abox
);
722 autoq
= gtk_toggle_button_get_active(
723 GTK_TOGGLE_BUTTON(ABOX(abox
)->quiet
));
734 report_error("fork: %s", g_strerror(errno
));
735 gtk_widget_destroy(abox
);
738 /* We are the child */
742 /* Reset the SIGCHLD handler */
743 act
.sa_handler
= SIG_DFL
;
744 sigemptyset(&act
.sa_mask
);
746 sigaction(SIGCHLD
, &act
, NULL
);
748 message
= g_string_new(NULL
);
751 to_parent
= fdopen(filedes
[1], "wb");
752 from_parent
= filedes
[2];
758 /* We are the parent */
761 gui_side
= g_malloc(sizeof(GUIside
));
762 gui_side
->from_child
= filedes
[0];
763 gui_side
->to_child
= fdopen(filedes
[3], "wb");
764 gui_side
->child
= child
;
765 gui_side
->errors
= 0;
766 gui_side
->show_info
= FALSE
;
767 gui_side
->default_string
= NULL
;
768 gui_side
->entry_string_func
= NULL
;
770 gui_side
->abox
= ABOX(abox
);
771 g_signal_connect(abox
, "destroy",
772 G_CALLBACK(destroy_action_window
), gui_side
);
774 g_signal_connect(abox
, "response", G_CALLBACK(response
), gui_side
);
775 g_signal_connect(abox
, "flag_toggled",
776 G_CALLBACK(flag_toggled
), gui_side
);
778 gui_side
->input_tag
= gtk_input_add_full(gui_side
->from_child
,
781 NULL
, gui_side
, NULL
);
786 /* ACTIONS ON ONE ITEM */
788 /* These may call themselves recursively, or ask questions, etc */
790 /* Updates the global size_tally, file_counter and dir_counter */
791 static void do_usage(const char *src_path
, const char *unused
)
797 if (mc_lstat(src_path
, &info
))
799 g_string_sprintf(message
, "'%s:\n", src_path
);
803 else if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
806 size_tally
+= info
.st_size
;
808 else if (S_ISDIR(info
.st_mode
))
811 g_string_sprintf(message
, _("?Count contents of %s?"),
813 if (reply(from_parent
, FALSE
))
816 safe_path
= g_strdup(src_path
);
817 for_dir_contents(do_usage
, safe_path
, safe_path
);
825 /* dest_path is the dir containing src_path */
826 static void do_delete(const char *src_path
, const char *unused
)
834 if (mc_lstat(src_path
, &info
))
840 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
841 : access(src_path
, W_OK
) != 0;
842 if (write_prot
|| !quiet
)
844 g_string_sprintf(message
, _("?Delete %s'%s'?"),
845 write_prot
? _("WRITE-PROTECTED ") : "",
847 if (!reply(from_parent
, write_prot
&& !o_force
))
852 g_string_sprintf(message
, _("'Deleting '%s'\n"), src_path
);
856 safe_path
= g_strdup(src_path
);
858 if (S_ISDIR(info
.st_mode
))
860 for_dir_contents(do_delete
, safe_path
, safe_path
);
861 if (rmdir(safe_path
))
867 g_string_sprintf(message
, _("'Directory '%s' deleted\n"),
870 g_string_sprintf(message
, "m%s", safe_path
);
873 else if (unlink(src_path
))
877 g_string_sprintf(message
, "s%s", safe_path
);
884 /* path is the item to check. If is is a directory then we may recurse
885 * (unless prune is used).
887 static void do_find(const char *path
, const char *unused
)
895 g_string_sprintf(message
, _("?Check '%s'?"), path
);
896 if (!reply(from_parent
, FALSE
))
902 if (new_entry_string
)
905 find_condition_free(find_condition
);
906 find_condition
= find_compile(new_entry_string
);
907 g_free(new_entry_string
);
908 new_entry_string
= NULL
;
914 g_string_assign(message
, _("!Invalid find condition - "
915 "change it and try again\n"));
917 g_string_sprintf(message
, _("?Check '%s'?"), path
);
918 if (!reply(from_parent
, TRUE
))
922 if (mc_lstat(path
, &info
.stats
))
925 g_string_sprintf(message
, _("'(while checking '%s')\n"), path
);
930 info
.fullpath
= path
;
931 time(&info
.now
); /* XXX: Not for each check! */
933 info
.leaf
= g_basename(path
);
935 if (find_test_condition(find_condition
, &info
))
937 g_string_sprintf(message
, "=%s", path
);
941 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
944 safe_path
= g_strdup(path
);
945 for_dir_contents(do_find
, safe_path
, safe_path
);
950 /* Like mode_compile(), but ignores spaces and bracketed bits */
951 struct mode_change
*nice_mode_compile(const char *mode_string
,
952 unsigned int masked_ops
)
956 struct mode_change
*retval
= NULL
;
958 new = g_string_new(NULL
);
960 for (; *mode_string
; mode_string
++)
962 if (*mode_string
== '(')
964 if (*mode_string
== ')')
972 if (brackets
== 0 && *mode_string
!= ' ')
973 g_string_append_c(new, *mode_string
);
977 retval
= mode_compile(new->str
, masked_ops
);
978 g_string_free(new, TRUE
);
982 static void do_chmod(const char *path
, const char *unused
)
989 if (mc_lstat(path
, &info
))
994 if (S_ISLNK(info
.st_mode
))
999 g_string_sprintf(message
,
1000 _("?Change permissions of '%s'?"), path
);
1001 if (!reply(from_parent
, FALSE
))
1006 g_string_sprintf(message
,
1007 _("'Changing permissions of '%s'\n"),
1014 if (new_entry_string
)
1017 mode_free(mode_change
);
1018 mode_change
= nice_mode_compile(new_entry_string
,
1020 g_free(new_entry_string
);
1021 new_entry_string
= NULL
;
1027 g_string_assign(message
,
1028 _("!Invalid mode command - change it and try again\n"));
1030 g_string_sprintf(message
,
1031 _("?Change permissions of '%s'?"), path
);
1032 if (!reply(from_parent
, TRUE
))
1036 if (mc_lstat(path
, &info
))
1041 if (S_ISLNK(info
.st_mode
))
1044 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1045 if (chmod(path
, new_mode
))
1051 g_string_sprintf(message
, "s%s", path
);
1054 if (S_ISDIR(info
.st_mode
))
1056 g_string_sprintf(message
, "m%s", path
);
1062 safe_path
= g_strdup(path
);
1063 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1069 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1070 * is set then that is the new leafname, otherwise the leafname stays
1073 static char *make_dest_path(const char *object
, const char *dir
)
1081 leaf
= strrchr(object
, '/');
1083 leaf
= object
; /* Error? */
1088 return make_path(dir
, leaf
)->str
;
1091 /* If action_leaf is not NULL it specifies the new leaf name */
1092 static void do_copy2(const char *path
, const char *dest
)
1096 struct stat dest_info
;
1100 dest_path
= make_dest_path(path
, dest
);
1102 if (mc_lstat(path
, &info
))
1108 if (mc_lstat(dest_path
, &dest_info
) == 0)
1113 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1115 if (!merge
&& o_newer
&& info
.st_mtime
> dest_info
.st_mtime
)
1117 /* Newer; keep going */
1121 g_string_sprintf(message
,
1122 _("?'%s' already exists - %s?"), dest_path
,
1123 merge
? _("merge contents") : _("overwrite"));
1125 if (!reply(from_parent
, TRUE
))
1131 if (S_ISDIR(dest_info
.st_mode
))
1132 err
= rmdir(dest_path
);
1134 err
= unlink(dest_path
);
1139 if (errno
!= ENOENT
)
1141 g_string_sprintf(message
,
1142 _("'Trying copy anyway...\n"));
1149 g_string_sprintf(message
,
1150 _("?Copy %s as %s?"), path
, dest_path
);
1151 if (!reply(from_parent
, FALSE
))
1156 g_string_sprintf(message
, _("'Copying %s as %s\n"), path
,
1161 if (S_ISDIR(info
.st_mode
))
1163 mode_t mode
= info
.st_mode
;
1164 char *safe_path
, *safe_dest
;
1165 struct stat dest_info
;
1168 safe_path
= g_strdup(path
);
1169 safe_dest
= g_strdup(dest_path
);
1171 exists
= !mc_lstat(dest_path
, &dest_info
);
1173 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1175 g_string_sprintf(message
,
1176 _("!ERROR: Destination already exists, "
1177 "but is not a directory\n"));
1180 else if (exists
== FALSE
&& mkdir(dest_path
, 0700 | mode
))
1186 /* (just been created then) */
1187 g_string_sprintf(message
, "s%s", dest_path
);
1192 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1193 /* Note: dest_path now invalid... */
1199 /* We may have created the directory with
1200 * more permissions than the source so that
1201 * we could write to it... change it back now.
1203 if (chmod(safe_dest
, mode
))
1206 /* Also, try to preserve the timestamps */
1207 utb
.actime
= info
.st_atime
;
1208 utb
.modtime
= info
.st_mtime
;
1210 utime(safe_dest
, &utb
);
1217 else if (S_ISLNK(info
.st_mode
))
1221 /* Not all versions of cp(1) can make symlinks,
1222 * so we special-case it.
1225 target
= readlink_dup(path
);
1228 if (symlink(target
, dest_path
))
1232 g_string_sprintf(message
, "s%s", dest_path
);
1245 error
= copy_file(path
, dest_path
);
1249 g_string_sprintf(message
, _("!%s\nFailed to copy '%s'"),
1256 g_string_sprintf(message
, "s%s", dest_path
);
1262 /* If action_leaf is not NULL it specifies the new leaf name */
1263 static void do_move2(const char *path
, const char *dest
)
1266 const char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1273 dest_path
= make_dest_path(path
, dest
);
1275 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1277 if (access(dest_path
, F_OK
) == 0)
1282 if (mc_lstat(dest_path
, &info
))
1288 if (!is_dir
&& o_newer
&& info2
.st_mtime
> info
.st_mtime
)
1290 /* Newer; keep going */
1294 g_string_sprintf(message
,
1295 _("?'%s' already exists - overwrite?"),
1297 if (!reply(from_parent
, TRUE
))
1301 if (S_ISDIR(info
.st_mode
))
1302 err
= rmdir(dest_path
);
1304 err
= unlink(dest_path
);
1309 if (errno
!= ENOENT
)
1311 g_string_sprintf(message
,
1312 _("'Trying move anyway...\n"));
1318 g_string_sprintf(message
,
1319 _("?Move %s as %s?"), path
, dest_path
);
1320 if (!reply(from_parent
, FALSE
))
1325 g_string_sprintf(message
, _("'Moving %s as %s\n"), path
,
1331 argv
[3] = dest_path
;
1333 err
= fork_exec_wait(argv
);
1336 g_string_sprintf(message
,
1337 _("!%s\nFailed to move %s as %s\n"),
1338 err
, path
, dest_path
);
1345 g_string_sprintf(message
, "s%s", dest_path
);
1349 g_string_sprintf(message
, "m%s", path
);
1351 g_string_sprintf(message
, "s%s", path
);
1357 /* Copy path to dest.
1358 * Check that path not copied into itself.
1360 static void do_copy(const char *path
, const char *dest
)
1362 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1364 g_string_sprintf(message
,
1365 _("!ERROR: Can't copy object into itself\n"));
1369 do_copy2(path
, dest
);
1372 /* Move path to dest.
1373 * Check that path not moved into itself.
1375 static void do_move(const char *path
, const char *dest
)
1377 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1379 g_string_sprintf(message
,
1380 _("!ERROR: Can't move/rename object into itself\n"));
1384 do_move2(path
, dest
);
1387 static void do_link(const char *path
, const char *dest
)
1393 dest_path
= make_dest_path(path
, dest
);
1397 g_string_sprintf(message
, _("'Linking %s as %s\n"), path
,
1403 g_string_sprintf(message
,
1404 _("?Link %s as %s?"), path
, dest_path
);
1405 if (!reply(from_parent
, FALSE
))
1409 if (symlink(path
, dest_path
))
1413 g_string_sprintf(message
, "s%s", dest_path
);
1418 /* Mount/umount this item (depending on 'mount') */
1419 static void do_mount(guchar
*path
, gboolean mount
)
1421 const char *argv
[3] = {NULL
, NULL
, NULL
};
1426 argv
[0] = mount
? "mount" : "umount";
1431 g_string_sprintf(message
,
1432 mount
? _("'Mounting %s\n")
1433 : _("'Unmounting %s\n"),
1439 g_string_sprintf(message
,
1440 mount
? _("?Mount %s?")
1441 : _("?Unmount %s?"),
1443 if (!reply(from_parent
, FALSE
))
1447 err
= fork_exec_wait(argv
);
1450 g_string_sprintf(message
, mount
?
1451 _("!%s\nMount failed\n") :
1452 _("!%s\nUnmount failed\n"), err
);
1458 g_string_sprintf(message
, "M%s", path
);
1460 if (mount
&& mount_open_dir
)
1462 g_string_sprintf(message
, "o%s", path
);
1468 /* CHILD MAIN LOOPS */
1470 /* After forking, the child calls one of these functions */
1472 /* We use a double for total size in order to count beyond 4Gb */
1473 static void usage_cb(gpointer data
)
1475 GList
*paths
= (GList
*) data
;
1476 double total_size
= 0;
1479 dir_counter
= file_counter
= 0;
1481 for (; paths
; paths
= paths
->next
)
1483 guchar
*path
= (guchar
*) paths
->data
;
1489 do_usage(path
, NULL
);
1491 g_string_sprintf(message
, "'%s: %s\n",
1493 format_double_size(size_tally
));
1495 total_size
+= size_tally
;
1498 g_string_sprintf(message
, _("'\nTotal: %s ("),
1499 format_double_size(total_size
));
1503 tmp
= g_strdup_printf("%ld %s%s",
1505 file_counter
== 1 ? _("file") : _("files"),
1506 dir_counter
? ", " : ")\n");
1507 g_string_append(message
, tmp
);
1511 if (file_counter
== 0 && dir_counter
== 0)
1512 g_string_append(message
, _("no directories)\n"));
1513 else if (dir_counter
)
1515 tmp
= g_strdup_printf("%ld %s)\n",
1517 dir_counter
== 1 ? _("directory")
1518 : _("directories"));
1519 g_string_append(message
, tmp
);
1526 #ifdef DO_MOUNT_POINTS
1527 static void mount_cb(gpointer data
)
1529 GList
*paths
= (GList
*) data
;
1530 gboolean mount_points
= FALSE
;
1532 for (; paths
; paths
= paths
->next
)
1534 guchar
*path
= (guchar
*) paths
->data
;
1536 if (mount_is_mounted(path
, NULL
, NULL
))
1537 do_mount(path
, FALSE
); /* Unmount */
1538 else if (g_hash_table_lookup(fstab_mounts
, path
))
1539 do_mount(path
, TRUE
); /* Mount */
1543 mount_points
= TRUE
;
1546 g_string_sprintf(message
,
1547 mount_points
? _("'\nDone\n")
1548 : _("!No mount points selected!\n"));
1553 /* (use g_dirname() instead?) */
1554 static guchar
*dirname(guchar
*path
)
1558 slash
= strrchr(path
, '/');
1559 g_return_val_if_fail(slash
!= NULL
, g_strdup(path
));
1562 return g_strndup(path
, slash
- path
);
1563 return g_strdup("/");
1566 static void delete_cb(gpointer data
)
1568 GList
*paths
= (GList
*) data
;
1572 guchar
*path
= (guchar
*) paths
->data
;
1575 dir
= dirname(path
);
1578 do_delete(path
, dir
);
1581 paths
= paths
->next
;
1584 g_string_sprintf(message
, _("'\nDone\n"));
1588 static void find_cb(gpointer data
)
1590 GList
*all_paths
= (GList
*) data
;
1595 for (paths
= all_paths
; paths
; paths
= paths
->next
)
1597 guchar
*path
= (guchar
*) paths
->data
;
1601 do_find(path
, NULL
);
1604 g_string_assign(message
, _("?Another search?"));
1605 if (!reply(from_parent
, TRUE
))
1607 g_string_assign(message
, "#");
1611 g_string_sprintf(message
, _("'\nDone\n"));
1615 static void chmod_cb(gpointer data
)
1617 GList
*paths
= (GList
*) data
;
1619 for (; paths
; paths
= paths
->next
)
1621 guchar
*path
= (guchar
*) paths
->data
;
1626 if (mc_stat(path
, &info
) != 0)
1628 else if (S_ISLNK(info
.st_mode
))
1630 g_string_sprintf(message
,
1631 _("!'%s' is a symbolic link\n"),
1636 do_chmod(path
, NULL
);
1639 g_string_sprintf(message
, _("'\nDone\n"));
1643 static void list_cb(gpointer data
)
1645 GList
*paths
= (GList
*) data
;
1649 send_dir((char *) paths
->data
);
1651 action_do_func((char *) paths
->data
, action_dest
);
1653 paths
= paths
->next
;
1656 g_string_sprintf(message
, _("'\nDone\n"));
1660 /* EXTERNAL INTERFACE */
1662 void action_find(GList
*paths
)
1669 report_error(_("You need to select some items "
1670 "to search through"));
1674 if (!last_find_string
)
1675 last_find_string
= g_strdup("'core'");
1677 new_entry_string
= last_find_string
;
1679 abox
= abox_new(_("Find"), FALSE
);
1680 gui_side
= start_action(abox
, find_cb
, paths
,
1681 o_action_force
.int_value
,
1682 o_action_brief
.int_value
,
1683 o_action_recurse
.int_value
,
1684 o_action_newer
.int_value
);
1688 abox_add_results(ABOX(abox
));
1690 gui_side
->default_string
= &last_find_string
;
1691 abox_add_entry(ABOX(abox
), last_find_string
,
1692 new_help_button(show_condition_help
, NULL
));
1693 g_signal_connect(ABOX(abox
)->entry
, "changed",
1694 G_CALLBACK(entry_changed
), gui_side
);
1695 set_find_string_colour(ABOX(abox
)->entry
, last_find_string
);
1697 gui_side
->show_info
= TRUE
;
1698 gui_side
->entry_string_func
= set_find_string_colour
;
1700 number_of_windows
++;
1701 gtk_widget_show_all(abox
);
1704 /* Count disk space used by selected items */
1705 void action_usage(GList
*paths
)
1712 report_error(_("You need to select some items to count"));
1716 abox
= abox_new(_("Disk Usage"), TRUE
);
1718 gui_side
= start_action(abox
, usage_cb
, paths
,
1719 o_action_force
.int_value
,
1720 o_action_brief
.int_value
,
1721 o_action_recurse
.int_value
,
1722 o_action_newer
.int_value
);
1726 gui_side
->show_info
= TRUE
;
1728 number_of_windows
++;
1730 gtk_widget_show(abox
);
1733 /* Mount/unmount listed items (paths).
1734 * Free the list after this function returns.
1735 * If open_dir is TRUE and the dir is successfully mounted, open it.
1736 * quiet can be -1 for default.
1738 void action_mount(GList
*paths
, gboolean open_dir
, int quiet
)
1740 #ifdef DO_MOUNT_POINTS
1745 quiet
= o_action_mount
.int_value
;
1747 mount_open_dir
= open_dir
;
1749 abox
= abox_new(_("Mount / Unmount"), quiet
);
1750 gui_side
= start_action(abox
, mount_cb
, paths
,
1751 o_action_force
.int_value
,
1752 o_action_brief
.int_value
,
1753 o_action_recurse
.int_value
,
1754 o_action_newer
.int_value
);
1758 number_of_windows
++;
1759 gtk_widget_show(abox
);
1762 _("ROX-Filer does not yet support mount points on your "
1764 #endif /* DO_MOUNT_POINTS */
1767 /* Deletes all selected items in the window */
1768 void action_delete(GList
*paths
)
1773 if (!remove_pinned_ok(paths
))
1776 abox
= abox_new(_("Delete"), o_action_delete
.int_value
);
1777 gui_side
= start_action(abox
, delete_cb
, paths
,
1778 o_action_force
.int_value
,
1779 o_action_brief
.int_value
,
1780 o_action_recurse
.int_value
,
1781 o_action_newer
.int_value
);
1785 abox_add_flag(ABOX(abox
),
1786 _("Force"), _("Don't confirm deletion of non-writeable items"),
1787 'F', o_action_force
.int_value
);
1788 abox_add_flag(ABOX(abox
),
1789 _("Brief"), _("Only log directories being deleted"),
1790 'B', o_action_brief
.int_value
);
1792 number_of_windows
++;
1793 gtk_widget_show(abox
);
1796 /* Change the permissions of the selected items */
1797 void action_chmod(GList
*paths
)
1801 static GList
*presets
= NULL
;
1805 report_error(_("You need to select the items "
1806 "whose permissions you want to change"));
1812 presets
= g_list_append(presets
, (gchar
*)
1813 _("a+x (Make executable/searchable)"));
1814 presets
= g_list_append(presets
, (gchar
*)
1815 _("a-x (Make non-executable/non-searchable)"));
1816 presets
= g_list_append(presets
, (gchar
*)
1817 _("u+rw (Give owner read+write)"));
1818 presets
= g_list_append(presets
, (gchar
*)
1819 _("go-rwx (Private - owner access only)"));
1820 presets
= g_list_append(presets
, (gchar
*)
1821 _("go=u-w (Public access, not write)"));
1824 if (!last_chmod_string
)
1825 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
1826 new_entry_string
= last_chmod_string
;
1828 abox
= abox_new(_("Permissions"), FALSE
);
1829 gui_side
= start_action(abox
, chmod_cb
, paths
,
1830 o_action_force
.int_value
,
1831 o_action_brief
.int_value
,
1832 o_action_recurse
.int_value
,
1833 o_action_newer
.int_value
);
1837 abox_add_flag(ABOX(abox
),
1838 _("Brief"), _("Don't list processed files"),
1839 'B', o_action_brief
.int_value
);
1840 abox_add_flag(ABOX(abox
),
1841 _("Recurse"), _("Also change contents of subdirectories"),
1842 'R', o_action_recurse
.int_value
);
1844 gui_side
->default_string
= &last_chmod_string
;
1845 abox_add_combo(ABOX(abox
), presets
, last_chmod_string
,
1846 new_help_button(show_chmod_help
, NULL
));
1848 g_signal_connect(ABOX(abox
)->entry
, "changed",
1849 G_CALLBACK(entry_changed
), gui_side
);
1851 g_signal_connect_swapped(gui_side
->entry
, "activate",
1852 G_CALLBACK(gtk_button_clicked
),
1856 number_of_windows
++;
1857 gtk_widget_show(abox
);
1860 /* If leaf is NULL then the copy has the same name as the original.
1861 * quiet can be -1 for default.
1863 void action_copy(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1869 quiet
= o_action_copy
.int_value
;
1873 action_do_func
= do_copy
;
1875 abox
= abox_new(_("Copy"), quiet
);
1876 gui_side
= start_action(abox
, list_cb
, paths
,
1877 o_action_force
.int_value
,
1878 o_action_brief
.int_value
,
1879 o_action_recurse
.int_value
,
1880 o_action_newer
.int_value
);
1884 abox_add_flag(ABOX(abox
),
1886 _("Only over-write if source is newer than destination."),
1887 'W', o_action_newer
.int_value
);
1889 number_of_windows
++;
1890 gtk_widget_show(abox
);
1893 /* If leaf is NULL then the file is not renamed.
1894 * quiet can be -1 for default.
1896 void action_move(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1902 quiet
= o_action_move
.int_value
;
1906 action_do_func
= do_move
;
1908 abox
= abox_new(_("Move"), quiet
);
1909 gui_side
= start_action(abox
, list_cb
, paths
,
1910 o_action_force
.int_value
,
1911 o_action_brief
.int_value
,
1912 o_action_recurse
.int_value
,
1913 o_action_newer
.int_value
);
1917 abox_add_flag(ABOX(abox
),
1919 _("Only over-write if source is newer than destination."),
1920 'W', o_action_newer
.int_value
);
1921 number_of_windows
++;
1922 gtk_widget_show(abox
);
1925 /* If leaf is NULL then the link will have the same name */
1926 /* XXX: No quiet option here? */
1927 void action_link(GList
*paths
, const char *dest
, const char *leaf
)
1934 action_do_func
= do_link
;
1936 abox
= abox_new(_("Link"), o_action_link
.int_value
);
1937 gui_side
= start_action(abox
, list_cb
, paths
,
1938 o_action_force
.int_value
,
1939 o_action_brief
.int_value
,
1940 o_action_recurse
.int_value
,
1941 o_action_newer
.int_value
);
1945 number_of_windows
++;
1946 gtk_widget_show(abox
);
1949 void action_init(void)
1951 option_add_int(&o_action_copy
, "action_copy", 1);
1952 option_add_int(&o_action_move
, "action_move", 1);
1953 option_add_int(&o_action_link
, "action_link", 1);
1954 option_add_int(&o_action_delete
, "action_delete", 0);
1955 option_add_int(&o_action_mount
, "action_mount", 1);
1956 option_add_int(&o_action_force
, "action_force", FALSE
);
1957 option_add_int(&o_action_brief
, "action_brief", FALSE
);
1958 option_add_int(&o_action_recurse
, "action_recurse", FALSE
);
1959 option_add_int(&o_action_newer
, "action_newer", FALSE
);
1964 /* Check to see if any of the selected items (or their children) are
1965 * on the pinboard or panel. If so, ask for confirmation.
1967 * TRUE if it's OK to lose them.
1969 static gboolean
remove_pinned_ok(GList
*paths
)
1971 GList
*ask
= NULL
, *next
;
1978 guchar
*path
= (guchar
*) paths
->data
;
1980 if (icons_require(path
))
1982 if (++ask_n
> MAX_ASK
)
1984 ask
= g_list_append(ask
, path
);
1987 paths
= paths
->next
;
1993 if (ask_n
> MAX_ASK
)
1995 message
= g_string_new(_("Deleting items such as "));
1998 else if (ask_n
== 1)
1999 message
= g_string_new(_("Deleting the item "));
2001 message
= g_string_new(_("Deleting the items "));
2004 for (next
= ask
; next
; next
= next
->next
)
2006 guchar
*path
= (guchar
*) next
->data
;
2009 leaf
= strrchr(path
, '/');
2015 g_string_append_c(message
, '`');
2016 g_string_append(message
, leaf
);
2017 g_string_append_c(message
, '\'');
2019 if (i
== ask_n
- 1 && i
> 0)
2020 g_string_append(message
, _(" and "));
2022 g_string_append(message
, ", ");
2028 message
= g_string_append(message
,
2029 _(" will affect some items on the pinboard "
2030 "or panel - really delete it?"));
2033 if (ask_n
> MAX_ASK
)
2034 message
= g_string_append_c(message
, ',');
2035 message
= g_string_append(message
,
2036 _(" will affect some items on the pinboard "
2037 "or panel - really delete them?"));
2040 retval
= get_choice(PROJECT
, message
->str
,
2041 2, _("Cancel"), _("Delete")) == 1;
2043 g_string_free(message
, TRUE
);
2048 void set_find_string_colour(GtkWidget
*widget
, const guchar
*string
)
2050 FindCondition
*cond
;
2052 cond
= find_compile(string
);
2053 entry_set_error(widget
, !cond
);
2056 find_condition_free(cond
);