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>
44 #include "gui_support.h"
49 #include "modechange.h"
55 /* Parent->Child messages are one character each:
57 * Y/N Yes/No button clicked
58 * F Force deletion of non-writeable items
60 * E Entry text changed
64 typedef struct _GUIside GUIside
;
65 typedef void ActionChild(gpointer data
);
66 typedef void ForDirCB(const char *path
, const char *dest_path
);
70 ABox
*abox
; /* The action window widget */
72 int from_child
; /* File descriptor */
74 int input_tag
; /* gdk_input_add() */
75 pid_t child
; /* Process ID */
76 int errors
; /* Number of errors so far */
77 gboolean show_info
; /* For Disk Usage */
79 guchar
**default_string
; /* Changed when the entry changes */
80 void (*entry_string_func
)(GtkWidget
*widget
,
81 const guchar
*string
);
84 /* These don't need to be in a structure because we fork() before
87 static gboolean mount_open_dir
= FALSE
;
88 static int from_parent
= 0;
89 static FILE *to_parent
= NULL
;
90 static gboolean quiet
= FALSE
;
91 static GString
*message
= NULL
;
92 static const char *action_dest
= NULL
;
93 static const char *action_leaf
= NULL
;
94 static void (*action_do_func
)(const char *source
, const char *dest
);
95 static double size_tally
; /* For Disk Usage */
96 static unsigned long dir_counter
; /* For Disk Usage */
97 static unsigned long file_counter
; /* For Disk Usage */
99 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
100 static FindCondition
*find_condition
= NULL
; /* For Find */
102 /* Only used by child */
103 static gboolean o_force
= FALSE
;
104 static gboolean o_brief
= FALSE
;
105 static gboolean o_recurse
= FALSE
;
106 static gboolean o_newer
= FALSE
;
108 static Option o_action_copy
, o_action_move
, o_action_link
;
109 static Option o_action_delete
, o_action_mount
;
110 static Option o_action_force
, o_action_brief
, o_action_recurse
;
111 static Option o_action_newer
;
113 /* Whenever the text in these boxes is changed we store a copy of the new
114 * string to be used as the default next time.
116 static guchar
*last_chmod_string
= NULL
;
117 static guchar
*last_find_string
= NULL
;
119 /* Set to one of the above before forking. This may change over a call to
120 * reply(). It is reset to NULL once the text is parsed.
122 static guchar
*new_entry_string
= NULL
;
124 /* Static prototypes */
125 static void send_done(void);
126 static void send_check_path(const gchar
*path
);
127 static void send_mount_path(const gchar
*path
);
128 static gboolean
printf_send(const char *msg
, ...);
129 static gboolean
send(void);
130 static gboolean
send_error(void);
131 static gboolean
send_dir(const char *dir
);
132 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
133 static void do_mount(guchar
*path
, gboolean mount
);
134 static gboolean
printf_reply(int fd
, gboolean ignore_quiet
,
135 const char *msg
, ...);
136 static gboolean
remove_pinned_ok(GList
*paths
);
141 /* This is called whenever the user edits the entry box (if any) - send the
144 static void entry_changed(GtkEditable
*entry
, GUIside
*gui_side
)
148 g_return_if_fail(gui_side
->default_string
!= NULL
);
150 text
= gtk_editable_get_chars(entry
, 0, -1);
152 if (gui_side
->entry_string_func
)
153 gui_side
->entry_string_func(GTK_WIDGET(entry
), text
);
155 g_free(*(gui_side
->default_string
));
156 *(gui_side
->default_string
) = text
; /* Gets text's ref */
158 if (!gui_side
->to_child
)
161 fputc('E', gui_side
->to_child
);
162 fputs(text
, gui_side
->to_child
);
163 fputc('\n', gui_side
->to_child
);
164 fflush(gui_side
->to_child
);
167 void show_condition_help(gpointer data
)
169 static GtkWidget
*help
= NULL
;
173 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
175 help
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
176 gtk_window_set_type_hint(GTK_WINDOW(help
),
177 GDK_WINDOW_TYPE_HINT_DIALOG
);
178 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
179 gtk_window_set_title(GTK_WINDOW(help
),
180 _("Find expression reference"));
182 vbox
= gtk_vbox_new(FALSE
, 0);
183 gtk_container_add(GTK_CONTAINER(help
), vbox
);
185 frame
= gtk_frame_new(_("Quick Start"));
186 text
= gtk_label_new(
187 _("Just put the name of the file you're looking for in single quotes:\n"
188 "'index.html' (to find a file called 'index.html')"));
189 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
190 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
191 gtk_container_add(GTK_CONTAINER(frame
), text
);
192 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
194 frame
= gtk_frame_new(_("Examples"));
195 text
= gtk_label_new(
196 _("'*.htm', '*.html' (finds HTML files)\n"
197 "IsDir 'lib' (finds directories called 'lib')\n"
198 "IsReg 'core' (finds a regular file called 'core')\n"
199 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
200 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
201 "'CVS' prune, isreg (a regular file not in CVS)\n"
202 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
203 gtk_widget_set_name(text
, "fixed-style");
204 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
205 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
206 gtk_container_add(GTK_CONTAINER(frame
), text
);
207 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
209 frame
= gtk_frame_new(_("Simple Tests"));
210 text
= gtk_label_new(
211 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor "
213 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
217 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
218 "contains a slash then the match is against the full path; otherwise it is \n"
219 "against the leafname only."));
220 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
221 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
222 gtk_container_add(GTK_CONTAINER(frame
), text
);
223 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
225 frame
= gtk_frame_new(_("Comparisons"));
226 text
= gtk_label_new(
227 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
228 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
229 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
230 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
231 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
232 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
233 gtk_container_add(GTK_CONTAINER(frame
), text
);
234 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
236 frame
= gtk_frame_new(_("Specials"));
237 text
= gtk_label_new(
238 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
239 "in 'command' is replaced with the path of the current file)\n"
240 "prune (false, and prevents searching the contents of a directory).")
242 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
243 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
244 gtk_container_add(GTK_CONTAINER(frame
), text
);
245 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
247 hbox
= gtk_hbox_new(FALSE
, 20);
248 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
250 text
= gtk_label_new(
251 _("See the ROX-Filer manual for full details."));
252 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
253 button
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
254 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
255 g_signal_connect_swapped(button
, "clicked",
256 G_CALLBACK(gtk_widget_hide
), help
);
258 g_signal_connect_swapped(help
, "delete_event",
259 G_CALLBACK(gtk_widget_hide
), help
);
262 if (GTK_WIDGET_VISIBLE(help
))
263 gtk_widget_hide(help
);
264 gtk_widget_show_all(help
);
267 static void show_chmod_help(gpointer data
)
269 static GtkWidget
*help
= NULL
;
273 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
275 help
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
276 gtk_window_set_type_hint(GTK_WINDOW(help
),
277 GDK_WINDOW_TYPE_HINT_DIALOG
);
278 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
279 gtk_window_set_title(GTK_WINDOW(help
),
280 _("Permissions command reference"));
282 vbox
= gtk_vbox_new(FALSE
, 0);
283 gtk_container_add(GTK_CONTAINER(help
), vbox
);
285 text
= gtk_label_new(
286 _("Normally, you can just select a command from the menu (click \n"
287 "on the arrow beside the command box). Sometimes, you need more...\n"
289 "The format of a command is:\n"
290 "CHANGE, CHANGE, ...\n"
292 "WHO HOW PERMISSIONS\n"
293 "WHO is some combination of u, g and o which determines whether to\n"
294 "change the permissions for the User (owner), Group or Others.\n"
295 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
296 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
298 "Bracketed text and spaces are ignored.\n\n"
301 "u+rw (the file owner gains read and write permission)\n"
302 "g=u (the group permissions are set to be the same as the user's)\n"
303 "o=u-w (others get the same permissions as the owner, but without "
304 "write permission)\n"
305 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
306 "a+X (directories become accessable by everyone; files which were\n"
307 "executable by anyone become executable by everyone)\n"
308 "u+rw, go+r (two commands at once!)\n"
309 "u+s (set the SetUID bit - often has no effect on script files)\n"
310 "755 (set the permissions directly)\n"
312 "\nSee the chmod(1) man page for full details."));
313 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
314 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
316 hbox
= gtk_hbox_new(FALSE
, 20);
317 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
319 sep
= gtk_hseparator_new();
320 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
321 button
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
322 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
323 g_signal_connect_swapped(button
, "clicked",
324 G_CALLBACK(gtk_widget_hide
), help
);
326 g_signal_connect_swapped(help
, "delete_event",
327 G_CALLBACK(gtk_widget_hide
), help
);
330 if (GTK_WIDGET_VISIBLE(help
))
331 gtk_widget_hide(help
);
332 gtk_widget_show_all(help
);
335 static void process_message(GUIside
*gui_side
, const gchar
*buffer
)
337 ABox
*abox
= gui_side
->abox
;
340 abox_ask(abox
, buffer
+ 1);
341 else if (*buffer
== 's')
342 dir_check_this(buffer
+ 1); /* Update this item */
343 else if (*buffer
== '=')
344 abox_add_filename(abox
, buffer
+ 1);
345 else if (*buffer
== '#')
346 abox_clear_results(abox
);
347 else if (*buffer
== 'm' || *buffer
== 'M')
349 /* Mount / major changes to this path */
352 filer_check_mounted(buffer
+ 1);
354 else if (*buffer
== '/')
355 abox_set_current_object(abox
, buffer
+ 1);
356 else if (*buffer
== 'o')
357 filer_opendir(buffer
+ 1, NULL
, NULL
);
358 else if (*buffer
== '!')
361 abox_log(abox
, buffer
+ 1, "error");
364 abox_log(abox
, buffer
+ 1, NULL
);
367 /* Called when the child sends us a message */
368 static void message_from_child(gpointer data
,
370 GdkInputCondition condition
)
373 GUIside
*gui_side
= (GUIside
*) data
;
374 ABox
*abox
= gui_side
->abox
;
375 GtkTextBuffer
*text_buffer
;
377 text_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox
->log
));
379 if (read_exact(source
, buf
, 4))
385 message_len
= strtol(buf
, NULL
, 16);
386 buffer
= g_malloc(message_len
+ 1);
387 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
389 buffer
[message_len
] = '\0';
390 process_message(gui_side
, buffer
);
394 g_printerr("Child died in the middle of a message.\n");
397 /* The child is dead */
400 fclose(gui_side
->to_child
);
401 gui_side
->to_child
= NULL
;
402 close(gui_side
->from_child
);
403 g_source_remove(gui_side
->input_tag
);
404 abox_cancel_ask(gui_side
->abox
);
406 if (gui_side
->errors
)
410 if (gui_side
->errors
== 1)
411 report
= g_strdup(_("There was one error.\n"));
413 report
= g_strdup_printf(_("There were %d errors.\n"),
416 gtk_text_buffer_insert_at_cursor(text_buffer
, report
, -1);
420 else if (gui_side
->show_info
== FALSE
)
421 gtk_widget_destroy(GTK_WIDGET(gui_side
->abox
));
424 /* Scans src_dir, calling cb(item, dest_path) for each item */
425 static void for_dir_contents(ForDirCB
*cb
,
427 const char *dest_path
)
431 GList
*list
= NULL
, *next
;
433 d
= mc_opendir(src_dir
);
436 /* Message displayed is "ERROR reading 'path': message" */
437 printf_send("!%s '%s': %s\n", _("ERROR reading"),
438 src_dir
, g_strerror(errno
));
444 while ((ent
= mc_readdir(d
)))
446 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
447 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
449 list
= g_list_append(list
, g_strdup(make_path(src_dir
,
457 for (next
= list
; next
; next
= next
->next
)
459 cb((char *) next
->data
, dest_path
);
466 /* Read this many bytes into the buffer. TRUE on success. */
467 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
472 got
= read(source
, buffer
, len
);
481 static void send_done(void)
483 printf_send(_("'\nDone\n"));
486 /* Notify the filer that this item has been updated */
487 static void send_check_path(const gchar
*path
)
489 printf_send("s%s", path
);
492 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
493 static void send_mount_path(const gchar
*path
)
495 printf_send("m%s", path
);
498 /* Send a message to the filer process. The first character indicates the
499 * type of the message.
501 static gboolean
printf_send(const char *msg
, ...)
507 tmp
= g_strdup_vprintf(msg
, args
);
510 g_string_assign(message
, tmp
);
516 /* Send 'message' to our parent process. TRUE on success. */
517 static gboolean
send(void)
522 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
524 sprintf(len_buffer
, "%04x", message
->len
);
525 fwrite(len_buffer
, 1, 4, to_parent
);
526 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
528 return len
== message
->len
;
531 /* Set the directory indicator at the top of the window */
532 static gboolean
send_dir(const char *dir
)
534 return printf_send("/%s", dir
);
537 static gboolean
send_error(void)
539 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno
));
542 static void response(GtkDialog
*dialog
, gint response
, GUIside
*gui_side
)
546 if (!gui_side
->to_child
)
549 if (response
== GTK_RESPONSE_YES
)
551 else if (response
== GTK_RESPONSE_NO
)
556 fputc(code
, gui_side
->to_child
);
557 fflush(gui_side
->to_child
);
560 static void flag_toggled(ABox
*abox
, gint flag
, GUIside
*gui_side
)
562 if (!gui_side
->to_child
)
565 fputc(flag
, gui_side
->to_child
);
566 fflush(gui_side
->to_child
);
569 static void read_new_entry_text(void)
575 new = g_string_new(NULL
);
579 len
= read(from_parent
, &c
, 1);
582 fprintf(stderr
, "read() error: %s\n",
584 _exit(1); /* Parent died? */
589 g_string_append_c(new, c
);
592 g_free(new_entry_string
);
593 new_entry_string
= new->str
;
594 g_string_free(new, FALSE
);
597 static void process_flag(char flag
)
608 o_recurse
= !o_recurse
;
617 read_new_entry_text();
620 printf_send("!ERROR: Bad message '%c'\n", flag
);
625 /* If the parent has sent any flag toggles, read them */
626 static void check_flags(void)
637 FD_SET(from_parent
, &set
);
640 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
643 g_error("select() failed: %s\n", g_strerror(errno
));
647 got
= read(from_parent
, &retval
, 1);
649 g_error("read() error: %s\n", g_strerror(errno
));
651 process_flag(retval
);
655 /* Read until the user sends a reply. If ignore_quiet is TRUE then
656 * the user MUST click Yes or No, else treat quiet on as Yes.
657 * If the user needs prompting then does send().
659 static gboolean
printf_reply(int fd
, gboolean ignore_quiet
,
660 const char *msg
, ...)
667 if (quiet
&& !ignore_quiet
)
671 tmp
= g_strdup_vprintf(msg
, args
);
674 g_string_assign(message
, tmp
);
681 len
= read(fd
, &retval
, 1);
684 fprintf(stderr
, "read() error: %s\n",
686 _exit(1); /* Parent died? */
692 printf_send("' %s\n", _("Yes"));
695 printf_send("' %s\n", _("No"));
698 process_flag(retval
);
704 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
706 GUIside
*gui_side
= (GUIside
*) data
;
710 kill(gui_side
->child
, SIGTERM
);
711 fclose(gui_side
->to_child
);
712 close(gui_side
->from_child
);
713 g_source_remove(gui_side
->input_tag
);
721 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
722 * (NULL on failure). The child calls func().
724 static GUIside
*start_action(GtkWidget
*abox
, ActionChild
*func
, gpointer data
,
725 int force
, int brief
, int recurse
, int newer
)
728 int filedes
[4]; /* 0 and 2 are for reading */
731 struct sigaction act
;
735 report_error("pipe: %s", g_strerror(errno
));
736 gtk_widget_destroy(abox
);
740 if (pipe(filedes
+ 2))
744 report_error("pipe: %s", g_strerror(errno
));
745 gtk_widget_destroy(abox
);
749 autoq
= gtk_toggle_button_get_active(
750 GTK_TOGGLE_BUTTON(ABOX(abox
)->quiet
));
761 report_error("fork: %s", g_strerror(errno
));
762 gtk_widget_destroy(abox
);
765 /* We are the child */
769 /* Reset the SIGCHLD handler */
770 act
.sa_handler
= SIG_DFL
;
771 sigemptyset(&act
.sa_mask
);
773 sigaction(SIGCHLD
, &act
, NULL
);
775 message
= g_string_new(NULL
);
778 to_parent
= fdopen(filedes
[1], "wb");
779 from_parent
= filedes
[2];
785 /* We are the parent */
788 gui_side
= g_malloc(sizeof(GUIside
));
789 gui_side
->from_child
= filedes
[0];
790 gui_side
->to_child
= fdopen(filedes
[3], "wb");
791 gui_side
->child
= child
;
792 gui_side
->errors
= 0;
793 gui_side
->show_info
= FALSE
;
794 gui_side
->default_string
= NULL
;
795 gui_side
->entry_string_func
= NULL
;
797 gui_side
->abox
= ABOX(abox
);
798 g_signal_connect(abox
, "destroy",
799 G_CALLBACK(destroy_action_window
), gui_side
);
801 g_signal_connect(abox
, "response", G_CALLBACK(response
), gui_side
);
802 g_signal_connect(abox
, "flag_toggled",
803 G_CALLBACK(flag_toggled
), gui_side
);
805 gui_side
->input_tag
= gtk_input_add_full(gui_side
->from_child
,
808 NULL
, gui_side
, NULL
);
813 /* ACTIONS ON ONE ITEM */
815 /* These may call themselves recursively, or ask questions, etc */
817 /* Updates the global size_tally, file_counter and dir_counter */
818 static void do_usage(const char *src_path
, const char *unused
)
824 if (mc_lstat(src_path
, &info
))
826 printf_send("'%s:\n", src_path
);
829 else if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
832 size_tally
+= info
.st_size
;
834 else if (S_ISDIR(info
.st_mode
))
837 if (printf_reply(from_parent
, FALSE
,
838 _("?Count contents of %s?"), src_path
))
841 safe_path
= g_strdup(src_path
);
842 for_dir_contents(do_usage
, safe_path
, safe_path
);
850 /* dest_path is the dir containing src_path */
851 static void do_delete(const char *src_path
, const char *unused
)
859 if (mc_lstat(src_path
, &info
))
865 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
866 : access(src_path
, W_OK
) != 0;
867 if (write_prot
|| !quiet
)
869 if (!printf_reply(from_parent
, write_prot
&& !o_force
,
870 _("?Delete %s'%s'?"),
871 write_prot
? _("WRITE-PROTECTED ") : "",
876 printf_send(_("'Deleting '%s'\n"), src_path
);
878 safe_path
= g_strdup(src_path
);
880 if (S_ISDIR(info
.st_mode
))
882 for_dir_contents(do_delete
, safe_path
, safe_path
);
883 if (rmdir(safe_path
))
889 printf_send(_("'Directory '%s' deleted\n"), safe_path
);
890 send_mount_path(safe_path
);
892 else if (unlink(src_path
))
896 send_check_path(safe_path
);
897 if (strcmp(g_basename(safe_path
), ".DirIcon") == 0)
900 dir
= g_dirname(safe_path
);
901 send_check_path(dir
);
909 /* path is the item to check. If is is a directory then we may recurse
910 * (unless prune is used).
912 static void do_find(const char *path
, const char *unused
)
920 if (!printf_reply(from_parent
, FALSE
, _("?Check '%s'?"), path
))
926 if (new_entry_string
)
928 find_condition_free(find_condition
);
929 find_condition
= find_compile(new_entry_string
);
930 null_g_free(&new_entry_string
);
936 printf_send(_("!Invalid find condition - "
937 "change it and try again\n"));
938 if (!printf_reply(from_parent
, TRUE
,
939 _("?Check '%s'?"), path
))
943 if (mc_lstat(path
, &info
.stats
))
946 printf_send(_("'(while checking '%s')\n"), path
);
950 info
.fullpath
= path
;
951 time(&info
.now
); /* XXX: Not for each check! */
953 info
.leaf
= g_basename(path
);
955 if (find_test_condition(find_condition
, &info
))
956 printf_send("=%s", path
);
958 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
961 safe_path
= g_strdup(path
);
962 for_dir_contents(do_find
, safe_path
, safe_path
);
967 /* Like mode_compile(), but ignores spaces and bracketed bits */
968 static struct mode_change
*nice_mode_compile(const char *mode_string
,
969 unsigned int masked_ops
)
973 struct mode_change
*retval
= NULL
;
975 new = g_string_new(NULL
);
977 for (; *mode_string
; mode_string
++)
979 if (*mode_string
== '(')
981 if (*mode_string
== ')')
989 if (brackets
== 0 && *mode_string
!= ' ')
990 g_string_append_c(new, *mode_string
);
994 retval
= mode_compile(new->str
, masked_ops
);
995 g_string_free(new, TRUE
);
999 static void do_chmod(const char *path
, const char *unused
)
1006 if (mc_lstat(path
, &info
))
1011 if (S_ISLNK(info
.st_mode
))
1016 if (!printf_reply(from_parent
, FALSE
,
1017 _("?Change permissions of '%s'?"), path
))
1021 printf_send(_("'Changing permissions of '%s'\n"), path
);
1025 if (new_entry_string
)
1028 mode_free(mode_change
);
1029 mode_change
= nice_mode_compile(new_entry_string
,
1031 null_g_free(&new_entry_string
);
1038 _("!Invalid mode command - change it and try again\n"));
1039 if (!printf_reply(from_parent
, TRUE
,
1040 _("?Change permissions of '%s'?"), path
))
1044 if (mc_lstat(path
, &info
))
1049 if (S_ISLNK(info
.st_mode
))
1052 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1053 if (chmod(path
, new_mode
))
1059 send_check_path(path
);
1061 if (S_ISDIR(info
.st_mode
))
1063 send_mount_path(path
);
1068 safe_path
= g_strdup(path
);
1069 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1075 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1076 * is set then that is the new leafname, otherwise the leafname stays
1079 static char *make_dest_path(const char *object
, const char *dir
)
1087 leaf
= strrchr(object
, '/');
1089 leaf
= object
; /* Error? */
1094 return make_path(dir
, leaf
)->str
;
1097 /* If action_leaf is not NULL it specifies the new leaf name */
1098 static void do_copy2(const char *path
, const char *dest
)
1102 struct stat dest_info
;
1106 dest_path
= make_dest_path(path
, dest
);
1108 if (mc_lstat(path
, &info
))
1114 if (mc_lstat(dest_path
, &dest_info
) == 0)
1119 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1121 if (!merge
&& o_newer
&& info
.st_mtime
> dest_info
.st_mtime
)
1123 /* Newer; keep going */
1127 if (!printf_reply(from_parent
, TRUE
,
1128 _("?'%s' already exists - %s?"),
1130 merge
? _("merge contents")
1137 if (S_ISDIR(dest_info
.st_mode
))
1138 err
= rmdir(dest_path
);
1140 err
= unlink(dest_path
);
1145 if (errno
!= ENOENT
)
1147 printf_send(_("'Trying copy anyway...\n"));
1153 if (!printf_reply(from_parent
, FALSE
,
1154 _("?Copy %s as %s?"), path
, dest_path
))
1158 printf_send(_("'Copying %s as %s\n"), path
, dest_path
);
1160 if (S_ISDIR(info
.st_mode
))
1162 mode_t mode
= info
.st_mode
;
1163 char *safe_path
, *safe_dest
;
1164 struct stat dest_info
;
1167 safe_path
= g_strdup(path
);
1168 safe_dest
= g_strdup(dest_path
);
1170 exists
= !mc_lstat(dest_path
, &dest_info
);
1172 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1173 printf_send(_("!ERROR: Destination already exists, "
1174 "but is not a directory\n"));
1175 else if (exists
== FALSE
&& mkdir(dest_path
, 0700 | mode
))
1180 /* (just been created then) */
1181 send_check_path(dest_path
);
1184 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1185 /* Note: dest_path now invalid... */
1191 /* We may have created the directory with
1192 * more permissions than the source so that
1193 * we could write to it... change it back now.
1195 if (chmod(safe_dest
, mode
))
1197 /* Some filesystems don't support
1198 * SetGID and SetUID bits. Ignore
1205 /* Also, try to preserve the timestamps */
1206 utb
.actime
= info
.st_atime
;
1207 utb
.modtime
= info
.st_mtime
;
1209 utime(safe_dest
, &utb
);
1216 else if (S_ISLNK(info
.st_mode
))
1220 /* Not all versions of cp(1) can make symlinks,
1221 * so we special-case it.
1224 target
= readlink_dup(path
);
1227 if (symlink(target
, dest_path
))
1230 send_check_path(dest_path
);
1241 error
= copy_file(path
, dest_path
);
1245 printf_send(_("!%s\nFailed to copy '%s'"), error
, path
);
1249 send_check_path(dest_path
);
1253 /* If action_leaf is not NULL it specifies the new leaf name */
1254 static void do_move2(const char *path
, const char *dest
)
1257 const char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1264 dest_path
= make_dest_path(path
, dest
);
1266 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1268 if (access(dest_path
, F_OK
) == 0)
1273 if (mc_lstat(dest_path
, &info
))
1279 if (!is_dir
&& o_newer
&& info2
.st_mtime
> info
.st_mtime
)
1281 /* Newer; keep going */
1283 else if (!printf_reply(from_parent
, TRUE
,
1284 _("?'%s' already exists - overwrite?"),
1288 if (S_ISDIR(info
.st_mode
))
1289 err
= rmdir(dest_path
);
1291 err
= unlink(dest_path
);
1296 if (errno
!= ENOENT
)
1298 printf_send(_("'Trying move anyway...\n"));
1303 if (!printf_reply(from_parent
, FALSE
,
1304 _("?Move %s as %s?"), path
, dest_path
))
1308 printf_send(_("'Moving %s as %s\n"), path
, dest_path
);
1311 argv
[3] = dest_path
;
1313 err
= fork_exec_wait(argv
);
1316 printf_send(_("!%s\nFailed to move %s as %s\n"),
1317 err
, path
, dest_path
);
1322 send_check_path(dest_path
);
1325 send_mount_path(path
);
1327 send_check_path(path
);
1331 /* Copy path to dest.
1332 * Check that path not copied into itself.
1334 static void do_copy(const char *path
, const char *dest
)
1336 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1337 printf_send(_("!ERROR: Can't copy object into itself\n"));
1339 do_copy2(path
, dest
);
1342 /* Move path to dest.
1343 * Check that path not moved into itself.
1345 static void do_move(const char *path
, const char *dest
)
1347 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1349 _("!ERROR: Can't move/rename object into itself\n"));
1351 do_move2(path
, dest
);
1354 static void do_link(const char *path
, const char *dest
)
1360 dest_path
= make_dest_path(path
, dest
);
1363 printf_send(_("'Linking %s as %s\n"), path
, dest_path
);
1364 else if (!printf_reply(from_parent
, FALSE
,
1365 _("?Link %s as %s?"), path
, dest_path
))
1368 if (symlink(path
, dest_path
))
1371 send_check_path(dest_path
);
1374 /* Mount/umount this item (depending on 'mount') */
1375 static void do_mount(guchar
*path
, gboolean mount
)
1377 const char *argv
[3] = {NULL
, NULL
, NULL
};
1382 argv
[0] = mount
? "mount" : "umount";
1386 printf_send(mount
? _("'Mounting %s\n")
1387 : _("'Unmounting %s\n"),
1389 else if (!printf_reply(from_parent
, FALSE
,
1390 mount
? _("?Mount %s?")
1391 : _("?Unmount %s?"),
1395 err
= fork_exec_wait(argv
);
1399 _("!%s\nMount failed\n") :
1400 _("!%s\nUnmount failed\n"), err
);
1405 printf_send("M%s", path
);
1406 if (mount
&& mount_open_dir
)
1407 printf_send("o%s", path
);
1411 /* CHILD MAIN LOOPS */
1413 /* After forking, the child calls one of these functions */
1415 /* We use a double for total size in order to count beyond 4Gb */
1416 static void usage_cb(gpointer data
)
1418 GList
*paths
= (GList
*) data
;
1419 double total_size
= 0;
1421 dir_counter
= file_counter
= 0;
1423 for (; paths
; paths
= paths
->next
)
1425 guchar
*path
= (guchar
*) paths
->data
;
1431 do_usage(path
, NULL
);
1433 printf_send("'%s: %s\n",
1435 format_double_size(size_tally
));
1436 total_size
+= size_tally
;
1439 g_string_printf(message
, _("'\nTotal: %s ("),
1440 format_double_size(total_size
));
1443 g_string_append_printf(message
,
1444 "%ld %s%s", file_counter
,
1445 file_counter
== 1 ? _("file") : _("files"),
1446 dir_counter
? ", " : ")\n");
1448 if (file_counter
== 0 && dir_counter
== 0)
1449 g_string_append(message
, _("no directories)\n"));
1450 else if (dir_counter
)
1451 g_string_append_printf(message
,
1452 "%ld %s)\n", dir_counter
,
1453 dir_counter
== 1 ? _("directory")
1454 : _("directories"));
1459 #ifdef DO_MOUNT_POINTS
1460 static void mount_cb(gpointer data
)
1462 GList
*paths
= (GList
*) data
;
1463 gboolean mount_points
= FALSE
;
1465 for (; paths
; paths
= paths
->next
)
1467 guchar
*path
= (guchar
*) paths
->data
;
1469 if (mount_is_mounted(path
, NULL
, NULL
))
1470 do_mount(path
, FALSE
); /* Unmount */
1471 else if (g_hash_table_lookup(fstab_mounts
, path
))
1472 do_mount(path
, TRUE
); /* Mount */
1476 mount_points
= TRUE
;
1482 printf_send(_("!No mount points selected!\n"));
1486 /* (use g_dirname() instead?) */
1487 static guchar
*dirname(guchar
*path
)
1491 slash
= strrchr(path
, '/');
1492 g_return_val_if_fail(slash
!= NULL
, g_strdup(path
));
1495 return g_strndup(path
, slash
- path
);
1496 return g_strdup("/");
1499 static void delete_cb(gpointer data
)
1501 GList
*paths
= (GList
*) data
;
1503 for (; paths
; paths
= paths
->next
)
1505 guchar
*path
= (guchar
*) paths
->data
;
1508 dir
= dirname(path
);
1511 do_delete(path
, dir
);
1519 static void find_cb(gpointer data
)
1521 GList
*all_paths
= (GList
*) data
;
1526 for (paths
= all_paths
; paths
; paths
= paths
->next
)
1528 guchar
*path
= (guchar
*) paths
->data
;
1532 do_find(path
, NULL
);
1535 if (!printf_reply(from_parent
, TRUE
,
1536 _("?Another search?")))
1544 static void chmod_cb(gpointer data
)
1546 GList
*paths
= (GList
*) data
;
1548 for (; paths
; paths
= paths
->next
)
1550 guchar
*path
= (guchar
*) paths
->data
;
1555 if (mc_stat(path
, &info
) != 0)
1557 else if (S_ISLNK(info
.st_mode
))
1558 printf_send(_("!'%s' is a symbolic link\n"),
1561 do_chmod(path
, NULL
);
1567 static void list_cb(gpointer data
)
1569 GList
*paths
= (GList
*) data
;
1571 for (; paths
; paths
= paths
->next
)
1573 send_dir((char *) paths
->data
);
1575 action_do_func((char *) paths
->data
, action_dest
);
1581 /* EXTERNAL INTERFACE */
1583 void action_find(GList
*paths
)
1590 report_error(_("You need to select some items "
1591 "to search through"));
1595 if (!last_find_string
)
1596 last_find_string
= g_strdup("'core'");
1598 new_entry_string
= last_find_string
;
1600 abox
= abox_new(_("Find"), FALSE
);
1601 gui_side
= start_action(abox
, find_cb
, paths
,
1602 o_action_force
.int_value
,
1603 o_action_brief
.int_value
,
1604 o_action_recurse
.int_value
,
1605 o_action_newer
.int_value
);
1609 abox_add_results(ABOX(abox
));
1611 gui_side
->default_string
= &last_find_string
;
1612 abox_add_entry(ABOX(abox
), last_find_string
,
1613 new_help_button(show_condition_help
, NULL
));
1614 g_signal_connect(ABOX(abox
)->entry
, "changed",
1615 G_CALLBACK(entry_changed
), gui_side
);
1616 set_find_string_colour(ABOX(abox
)->entry
, last_find_string
);
1618 gui_side
->show_info
= TRUE
;
1619 gui_side
->entry_string_func
= set_find_string_colour
;
1621 number_of_windows
++;
1622 gtk_widget_show_all(abox
);
1625 /* Count disk space used by selected items */
1626 void action_usage(GList
*paths
)
1633 report_error(_("You need to select some items to count"));
1637 abox
= abox_new(_("Disk Usage"), TRUE
);
1639 gui_side
= start_action(abox
, usage_cb
, paths
,
1640 o_action_force
.int_value
,
1641 o_action_brief
.int_value
,
1642 o_action_recurse
.int_value
,
1643 o_action_newer
.int_value
);
1647 gui_side
->show_info
= TRUE
;
1649 number_of_windows
++;
1651 gtk_widget_show(abox
);
1654 /* Mount/unmount listed items (paths).
1655 * Free the list after this function returns.
1656 * If open_dir is TRUE and the dir is successfully mounted, open it.
1657 * quiet can be -1 for default.
1659 void action_mount(GList
*paths
, gboolean open_dir
, int quiet
)
1661 #ifdef DO_MOUNT_POINTS
1666 quiet
= o_action_mount
.int_value
;
1668 mount_open_dir
= open_dir
;
1670 abox
= abox_new(_("Mount / Unmount"), quiet
);
1671 gui_side
= start_action(abox
, mount_cb
, paths
,
1672 o_action_force
.int_value
,
1673 o_action_brief
.int_value
,
1674 o_action_recurse
.int_value
,
1675 o_action_newer
.int_value
);
1679 number_of_windows
++;
1680 gtk_widget_show(abox
);
1683 _("ROX-Filer does not yet support mount points on your "
1685 #endif /* DO_MOUNT_POINTS */
1688 /* Deletes all selected items in the window */
1689 void action_delete(GList
*paths
)
1694 if (!remove_pinned_ok(paths
))
1697 abox
= abox_new(_("Delete"), o_action_delete
.int_value
);
1698 gui_side
= start_action(abox
, delete_cb
, paths
,
1699 o_action_force
.int_value
,
1700 o_action_brief
.int_value
,
1701 o_action_recurse
.int_value
,
1702 o_action_newer
.int_value
);
1706 abox_add_flag(ABOX(abox
),
1707 _("Force"), _("Don't confirm deletion of non-writeable items"),
1708 'F', o_action_force
.int_value
);
1709 abox_add_flag(ABOX(abox
),
1710 _("Brief"), _("Only log directories being deleted"),
1711 'B', o_action_brief
.int_value
);
1713 number_of_windows
++;
1714 gtk_widget_show(abox
);
1717 /* Change the permissions of the selected items */
1718 void action_chmod(GList
*paths
, gboolean force_recurse
, const char *action
)
1722 static GList
*presets
= NULL
;
1723 gboolean recurse
= force_recurse
|| o_action_recurse
.int_value
;
1727 report_error(_("You need to select the items "
1728 "whose permissions you want to change"));
1734 presets
= g_list_append(presets
, (gchar
*)
1735 _("a+x (Make executable/searchable)"));
1736 presets
= g_list_append(presets
, (gchar
*)
1737 _("a-x (Make non-executable/non-searchable)"));
1738 presets
= g_list_append(presets
, (gchar
*)
1739 _("u+rw (Give owner read+write)"));
1740 presets
= g_list_append(presets
, (gchar
*)
1741 _("go-rwx (Private - owner access only)"));
1742 presets
= g_list_append(presets
, (gchar
*)
1743 _("go=u-w (Public access, not write)"));
1746 if (!last_chmod_string
)
1747 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
1750 new_entry_string
= g_strdup(action
);
1752 new_entry_string
= g_strdup(last_chmod_string
);
1754 abox
= abox_new(_("Permissions"), FALSE
);
1755 gui_side
= start_action(abox
, chmod_cb
, paths
,
1756 o_action_force
.int_value
,
1757 o_action_brief
.int_value
,
1759 o_action_newer
.int_value
);
1764 abox_add_flag(ABOX(abox
),
1765 _("Brief"), _("Don't list processed files"),
1766 'B', o_action_brief
.int_value
);
1767 abox_add_flag(ABOX(abox
),
1768 _("Recurse"), _("Also change contents of subdirectories"),
1771 gui_side
->default_string
= &last_chmod_string
;
1772 abox_add_combo(ABOX(abox
), presets
, new_entry_string
,
1773 new_help_button(show_chmod_help
, NULL
));
1775 g_signal_connect(ABOX(abox
)->entry
, "changed",
1776 G_CALLBACK(entry_changed
), gui_side
);
1778 g_signal_connect_swapped(gui_side
->entry
, "activate",
1779 G_CALLBACK(gtk_button_clicked
),
1783 number_of_windows
++;
1784 gtk_widget_show(abox
);
1787 null_g_free(&new_entry_string
);
1790 /* If leaf is NULL then the copy has the same name as the original.
1791 * quiet can be -1 for default.
1793 void action_copy(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1799 quiet
= o_action_copy
.int_value
;
1803 action_do_func
= do_copy
;
1805 abox
= abox_new(_("Copy"), quiet
);
1806 gui_side
= start_action(abox
, list_cb
, paths
,
1807 o_action_force
.int_value
,
1808 o_action_brief
.int_value
,
1809 o_action_recurse
.int_value
,
1810 o_action_newer
.int_value
);
1814 abox_add_flag(ABOX(abox
),
1816 _("Only over-write if source is newer than destination."),
1817 'W', o_action_newer
.int_value
);
1819 number_of_windows
++;
1820 gtk_widget_show(abox
);
1823 /* If leaf is NULL then the file is not renamed.
1824 * quiet can be -1 for default.
1826 void action_move(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1832 quiet
= o_action_move
.int_value
;
1836 action_do_func
= do_move
;
1838 abox
= abox_new(_("Move"), quiet
);
1839 gui_side
= start_action(abox
, list_cb
, paths
,
1840 o_action_force
.int_value
,
1841 o_action_brief
.int_value
,
1842 o_action_recurse
.int_value
,
1843 o_action_newer
.int_value
);
1847 abox_add_flag(ABOX(abox
),
1849 _("Only over-write if source is newer than destination."),
1850 'W', o_action_newer
.int_value
);
1851 number_of_windows
++;
1852 gtk_widget_show(abox
);
1855 /* If leaf is NULL then the link will have the same name */
1856 /* XXX: No quiet option here? */
1857 void action_link(GList
*paths
, const char *dest
, const char *leaf
)
1864 action_do_func
= do_link
;
1866 abox
= abox_new(_("Link"), o_action_link
.int_value
);
1867 gui_side
= start_action(abox
, list_cb
, paths
,
1868 o_action_force
.int_value
,
1869 o_action_brief
.int_value
,
1870 o_action_recurse
.int_value
,
1871 o_action_newer
.int_value
);
1875 number_of_windows
++;
1876 gtk_widget_show(abox
);
1879 void action_init(void)
1881 option_add_int(&o_action_copy
, "action_copy", 1);
1882 option_add_int(&o_action_move
, "action_move", 1);
1883 option_add_int(&o_action_link
, "action_link", 1);
1884 option_add_int(&o_action_delete
, "action_delete", 0);
1885 option_add_int(&o_action_mount
, "action_mount", 1);
1886 option_add_int(&o_action_force
, "action_force", FALSE
);
1887 option_add_int(&o_action_brief
, "action_brief", FALSE
);
1888 option_add_int(&o_action_recurse
, "action_recurse", FALSE
);
1889 option_add_int(&o_action_newer
, "action_newer", FALSE
);
1894 /* Check to see if any of the selected items (or their children) are
1895 * on the pinboard or panel. If so, ask for confirmation.
1897 * TRUE if it's OK to lose them.
1899 static gboolean
remove_pinned_ok(GList
*paths
)
1901 GList
*ask
= NULL
, *next
;
1906 for (; paths
; paths
= paths
->next
)
1908 guchar
*path
= (guchar
*) paths
->data
;
1910 if (icons_require(path
))
1912 if (++ask_n
> MAX_ASK
)
1914 ask
= g_list_append(ask
, path
);
1921 if (ask_n
> MAX_ASK
)
1923 message
= g_string_new(_("Deleting items such as "));
1926 else if (ask_n
== 1)
1927 message
= g_string_new(_("Deleting the item "));
1929 message
= g_string_new(_("Deleting the items "));
1932 for (next
= ask
; next
; next
= next
->next
)
1934 guchar
*path
= (guchar
*) next
->data
;
1937 leaf
= strrchr(path
, '/');
1943 g_string_append_c(message
, '`');
1944 g_string_append(message
, leaf
);
1945 g_string_append_c(message
, '\'');
1947 if (i
== ask_n
- 1 && i
> 0)
1948 g_string_append(message
, _(" and "));
1950 g_string_append(message
, ", ");
1956 message
= g_string_append(message
,
1957 _(" will affect some items on the pinboard "
1958 "or panel - really delete it?"));
1961 if (ask_n
> MAX_ASK
)
1962 message
= g_string_append_c(message
, ',');
1963 message
= g_string_append(message
,
1964 _(" will affect some items on the pinboard "
1965 "or panel - really delete them?"));
1968 retval
= get_choice(PROJECT
, message
->str
,
1969 2, _("Cancel"), _("Delete")) == 1;
1971 g_string_free(message
, TRUE
);
1976 void set_find_string_colour(GtkWidget
*widget
, const guchar
*string
)
1978 FindCondition
*cond
;
1980 cond
= find_compile(string
);
1981 entry_set_error(widget
, !cond
);
1983 find_condition_free(cond
);