4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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
);
86 /* These don't need to be in a structure because we fork() before
89 static gboolean mount_open_dir
= FALSE
;
90 static int from_parent
= 0;
91 static FILE *to_parent
= NULL
;
92 static gboolean quiet
= FALSE
;
93 static GString
*message
= NULL
;
94 static const char *action_dest
= NULL
;
95 static const char *action_leaf
= NULL
;
96 static void (*action_do_func
)(const char *source
, const char *dest
);
97 static double size_tally
; /* For Disk Usage */
98 static unsigned long dir_counter
; /* For Disk Usage */
99 static unsigned long file_counter
; /* For Disk Usage */
101 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
102 static FindCondition
*find_condition
= NULL
; /* For Find */
104 /* Only used by child */
105 static gboolean o_force
= FALSE
;
106 static gboolean o_brief
= FALSE
;
107 static gboolean o_recurse
= FALSE
;
108 static gboolean o_newer
= FALSE
;
110 static Option o_action_copy
, o_action_move
, o_action_link
;
111 static Option o_action_delete
, o_action_mount
;
112 static Option o_action_force
, o_action_brief
, o_action_recurse
;
113 static Option o_action_newer
;
115 /* Whenever the text in these boxes is changed we store a copy of the new
116 * string to be used as the default next time.
118 static guchar
*last_chmod_string
= NULL
;
119 static guchar
*last_find_string
= NULL
;
121 /* Set to one of the above before forking. This may change over a call to
122 * reply(). It is reset to NULL once the text is parsed.
124 static guchar
*new_entry_string
= NULL
;
126 /* Static prototypes */
127 static void send_done(void);
128 static void send_check_path(const gchar
*path
);
129 static void send_mount_path(const gchar
*path
);
130 static gboolean
printf_send(const char *msg
, ...);
131 static gboolean
send(void);
132 static gboolean
send_error(void);
133 static gboolean
send_dir(const char *dir
);
134 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
135 static void do_mount(const guchar
*path
, gboolean mount
);
136 static gboolean
printf_reply(int fd
, gboolean ignore_quiet
,
137 const char *msg
, ...);
138 static gboolean
remove_pinned_ok(GList
*paths
);
143 /* This is called whenever the user edits the entry box (if any) - send the
146 static void entry_changed(GtkEditable
*entry
, GUIside
*gui_side
)
150 g_return_if_fail(gui_side
->default_string
!= NULL
);
152 text
= gtk_editable_get_chars(entry
, 0, -1);
154 if (gui_side
->entry_string_func
)
155 gui_side
->entry_string_func(GTK_WIDGET(entry
), text
);
157 g_free(*(gui_side
->default_string
));
158 *(gui_side
->default_string
) = text
; /* Gets text's ref */
160 if (!gui_side
->to_child
)
163 fputc('E', gui_side
->to_child
);
164 fputs(text
, gui_side
->to_child
);
165 fputc('\n', gui_side
->to_child
);
166 fflush(gui_side
->to_child
);
169 void show_condition_help(gpointer data
)
174 help
= gtk_dialog_new_with_buttons(
175 _("Find expression reference"),
177 GTK_STOCK_CLOSE
, GTK_RESPONSE_CANCEL
,
179 gtk_dialog_set_default_response(GTK_DIALOG(help
), GTK_RESPONSE_CANCEL
);
181 text
= gtk_label_new(NULL
);
182 gtk_misc_set_padding(GTK_MISC(text
), 2, 2);
183 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help
)->vbox
), text
);
184 gtk_label_set_selectable(GTK_LABEL(text
), TRUE
);
185 gtk_label_set_markup(GTK_LABEL(text
), _(
186 "<u>Quick Start</u>\n"
187 "Just put the name of the file you're looking for in single quotes:\n"
188 "<b>'index.html'</b> (to find a file called 'index.html')\n"
191 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
192 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
193 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
194 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
195 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
196 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
197 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
199 "<u>Simple Tests</u>\n"
200 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
202 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
204 "<b>IsEmpty, IsMine</b>\n"
205 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
206 "contains a slash then the match is against the full path; otherwise it is\n"
207 "against the leafname only.\n"
209 "<u>Comparisons</u>\n"
210 "<b><, <=, =, !=, >, >=, After, Before</b> (compare two values)\n"
211 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
212 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
213 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
217 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
218 "a % in 'command' is replaced with the path of the current file)\n"
219 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
221 g_signal_connect(help
, "response",
222 G_CALLBACK(gtk_widget_destroy
), NULL
);
224 gtk_widget_show_all(help
);
227 static void show_chmod_help(gpointer data
)
232 help
= gtk_dialog_new_with_buttons(
233 _("Find expression reference"),
235 GTK_STOCK_CLOSE
, GTK_RESPONSE_CANCEL
,
237 gtk_dialog_set_default_response(GTK_DIALOG(help
), GTK_RESPONSE_CANCEL
);
239 text
= gtk_label_new(NULL
);
240 gtk_misc_set_padding(GTK_MISC(text
), 2, 2);
241 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help
)->vbox
), text
);
242 gtk_label_set_selectable(GTK_LABEL(text
), TRUE
);
243 gtk_label_set_markup(GTK_LABEL(text
), _(
244 "Normally, you can just select a command from the menu (click \n"
245 "on the arrow beside the command box). Sometimes, you need more...\n"
247 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
248 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
249 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
250 "determines whether to\n"
251 "change the permissions for the User (owner), Group or Others.\n"
252 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
253 "exactly the permissions.\n"
254 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
256 "Bracketed text and spaces are ignored.\n"
259 "<b>u+rw</b>: the file owner gains read and write permission\n"
260 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
261 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
263 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
264 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
265 "executable by anyone become executable by everyone\n"
266 "<b>u+rw, go+r</b>: two commands at once!\n"
267 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
268 "<b>755</b>: set the permissions directly\n"
270 "See the chmod(1) man page for full details."));
272 g_signal_connect(help
, "response",
273 G_CALLBACK(gtk_widget_destroy
), NULL
);
275 gtk_widget_show_all(help
);
278 static void process_message(GUIside
*gui_side
, const gchar
*buffer
)
280 ABox
*abox
= gui_side
->abox
;
283 abox_ask(abox
, buffer
+ 1);
284 else if (*buffer
== 's')
285 dir_check_this(buffer
+ 1); /* Update this item */
286 else if (*buffer
== '=')
287 abox_add_filename(abox
, buffer
+ 1);
288 else if (*buffer
== '#')
289 abox_clear_results(abox
);
290 else if (*buffer
== 'X')
292 filer_close_recursive(buffer
+ 1);
293 /* Let child know it's safe to continue... */
294 fputc('X', gui_side
->to_child
);
295 fflush(gui_side
->to_child
);
297 else if (*buffer
== 'm' || *buffer
== 'M')
299 /* Mount / major changes to this path */
303 mount_user_mount(buffer
+ 1);
305 filer_check_mounted(buffer
+ 1);
307 else if (*buffer
== '/')
308 abox_set_current_object(abox
, buffer
+ 1);
309 else if (*buffer
== 'o')
310 filer_opendir(buffer
+ 1, NULL
, NULL
);
311 else if (*buffer
== '!')
314 abox_log(abox
, buffer
+ 1, "error");
316 else if (*buffer
== '<')
317 abox_set_file(abox
, 0, buffer
+1);
318 else if (*buffer
== '>')
320 abox_set_file(abox
, 1, buffer
+1);
321 abox_show_compare(abox
, TRUE
);
324 abox_log(abox
, buffer
+ 1, NULL
);
327 /* Called when the child sends us a message */
328 static void message_from_child(gpointer data
,
330 GdkInputCondition condition
)
333 GUIside
*gui_side
= (GUIside
*) data
;
334 ABox
*abox
= gui_side
->abox
;
335 GtkTextBuffer
*text_buffer
;
337 text_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox
->log
));
339 if (read_exact(source
, buf
, 4))
345 message_len
= strtol(buf
, NULL
, 16);
346 buffer
= g_malloc(message_len
+ 1);
347 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
349 buffer
[message_len
] = '\0';
350 process_message(gui_side
, buffer
);
354 g_printerr("Child died in the middle of a message.\n");
357 if (gui_side
->abort_attempts
)
358 abox_log(abox
, _("\nProcess terminated.\n"), "error");
360 /* The child is dead */
363 fclose(gui_side
->to_child
);
364 gui_side
->to_child
= NULL
;
365 close(gui_side
->from_child
);
366 g_source_remove(gui_side
->input_tag
);
367 abox_cancel_ask(gui_side
->abox
);
369 if (gui_side
->errors
)
373 if (gui_side
->errors
== 1)
374 report
= g_strdup(_("There was one error.\n"));
376 report
= g_strdup_printf(_("There were %d errors.\n"),
379 gtk_text_buffer_insert_at_cursor(text_buffer
, report
, -1);
383 else if (gui_side
->show_info
== FALSE
)
384 gtk_widget_destroy(GTK_WIDGET(gui_side
->abox
));
387 /* Scans src_dir, calling cb(item, dest_path) for each item */
388 static void for_dir_contents(ForDirCB
*cb
,
390 const char *dest_path
)
394 GList
*list
= NULL
, *next
;
396 d
= mc_opendir(src_dir
);
399 /* Message displayed is "ERROR reading 'path': message" */
400 printf_send("!%s '%s': %s\n", _("ERROR reading"),
401 src_dir
, g_strerror(errno
));
407 while ((ent
= mc_readdir(d
)))
409 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
410 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
412 list
= g_list_prepend(list
, g_strdup(make_path(src_dir
,
417 for (next
= list
; next
; next
= next
->next
)
419 cb((char *) next
->data
, dest_path
);
426 /* Read this many bytes into the buffer. TRUE on success. */
427 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
432 got
= read(source
, buffer
, len
);
441 static void send_done(void)
443 printf_send(_("'\nDone\n"));
446 /* Notify the filer that this item has been updated */
447 static void send_check_path(const gchar
*path
)
449 printf_send("s%s", path
);
452 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
453 static void send_mount_path(const gchar
*path
)
455 printf_send("m%s", path
);
458 /* Send a message to the filer process. The first character indicates the
459 * type of the message.
461 static gboolean
printf_send(const char *msg
, ...)
467 tmp
= g_strdup_vprintf(msg
, args
);
470 g_string_assign(message
, tmp
);
476 /* Send 'message' to our parent process. TRUE on success. */
477 static gboolean
send(void)
482 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
484 sprintf(len_buffer
, "%04x", message
->len
);
485 fwrite(len_buffer
, 1, 4, to_parent
);
486 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
488 return len
== (ssize_t
) message
->len
;
491 /* Set the directory indicator at the top of the window */
492 static gboolean
send_dir(const char *dir
)
494 return printf_send("/%s", dir
);
497 static gboolean
send_error(void)
499 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno
));
502 static void response(GtkDialog
*dialog
, gint response
, GUIside
*gui_side
)
506 if (!gui_side
->to_child
)
509 if (response
== GTK_RESPONSE_YES
)
511 else if (response
== GTK_RESPONSE_NO
)
516 fputc(code
, gui_side
->to_child
);
517 fflush(gui_side
->to_child
);
518 abox_show_compare(gui_side
->abox
, FALSE
);
521 static void flag_toggled(ABox
*abox
, gint flag
, GUIside
*gui_side
)
523 if (!gui_side
->to_child
)
526 fputc(flag
, gui_side
->to_child
);
527 fflush(gui_side
->to_child
);
530 static void read_new_entry_text(void)
536 new = g_string_new(NULL
);
540 len
= read(from_parent
, &c
, 1);
543 fprintf(stderr
, "read() error: %s\n",
545 _exit(1); /* Parent died? */
550 g_string_append_c(new, c
);
553 g_free(new_entry_string
);
554 new_entry_string
= new->str
;
555 g_string_free(new, FALSE
);
558 static void process_flag(char flag
)
569 o_recurse
= !o_recurse
;
578 read_new_entry_text();
581 printf_send("!ERROR: Bad message '%c'\n", flag
);
586 /* If the parent has sent any flag toggles, read them */
587 static void check_flags(void)
598 FD_SET(from_parent
, &set
);
601 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
604 g_error("select() failed: %s\n", g_strerror(errno
));
608 got
= read(from_parent
, &retval
, 1);
610 g_error("read() error: %s\n", g_strerror(errno
));
612 process_flag(retval
);
616 /* Read until the user sends a reply. If ignore_quiet is TRUE then
617 * the user MUST click Yes or No, else treat quiet on as Yes.
618 * If the user needs prompting then does send().
620 static gboolean
printf_reply(int fd
, gboolean ignore_quiet
,
621 const char *msg
, ...)
628 if (quiet
&& !ignore_quiet
)
632 tmp
= g_strdup_vprintf(msg
, args
);
635 g_string_assign(message
, tmp
);
642 len
= read(fd
, &retval
, 1);
645 fprintf(stderr
, "read() error: %s\n",
647 _exit(1); /* Parent died? */
653 printf_send("' %s\n", _("Yes"));
656 printf_send("' %s\n", _("No"));
659 process_flag(retval
);
665 static void abort_operation(GtkWidget
*widget
, gpointer data
)
667 GUIside
*gui_side
= (GUIside
*) data
;
671 if (gui_side
->abort_attempts
== 0)
673 abox_log(ABOX(widget
),
674 _("\nAsking child process to terminate...\n"),
676 kill(-gui_side
->child
, SIGTERM
);
680 abox_log(ABOX(widget
),
681 _("\nTrying to KILL run-away process...\n"),
683 kill(-gui_side
->child
, SIGKILL
);
684 kill(-gui_side
->child
, SIGCONT
);
686 gui_side
->abort_attempts
++;
689 gtk_widget_destroy(widget
);
692 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
694 GUIside
*gui_side
= (GUIside
*) data
;
698 kill(-gui_side
->child
, SIGTERM
);
699 fclose(gui_side
->to_child
);
700 close(gui_side
->from_child
);
701 g_source_remove(gui_side
->input_tag
);
709 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
710 * (NULL on failure). The child calls func().
712 static GUIside
*start_action(GtkWidget
*abox
, ActionChild
*func
, gpointer data
,
713 int force
, int brief
, int recurse
, int newer
)
716 int filedes
[4]; /* 0 and 2 are for reading */
719 struct sigaction act
;
723 report_error("pipe: %s", g_strerror(errno
));
724 gtk_widget_destroy(abox
);
728 if (pipe(filedes
+ 2))
732 report_error("pipe: %s", g_strerror(errno
));
733 gtk_widget_destroy(abox
);
737 autoq
= gtk_toggle_button_get_active(
738 GTK_TOGGLE_BUTTON(ABOX(abox
)->quiet
));
749 report_error("fork: %s", g_strerror(errno
));
750 gtk_widget_destroy(abox
);
753 /* We are the child */
755 /* Create a new process group */
760 dir_drop_all_dnotifies();
762 /* Reset the SIGCHLD handler */
763 act
.sa_handler
= SIG_DFL
;
764 sigemptyset(&act
.sa_mask
);
766 sigaction(SIGCHLD
, &act
, NULL
);
768 message
= g_string_new(NULL
);
771 to_parent
= fdopen(filedes
[1], "wb");
772 from_parent
= filedes
[2];
778 /* We are the parent */
781 gui_side
= g_new(GUIside
, 1);
782 gui_side
->from_child
= filedes
[0];
783 gui_side
->to_child
= fdopen(filedes
[3], "wb");
784 gui_side
->child
= child
;
785 gui_side
->errors
= 0;
786 gui_side
->show_info
= FALSE
;
787 gui_side
->default_string
= NULL
;
788 gui_side
->entry_string_func
= NULL
;
789 gui_side
->abort_attempts
= 0;
791 gui_side
->abox
= ABOX(abox
);
792 g_signal_connect(abox
, "destroy",
793 G_CALLBACK(destroy_action_window
), gui_side
);
795 g_signal_connect(abox
, "response", G_CALLBACK(response
), gui_side
);
796 g_signal_connect(abox
, "flag_toggled",
797 G_CALLBACK(flag_toggled
), gui_side
);
798 g_signal_connect(abox
, "abort_operation",
799 G_CALLBACK(abort_operation
), gui_side
);
801 gui_side
->input_tag
= gtk_input_add_full(gui_side
->from_child
,
804 NULL
, gui_side
, NULL
);
809 /* ACTIONS ON ONE ITEM */
811 /* These may call themselves recursively, or ask questions, etc */
813 /* Updates the global size_tally, file_counter and dir_counter */
814 static void do_usage(const char *src_path
, const char *unused
)
820 if (mc_lstat(src_path
, &info
))
822 printf_send("'%s:\n", src_path
);
825 else if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
828 size_tally
+= info
.st_size
;
830 else if (S_ISDIR(info
.st_mode
))
833 if (printf_reply(from_parent
, FALSE
,
834 _("?Count contents of %s?"), src_path
))
837 safe_path
= g_strdup(src_path
);
838 for_dir_contents(do_usage
, safe_path
, safe_path
);
846 /* dest_path is the dir containing src_path */
847 static void do_delete(const char *src_path
, const char *unused
)
855 if (mc_lstat(src_path
, &info
))
861 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
862 : access(src_path
, W_OK
) != 0;
863 if (write_prot
|| !quiet
)
865 printf_send("<%s", src_path
);
867 if (!printf_reply(from_parent
, write_prot
&& !o_force
,
868 _("?Delete %s'%s'?"),
869 write_prot
? _("WRITE-PROTECTED ") : "",
874 printf_send(_("'Deleting '%s'\n"), src_path
);
876 safe_path
= g_strdup(src_path
);
878 if (S_ISDIR(info
.st_mode
))
880 for_dir_contents(do_delete
, safe_path
, safe_path
);
881 if (rmdir(safe_path
))
887 printf_send(_("'Directory '%s' deleted\n"), safe_path
);
888 send_mount_path(safe_path
);
890 else if (unlink(src_path
))
894 send_check_path(safe_path
);
895 if (strcmp(g_basename(safe_path
), ".DirIcon") == 0)
898 dir
= g_path_get_dirname(safe_path
);
899 send_check_path(dir
);
907 static void do_eject(const char *path
)
909 const char *argv
[3] = {NULL
, NULL
, NULL
};
916 printf_send("<%s", path
);
918 if (!printf_reply(from_parent
, !o_force
,
924 printf_send(_("'Eject '%s'\n"), path
);
929 err
=fork_exec_wait(argv
);
932 printf_send(_("!%s\neject failed\n"), err
);
938 /* path is the item to check. If is is a directory then we may recurse
939 * (unless prune is used).
941 static void do_find(const char *path
, const char *unused
)
949 if (!printf_reply(from_parent
, FALSE
, _("?Check '%s'?"), path
))
955 if (new_entry_string
)
957 find_condition_free(find_condition
);
958 find_condition
= find_compile(new_entry_string
);
959 null_g_free(&new_entry_string
);
965 printf_send(_("!Invalid find condition - "
966 "change it and try again\n"));
967 if (!printf_reply(from_parent
, TRUE
,
968 _("?Check '%s'?"), path
))
972 if (mc_lstat(path
, &info
.stats
))
975 printf_send(_("'(while checking '%s')\n"), path
);
979 info
.fullpath
= path
;
980 time(&info
.now
); /* XXX: Not for each check! */
982 info
.leaf
= g_basename(path
);
984 if (find_test_condition(find_condition
, &info
))
985 printf_send("=%s", path
);
987 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
990 safe_path
= g_strdup(path
);
991 for_dir_contents(do_find
, safe_path
, safe_path
);
996 /* Like mode_compile(), but ignores spaces and bracketed bits */
997 static struct mode_change
*nice_mode_compile(const char *mode_string
,
998 unsigned int masked_ops
)
1002 struct mode_change
*retval
= NULL
;
1004 new = g_string_new(NULL
);
1006 for (; *mode_string
; mode_string
++)
1008 if (*mode_string
== '(')
1010 if (*mode_string
== ')')
1018 if (brackets
== 0 && *mode_string
!= ' ')
1019 g_string_append_c(new, *mode_string
);
1023 retval
= mode_compile(new->str
, masked_ops
);
1024 g_string_free(new, TRUE
);
1028 static void do_chmod(const char *path
, const char *unused
)
1035 if (mc_lstat(path
, &info
))
1040 if (S_ISLNK(info
.st_mode
))
1045 printf_send("<%s", path
);
1047 if (!printf_reply(from_parent
, FALSE
,
1048 _("?Change permissions of '%s'?"), path
))
1052 printf_send(_("'Changing permissions of '%s'\n"), path
);
1056 if (new_entry_string
)
1059 mode_free(mode_change
);
1060 mode_change
= nice_mode_compile(new_entry_string
,
1062 null_g_free(&new_entry_string
);
1069 _("!Invalid mode command - change it and try again\n"));
1070 if (!printf_reply(from_parent
, TRUE
,
1071 _("?Change permissions of '%s'?"), path
))
1075 if (mc_lstat(path
, &info
))
1080 if (S_ISLNK(info
.st_mode
))
1083 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1084 if (chmod(path
, new_mode
))
1090 send_check_path(path
);
1092 if (S_ISDIR(info
.st_mode
))
1094 send_mount_path(path
);
1099 safe_path
= g_strdup(path
);
1100 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1106 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1107 * is set then that is the new leafname, otherwise the leafname stays
1110 static const char *make_dest_path(const char *object
, const char *dir
)
1118 leaf
= strrchr(object
, '/');
1120 leaf
= object
; /* Error? */
1125 return make_path(dir
, leaf
);
1128 /* If action_leaf is not NULL it specifies the new leaf name */
1129 static void do_copy2(const char *path
, const char *dest
)
1131 const char *dest_path
;
1133 struct stat dest_info
;
1137 dest_path
= make_dest_path(path
, dest
);
1139 if (mc_lstat(path
, &info
))
1145 if (mc_lstat(dest_path
, &dest_info
) == 0)
1150 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1152 if (!merge
&& o_newer
&& info
.st_mtime
> dest_info
.st_mtime
)
1154 /* Newer; keep going */
1158 printf_send("<%s", path
);
1159 printf_send(">%s", dest_path
);
1160 if (!printf_reply(from_parent
, TRUE
,
1161 _("?'%s' already exists - %s?"),
1163 merge
? _("merge contents")
1170 if (S_ISDIR(dest_info
.st_mode
))
1171 err
= rmdir(dest_path
);
1173 err
= unlink(dest_path
);
1178 if (errno
!= ENOENT
)
1180 printf_send(_("'Trying copy anyway...\n"));
1186 printf_send("<%s", path
);
1188 if (!printf_reply(from_parent
, FALSE
,
1189 _("?Copy %s as %s?"), path
, dest_path
))
1193 printf_send(_("'Copying %s as %s\n"), path
, dest_path
);
1195 if (S_ISDIR(info
.st_mode
))
1197 mode_t mode
= info
.st_mode
;
1198 char *safe_path
, *safe_dest
;
1199 struct stat dest_info
;
1202 safe_path
= g_strdup(path
);
1203 safe_dest
= g_strdup(dest_path
);
1205 exists
= !mc_lstat(dest_path
, &dest_info
);
1207 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1208 printf_send(_("!ERROR: Destination already exists, "
1209 "but is not a directory\n"));
1210 else if (exists
== FALSE
&& mkdir(dest_path
, 0700 | mode
))
1215 /* (just been created then) */
1216 send_check_path(dest_path
);
1219 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1220 /* Note: dest_path now invalid... */
1226 /* We may have created the directory with
1227 * more permissions than the source so that
1228 * we could write to it... change it back now.
1230 if (chmod(safe_dest
, mode
))
1232 /* Some filesystems don't support
1233 * SetGID and SetUID bits. Ignore
1240 /* Also, try to preserve the timestamps */
1241 utb
.actime
= info
.st_atime
;
1242 utb
.modtime
= info
.st_mtime
;
1244 utime(safe_dest
, &utb
);
1251 else if (S_ISLNK(info
.st_mode
))
1255 /* Not all versions of cp(1) can make symlinks,
1256 * so we special-case it.
1259 target
= readlink_dup(path
);
1262 if (symlink(target
, dest_path
))
1265 send_check_path(dest_path
);
1276 error
= copy_file(path
, dest_path
);
1280 printf_send(_("!%s\nFailed to copy '%s'\n"),
1285 send_check_path(dest_path
);
1289 /* If action_leaf is not NULL it specifies the new leaf name */
1290 static void do_move2(const char *path
, const char *dest
)
1292 const char *dest_path
;
1293 const char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1300 dest_path
= make_dest_path(path
, dest
);
1302 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1304 if (access(dest_path
, F_OK
) == 0)
1309 if (mc_lstat(dest_path
, &info
))
1315 if (!is_dir
&& o_newer
&& info2
.st_mtime
> info
.st_mtime
)
1317 /* Newer; keep going */
1320 printf_send("<%s", path
);
1321 printf_send(">%s", dest_path
);
1322 if (!printf_reply(from_parent
, TRUE
,
1323 _("?'%s' already exists - overwrite?"),
1328 if (S_ISDIR(info
.st_mode
))
1329 err
= rmdir(dest_path
);
1331 err
= unlink(dest_path
);
1336 if (errno
!= ENOENT
)
1338 printf_send(_("'Trying move anyway...\n"));
1343 printf_send("<%s", path
);
1345 if (!printf_reply(from_parent
, FALSE
,
1346 _("?Move %s as %s?"), path
, dest_path
))
1350 printf_send(_("'Moving %s as %s\n"), path
, dest_path
);
1353 argv
[3] = dest_path
;
1355 err
= fork_exec_wait(argv
);
1358 printf_send(_("!%s\nFailed to move %s as %s\n"),
1359 err
, path
, dest_path
);
1364 send_check_path(dest_path
);
1367 send_mount_path(path
);
1369 send_check_path(path
);
1373 /* Copy path to dest.
1374 * Check that path not copied into itself.
1376 static void do_copy(const char *path
, const char *dest
)
1378 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1379 printf_send(_("!ERROR: Can't copy object into itself\n"));
1382 do_copy2(path
, dest
);
1383 send_check_path(dest
);
1387 /* Move path to dest.
1388 * Check that path not moved into itself.
1390 static void do_move(const char *path
, const char *dest
)
1392 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1394 _("!ERROR: Can't move/rename object into itself\n"));
1397 do_move2(path
, dest
);
1398 send_check_path(dest
);
1402 static void do_link(const char *path
, const char *dest
)
1404 const char *dest_path
;
1408 dest_path
= make_dest_path(path
, dest
);
1411 printf_send(_("'Linking %s as %s\n"), path
, dest_path
);
1413 printf_send("<%s", path
);
1415 if (!printf_reply(from_parent
, FALSE
,
1416 _("?Link %s as %s?"), path
, dest_path
))
1420 if (symlink(path
, dest_path
))
1423 send_check_path(dest_path
);
1426 /* Mount/umount this item (depending on 'mount') */
1427 static void do_mount(const guchar
*path
, gboolean mount
)
1429 const char *argv
[3] = {NULL
, NULL
, NULL
};
1434 argv
[0] = mount
? "mount" : "umount";
1438 printf_send(mount
? _("'Mounting %s\n")
1439 : _("'Unmounting %s\n"),
1441 else if (!printf_reply(from_parent
, FALSE
,
1442 mount
? _("?Mount %s?")
1443 : _("?Unmount %s?"),
1450 /* Need to close all sub-directories now, or we
1451 * can't unmount if dnotify is used.
1453 printf_send("X%s", path
);
1454 /* Wait until it's safe... */
1455 read(from_parent
, &c
, 1);
1456 g_return_if_fail(c
== 'X');
1459 err
= fork_exec_wait(argv
);
1463 _("!%s\nMount failed\n") :
1464 _("!%s\nUnmount failed\n"), err
);
1467 /* Mount may have worked even on error, eg if we try to mount
1468 * a read-only disk read/write, it gets mounted read-only
1471 if (mount
&& mount_is_mounted(path
, NULL
, NULL
))
1472 printf_send(_("'(seems to be mounted now anyway)\n"));
1477 printf_send("M%s", path
);
1478 if (mount
&& mount_open_dir
)
1479 printf_send("o%s", path
);
1482 /* CHILD MAIN LOOPS */
1484 /* After forking, the child calls one of these functions */
1486 /* We use a double for total size in order to count beyond 4Gb */
1487 static void usage_cb(gpointer data
)
1489 GList
*paths
= (GList
*) data
;
1490 double total_size
= 0;
1492 dir_counter
= file_counter
= 0;
1494 for (; paths
; paths
= paths
->next
)
1496 guchar
*path
= (guchar
*) paths
->data
;
1502 do_usage(path
, NULL
);
1504 printf_send("'%s: %s\n",
1506 format_double_size(size_tally
));
1507 total_size
+= size_tally
;
1510 g_string_printf(message
, _("'\nTotal: %s ("),
1511 format_double_size(total_size
));
1514 g_string_append_printf(message
,
1515 "%ld %s%s", file_counter
,
1516 file_counter
== 1 ? _("file") : _("files"),
1517 dir_counter
? ", " : ")\n");
1519 if (file_counter
== 0 && dir_counter
== 0)
1520 g_string_append(message
, _("no directories)\n"));
1521 else if (dir_counter
)
1522 g_string_append_printf(message
,
1523 "%ld %s)\n", dir_counter
,
1524 dir_counter
== 1 ? _("directory")
1525 : _("directories"));
1530 #ifdef DO_MOUNT_POINTS
1531 static void mount_cb(gpointer data
)
1533 GList
*paths
= (GList
*) data
;
1534 gboolean mount_points
= FALSE
;
1536 for (; paths
; paths
= paths
->next
)
1538 guchar
*path
= (guchar
*) paths
->data
;
1541 target
= readlink_dup(path
);
1545 if (mount_is_mounted(target
, NULL
, NULL
))
1547 mount_points
= TRUE
;
1548 do_mount(target
, FALSE
); /* Unmount */
1550 else if (g_hash_table_lookup(fstab_mounts
, target
))
1552 mount_points
= TRUE
;
1553 do_mount(target
, TRUE
); /* Mount */
1563 printf_send(_("!No mount points selected!\n"));
1567 /* (use g_dirname() instead?) */
1568 static guchar
*dirname(guchar
*path
)
1572 slash
= strrchr(path
, '/');
1573 g_return_val_if_fail(slash
!= NULL
, g_strdup(path
));
1576 return g_strndup(path
, slash
- path
);
1577 return g_strdup("/");
1580 static void delete_cb(gpointer data
)
1582 GList
*paths
= (GList
*) data
;
1584 for (; paths
; paths
= paths
->next
)
1586 guchar
*path
= (guchar
*) paths
->data
;
1589 dir
= dirname(path
);
1592 do_delete(path
, dir
);
1600 static void eject_cb(gpointer data
)
1602 GList
*paths
= (GList
*) data
;
1604 for (; paths
; paths
= paths
->next
)
1606 guchar
*path
= (guchar
*) paths
->data
;
1616 static void find_cb(gpointer data
)
1618 GList
*all_paths
= (GList
*) data
;
1623 for (paths
= all_paths
; paths
; paths
= paths
->next
)
1625 guchar
*path
= (guchar
*) paths
->data
;
1629 do_find(path
, NULL
);
1632 if (!printf_reply(from_parent
, TRUE
,
1633 _("?Another search?")))
1641 static void chmod_cb(gpointer data
)
1643 GList
*paths
= (GList
*) data
;
1645 for (; paths
; paths
= paths
->next
)
1647 guchar
*path
= (guchar
*) paths
->data
;
1652 if (mc_stat(path
, &info
) != 0)
1654 else if (S_ISLNK(info
.st_mode
))
1655 printf_send(_("!'%s' is a symbolic link\n"),
1658 do_chmod(path
, NULL
);
1664 static void list_cb(gpointer data
)
1666 GList
*paths
= (GList
*) data
;
1668 for (; paths
; paths
= paths
->next
)
1670 send_dir((char *) paths
->data
);
1672 action_do_func((char *) paths
->data
, action_dest
);
1678 /* EXTERNAL INTERFACE */
1680 void action_find(GList
*paths
)
1687 report_error(_("You need to select some items "
1688 "to search through"));
1692 if (!last_find_string
)
1693 last_find_string
= g_strdup("'core'");
1695 new_entry_string
= last_find_string
;
1697 abox
= abox_new(_("Find"), FALSE
);
1698 gui_side
= start_action(abox
, find_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_results(ABOX(abox
));
1708 gui_side
->default_string
= &last_find_string
;
1709 abox_add_entry(ABOX(abox
), last_find_string
,
1710 new_help_button(show_condition_help
, NULL
));
1711 g_signal_connect(ABOX(abox
)->entry
, "changed",
1712 G_CALLBACK(entry_changed
), gui_side
);
1713 set_find_string_colour(ABOX(abox
)->entry
, last_find_string
);
1715 gui_side
->show_info
= TRUE
;
1716 gui_side
->entry_string_func
= set_find_string_colour
;
1718 number_of_windows
++;
1719 gtk_widget_show_all(abox
);
1722 /* Count disk space used by selected items */
1723 void action_usage(GList
*paths
)
1730 report_error(_("You need to select some items to count"));
1734 abox
= abox_new(_("Disk Usage"), TRUE
);
1736 gui_side
= start_action(abox
, usage_cb
, paths
,
1737 o_action_force
.int_value
,
1738 o_action_brief
.int_value
,
1739 o_action_recurse
.int_value
,
1740 o_action_newer
.int_value
);
1744 gui_side
->show_info
= TRUE
;
1746 number_of_windows
++;
1748 gtk_widget_show(abox
);
1751 /* Mount/unmount listed items (paths).
1752 * Free the list after this function returns.
1753 * If open_dir is TRUE and the dir is successfully mounted, open it.
1754 * quiet can be -1 for default.
1756 void action_mount(GList
*paths
, gboolean open_dir
, int quiet
)
1758 #ifdef DO_MOUNT_POINTS
1763 quiet
= o_action_mount
.int_value
;
1765 mount_open_dir
= open_dir
;
1767 abox
= abox_new(_("Mount / Unmount"), quiet
);
1768 gui_side
= start_action(abox
, mount_cb
, paths
,
1769 o_action_force
.int_value
,
1770 o_action_brief
.int_value
,
1771 o_action_recurse
.int_value
,
1772 o_action_newer
.int_value
);
1776 number_of_windows
++;
1777 gtk_widget_show(abox
);
1780 _("ROX-Filer does not yet support mount points on your "
1782 #endif /* DO_MOUNT_POINTS */
1785 /* Delete these paths */
1786 void action_delete(GList
*paths
)
1791 if (!remove_pinned_ok(paths
))
1794 abox
= abox_new(_("Delete"), o_action_delete
.int_value
);
1795 gui_side
= start_action(abox
, delete_cb
, paths
,
1796 o_action_force
.int_value
,
1797 o_action_brief
.int_value
,
1798 o_action_recurse
.int_value
,
1799 o_action_newer
.int_value
);
1803 abox_add_flag(ABOX(abox
),
1804 _("Force"), _("Don't confirm deletion of non-writeable items"),
1805 'F', o_action_force
.int_value
);
1806 abox_add_flag(ABOX(abox
),
1807 _("Brief"), _("Only log directories being deleted"),
1808 'B', o_action_brief
.int_value
);
1810 number_of_windows
++;
1811 gtk_widget_show(abox
);
1814 /* Change the permissions of the selected items */
1815 void action_chmod(GList
*paths
, gboolean force_recurse
, const char *action
)
1819 static GList
*presets
= NULL
;
1820 gboolean recurse
= force_recurse
|| o_action_recurse
.int_value
;
1824 report_error(_("You need to select the items "
1825 "whose permissions you want to change"));
1831 presets
= g_list_append(presets
, (gchar
*)
1832 _("a+x (Make executable/searchable)"));
1833 presets
= g_list_append(presets
, (gchar
*)
1834 _("a-x (Make non-executable/non-searchable)"));
1835 presets
= g_list_append(presets
, (gchar
*)
1836 _("u+rw (Give owner read+write)"));
1837 presets
= g_list_append(presets
, (gchar
*)
1838 _("go-rwx (Private - owner access only)"));
1839 presets
= g_list_append(presets
, (gchar
*)
1840 _("go=u-w (Public access, not write)"));
1843 if (!last_chmod_string
)
1844 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
1847 new_entry_string
= g_strdup(action
);
1849 new_entry_string
= g_strdup(last_chmod_string
);
1851 abox
= abox_new(_("Permissions"), FALSE
);
1852 gui_side
= start_action(abox
, chmod_cb
, paths
,
1853 o_action_force
.int_value
,
1854 o_action_brief
.int_value
,
1856 o_action_newer
.int_value
);
1861 abox_add_flag(ABOX(abox
),
1862 _("Brief"), _("Don't list processed files"),
1863 'B', o_action_brief
.int_value
);
1864 abox_add_flag(ABOX(abox
),
1865 _("Recurse"), _("Also change contents of subdirectories"),
1868 gui_side
->default_string
= &last_chmod_string
;
1869 abox_add_combo(ABOX(abox
), presets
, new_entry_string
,
1870 new_help_button(show_chmod_help
, NULL
));
1872 g_signal_connect(ABOX(abox
)->entry
, "changed",
1873 G_CALLBACK(entry_changed
), gui_side
);
1875 g_signal_connect_swapped(gui_side
->entry
, "activate",
1876 G_CALLBACK(gtk_button_clicked
),
1880 number_of_windows
++;
1881 gtk_widget_show(abox
);
1884 null_g_free(&new_entry_string
);
1887 /* If leaf is NULL then the copy has the same name as the original.
1888 * quiet can be -1 for default.
1890 void action_copy(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1896 quiet
= o_action_copy
.int_value
;
1900 action_do_func
= do_copy
;
1902 abox
= abox_new(_("Copy"), quiet
);
1903 gui_side
= start_action(abox
, list_cb
, paths
,
1904 o_action_force
.int_value
,
1905 o_action_brief
.int_value
,
1906 o_action_recurse
.int_value
,
1907 o_action_newer
.int_value
);
1911 abox_add_flag(ABOX(abox
),
1913 _("Only over-write if source is newer than destination."),
1914 'W', o_action_newer
.int_value
);
1916 number_of_windows
++;
1917 gtk_widget_show(abox
);
1920 /* If leaf is NULL then the file is not renamed.
1921 * quiet can be -1 for default.
1923 void action_move(GList
*paths
, const char *dest
, const char *leaf
, int quiet
)
1929 quiet
= o_action_move
.int_value
;
1933 action_do_func
= do_move
;
1935 abox
= abox_new(_("Move"), quiet
);
1936 gui_side
= start_action(abox
, list_cb
, paths
,
1937 o_action_force
.int_value
,
1938 o_action_brief
.int_value
,
1939 o_action_recurse
.int_value
,
1940 o_action_newer
.int_value
);
1944 abox_add_flag(ABOX(abox
),
1946 _("Only over-write if source is newer than destination."),
1947 'W', o_action_newer
.int_value
);
1948 number_of_windows
++;
1949 gtk_widget_show(abox
);
1952 /* If leaf is NULL then the link will have the same name */
1953 /* XXX: No quiet option here? */
1954 void action_link(GList
*paths
, const char *dest
, const char *leaf
)
1961 action_do_func
= do_link
;
1963 abox
= abox_new(_("Link"), o_action_link
.int_value
);
1964 gui_side
= start_action(abox
, list_cb
, paths
,
1965 o_action_force
.int_value
,
1966 o_action_brief
.int_value
,
1967 o_action_recurse
.int_value
,
1968 o_action_newer
.int_value
);
1972 number_of_windows
++;
1973 gtk_widget_show(abox
);
1976 /* Eject these paths */
1977 void action_eject(GList
*paths
)
1982 abox
= abox_new(_("Eject"), 0);
1983 gui_side
= start_action(abox
, eject_cb
, paths
,
1984 o_action_force
.int_value
,
1985 o_action_brief
.int_value
,
1986 o_action_recurse
.int_value
,
1987 o_action_newer
.int_value
);
1991 number_of_windows
++;
1992 gtk_widget_show(abox
);
1995 void action_init(void)
1997 option_add_int(&o_action_copy
, "action_copy", 1);
1998 option_add_int(&o_action_move
, "action_move", 1);
1999 option_add_int(&o_action_link
, "action_link", 1);
2000 option_add_int(&o_action_delete
, "action_delete", 0);
2001 option_add_int(&o_action_mount
, "action_mount", 1);
2002 option_add_int(&o_action_force
, "action_force", FALSE
);
2003 option_add_int(&o_action_brief
, "action_brief", FALSE
);
2004 option_add_int(&o_action_recurse
, "action_recurse", FALSE
);
2005 option_add_int(&o_action_newer
, "action_newer", FALSE
);
2010 /* Check to see if any of the selected items (or their children) are
2011 * on the pinboard or panel. If so, ask for confirmation.
2013 * TRUE if it's OK to lose them.
2015 static gboolean
remove_pinned_ok(GList
*paths
)
2017 GList
*ask
= NULL
, *next
;
2022 for (; paths
; paths
= paths
->next
)
2024 guchar
*path
= (guchar
*) paths
->data
;
2026 if (icons_require(path
))
2028 if (++ask_n
> MAX_ASK
)
2030 ask
= g_list_append(ask
, path
);
2037 if (ask_n
> MAX_ASK
)
2039 message
= g_string_new(_("Deleting items such as "));
2042 else if (ask_n
== 1)
2043 message
= g_string_new(_("Deleting the item "));
2045 message
= g_string_new(_("Deleting the items "));
2048 for (next
= ask
; next
; next
= next
->next
)
2050 guchar
*path
= (guchar
*) next
->data
;
2053 leaf
= strrchr(path
, '/');
2059 g_string_append_c(message
, '`');
2060 g_string_append(message
, leaf
);
2061 g_string_append_c(message
, '\'');
2063 if (i
== ask_n
- 1 && i
> 0)
2064 g_string_append(message
, _(" and "));
2066 g_string_append(message
, ", ");
2072 message
= g_string_append(message
,
2073 _(" will affect some items on the pinboard "
2074 "or panel - really delete it?"));
2077 if (ask_n
> MAX_ASK
)
2078 message
= g_string_append_c(message
, ',');
2079 message
= g_string_append(message
,
2080 _(" will affect some items on the pinboard "
2081 "or panel - really delete them?"));
2084 retval
= confirm(message
->str
, GTK_STOCK_DELETE
, NULL
);
2086 g_string_free(message
, TRUE
);
2091 void set_find_string_colour(GtkWidget
*widget
, const guchar
*string
)
2093 FindCondition
*cond
;
2095 cond
= find_compile(string
);
2096 entry_set_error(widget
, !cond
);
2098 find_condition_free(cond
);