r3901: When using the drag-and-drop menu, give the choice of creating relative or
[rox-filer.git] / ROX-Filer / src / action.c
bloba4ae2a84034f5e4dfd14266e6ed9b509bb102205
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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)
10 * any later version.
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
15 * more details.
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.
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <sys/time.h>
35 #include <utime.h>
36 #include <stdarg.h>
38 #include "global.h"
40 #include "action.h"
41 #include "abox.h"
42 #include "string.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "main.h"
48 #include "options.h"
49 #include "modechange.h"
50 #include "find.h"
51 #include "dir.h"
52 #include "icon.h"
53 #include "mount.h"
54 #include "type.h"
55 #include "xtypes.h"
57 #if defined(HAVE_GETXATTR)
58 # define ATTR_MAN_PAGE N_("See the attr(5) man page for full details.")
59 #elif defined(HAVE_ATTROPEN)
60 # define ATTR_MAN_PAGE N_("See the fsattr(5) man page for full details.")
61 #else
62 # define ATTR_MAN_PAGE N_("You do not appear to have OS support.")
63 #endif
65 /* Parent->Child messages are one character each:
67 * Y/N Yes/No button clicked
68 * F Force deletion of non-writeable items
69 * Q Quiet toggled
70 * E Entry text changed
71 * W neWer toggled
74 typedef struct _GUIside GUIside;
75 typedef void ActionChild(gpointer data);
76 typedef void ForDirCB(const char *path, const char *dest_path);
78 struct _GUIside
80 ABox *abox; /* The action window widget */
82 int from_child; /* File descriptor */
83 FILE *to_child;
84 int input_tag; /* gdk_input_add() */
85 pid_t child; /* Process ID */
86 int errors; /* Number of errors so far */
87 gboolean show_info; /* For Disk Usage */
89 guchar **default_string; /* Changed when the entry changes */
90 void (*entry_string_func)(GtkWidget *widget,
91 const guchar *string);
93 int abort_attempts;
96 /* These don't need to be in a structure because we fork() before
97 * using them again.
99 static gboolean mount_open_dir = FALSE;
100 static int from_parent = 0;
101 static FILE *to_parent = NULL;
102 static gboolean quiet = FALSE;
103 static GString *message = NULL;
104 static const char *action_dest = NULL;
105 static const char *action_leaf = NULL;
106 static void (*action_do_func)(const char *source, const char *dest);
107 static double size_tally; /* For Disk Usage */
108 static unsigned long dir_counter; /* For Disk Usage */
109 static unsigned long file_counter; /* For Disk Usage */
111 static struct mode_change *mode_change = NULL; /* For Permissions */
112 static FindCondition *find_condition = NULL; /* For Find */
113 static MIME_type *type_change = NULL;
115 /* Only used by child */
116 static gboolean o_force = FALSE;
117 static gboolean o_brief = FALSE;
118 static gboolean o_recurse = FALSE;
119 static gboolean o_newer = FALSE;
121 static Option o_action_copy, o_action_move, o_action_link;
122 static Option o_action_delete, o_action_mount;
123 static Option o_action_force, o_action_brief, o_action_recurse;
124 static Option o_action_newer;
126 /* Whenever the text in these boxes is changed we store a copy of the new
127 * string to be used as the default next time.
129 static guchar *last_chmod_string = NULL;
130 static guchar *last_find_string = NULL;
131 static guchar *last_settype_string = NULL;
133 /* Set to one of the above before forking. This may change over a call to
134 * reply(). It is reset to NULL once the text is parsed.
136 static guchar *new_entry_string = NULL;
138 /* Static prototypes */
139 static void send_done(void);
140 static void send_check_path(const gchar *path);
141 static void send_mount_path(const gchar *path);
142 static gboolean printf_send(const char *msg, ...);
143 static gboolean send_msg(void);
144 static gboolean send_error(void);
145 static gboolean send_dir(const char *dir);
146 static gboolean read_exact(int source, char *buffer, ssize_t len);
147 static void do_mount(const guchar *path, gboolean mount);
148 static gboolean printf_reply(int fd, gboolean ignore_quiet,
149 const char *msg, ...);
150 static gboolean remove_pinned_ok(GList *paths);
152 /* SUPPORT */
155 /* This is called whenever the user edits the entry box (if any) - send the
156 * new string.
158 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
160 guchar *text;
162 g_return_if_fail(gui_side->default_string != NULL);
164 text = gtk_editable_get_chars(entry, 0, -1);
166 if (gui_side->entry_string_func)
167 gui_side->entry_string_func(GTK_WIDGET(entry), text);
169 g_free(*(gui_side->default_string));
170 *(gui_side->default_string) = text; /* Gets text's ref */
172 if (!gui_side->to_child)
173 return;
175 fputc('E', gui_side->to_child);
176 fputs(text, gui_side->to_child);
177 fputc('\n', gui_side->to_child);
178 fflush(gui_side->to_child);
181 void show_condition_help(gpointer data)
183 GtkWidget *help;
184 GtkWidget *text;
186 help = gtk_dialog_new_with_buttons(
187 _("Find expression reference"),
188 NULL, 0,
189 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
190 NULL);
191 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
193 text = gtk_label_new(NULL);
194 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
195 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
196 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
197 gtk_label_set_markup(GTK_LABEL(text), _(
198 "<u>Quick Start</u>\n"
199 "Just put the name of the file you're looking for in single quotes:\n"
200 "<b>'index.html'</b> (to find a file called 'index.html')\n"
201 "\n"
202 "<u>Examples</u>\n"
203 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
204 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
205 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
206 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
207 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
208 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
209 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
210 "\n"
211 "<u>Simple Tests</u>\n"
212 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
213 "(types)\n"
214 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
215 "(permissions)\n"
216 "<b>IsEmpty, IsMine</b>\n"
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.\n"
220 "\n"
221 "<u>Comparisons</u>\n"
222 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
223 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
224 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
225 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
226 "(values)\n"
227 "\n"
228 "<u>Specials</u>\n"
229 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
230 "a % in 'command' is replaced with the path of the current file)\n"
231 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
233 g_signal_connect(help, "response",
234 G_CALLBACK(gtk_widget_destroy), NULL);
236 gtk_widget_show_all(help);
239 static void show_chmod_help(gpointer data)
241 GtkWidget *help;
242 GtkWidget *text;
244 help = gtk_dialog_new_with_buttons(
245 _("Change permissions reference"),
246 NULL, 0,
247 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
248 NULL);
249 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
251 text = gtk_label_new(NULL);
252 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
253 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
254 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
255 gtk_label_set_markup(GTK_LABEL(text), _(
256 "Normally, you can just select a command from the menu (click \n"
257 "on the arrow beside the command box). Sometimes, you need more...\n"
258 "\n"
259 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
260 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
261 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
262 "determines whether to\n"
263 "change the permissions for the User (owner), Group or Others.\n"
264 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
265 "exactly the permissions.\n"
266 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
267 "\n"
268 "Bracketed text and spaces are ignored.\n"
269 "\n"
270 "<u>Examples</u>\n"
271 "<b>u+rw</b>: the file owner gains read and write permission\n"
272 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
273 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
274 "write permission\n"
275 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
276 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
277 "executable by anyone become executable by everyone\n"
278 "<b>u+rw, go+r</b>: two commands at once!\n"
279 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
280 "<b>755</b>: set the permissions directly\n"
281 "\n"
282 "See the chmod(1) man page for full details."));
284 g_signal_connect(help, "response",
285 G_CALLBACK(gtk_widget_destroy), NULL);
287 gtk_widget_show_all(help);
291 static void show_settype_help(gpointer data)
293 GtkWidget *help;
294 GtkWidget *text;
296 help = gtk_dialog_new_with_buttons(
297 _("Set type reference"),
298 NULL, 0,
299 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
300 NULL);
301 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
303 text = gtk_label_new(NULL);
304 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
305 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
306 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
307 gtk_label_set_markup(GTK_LABEL(text), _(
308 "Normally ROX-Filer determines the type of a regular file\n"
309 "by matching it's name against a pattern. To change the\n"
310 "type of the file you must rename it.\n"
311 "\n"
312 "Newer file systems can support something called 'Extended\n"
313 "Attributes' which can be used to store additional data with\n"
314 "each file as named parameters. ROX-Filer uses the\n"
315 "'user.mime_type' attribute to store file types.\n"
316 "\n"
317 "File types are only supported for regular files, not\n"
318 "directories, devices, pipes or sockets, and then only\n"
319 "on certain file systems and where the OS implements them.\n"));
321 text = gtk_label_new(_(ATTR_MAN_PAGE));
322 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
323 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
325 g_signal_connect(help, "response",
326 G_CALLBACK(gtk_widget_destroy), NULL);
328 gtk_widget_show_all(help);
331 static void process_message(GUIside *gui_side, const gchar *buffer)
333 ABox *abox = gui_side->abox;
335 if (*buffer == '?')
336 abox_ask(abox, buffer + 1);
337 else if (*buffer == 's')
338 dir_check_this(buffer + 1); /* Update this item */
339 else if (*buffer == '=')
340 abox_add_filename(abox, buffer + 1);
341 else if (*buffer == '#')
342 abox_clear_results(abox);
343 else if (*buffer == 'X')
345 filer_close_recursive(buffer + 1);
346 /* Let child know it's safe to continue... */
347 fputc('X', gui_side->to_child);
348 fflush(gui_side->to_child);
350 else if (*buffer == 'm' || *buffer == 'M')
352 /* Mount / major changes to this path */
353 if (*buffer == 'M')
355 mount_update(TRUE);
356 mount_user_mount(buffer + 1);
358 filer_check_mounted(buffer + 1);
360 else if (*buffer == '/')
361 abox_set_current_object(abox, buffer + 1);
362 else if (*buffer == 'o')
363 filer_opendir(buffer + 1, NULL, NULL);
364 else if (*buffer == '!')
366 gui_side->errors++;
367 abox_log(abox, buffer + 1, "error");
369 else if (*buffer == '<')
370 abox_set_file(abox, 0, buffer+1);
371 else if (*buffer == '>')
373 abox_set_file(abox, 1, buffer+1);
374 abox_show_compare(abox, TRUE);
376 else if (*buffer == '%')
378 abox_set_percentage(abox, atoi(buffer+1));
380 else
381 abox_log(abox, buffer + 1, NULL);
384 /* Called when the child sends us a message */
385 static void message_from_child(gpointer data,
386 gint source,
387 GdkInputCondition condition)
389 char buf[5];
390 GUIside *gui_side = (GUIside *) data;
391 ABox *abox = gui_side->abox;
392 GtkTextBuffer *text_buffer;
394 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
396 if (read_exact(source, buf, 4))
398 ssize_t message_len;
399 char *buffer;
401 buf[4] = '\0';
402 message_len = strtol(buf, NULL, 16);
403 buffer = g_malloc(message_len + 1);
404 if (message_len > 0 && read_exact(source, buffer, message_len))
406 buffer[message_len] = '\0';
407 process_message(gui_side, buffer);
408 g_free(buffer);
409 return;
411 g_printerr("Child died in the middle of a message.\n");
414 if (gui_side->abort_attempts)
415 abox_log(abox, _("\nProcess terminated.\n"), "error");
417 /* The child is dead */
418 gui_side->child = 0;
420 fclose(gui_side->to_child);
421 gui_side->to_child = NULL;
422 close(gui_side->from_child);
423 g_source_remove(gui_side->input_tag);
424 abox_cancel_ask(gui_side->abox);
426 if (gui_side->errors)
428 guchar *report;
430 if (gui_side->errors == 1)
431 report = g_strdup(_("There was one error.\n"));
432 else
433 report = g_strdup_printf(_("There were %d errors.\n"),
434 gui_side->errors);
436 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
438 g_free(report);
440 else if (gui_side->show_info == FALSE)
441 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
444 /* Scans src_dir, calling cb(item, dest_path) for each item */
445 static void for_dir_contents(ForDirCB *cb,
446 const char *src_dir,
447 const char *dest_path)
449 DIR *d;
450 struct dirent *ent;
451 GList *list = NULL, *next;
453 d = mc_opendir(src_dir);
454 if (!d)
456 /* Message displayed is "ERROR reading 'path': message" */
457 printf_send("!%s '%s': %s\n", _("ERROR reading"),
458 src_dir, g_strerror(errno));
459 return;
462 send_dir(src_dir);
464 while ((ent = mc_readdir(d)))
466 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
467 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
468 continue;
469 list = g_list_prepend(list, g_strdup(make_path(src_dir,
470 ent->d_name)));
472 mc_closedir(d);
474 for (next = list; next; next = next->next)
476 cb((char *) next->data, dest_path);
478 g_free(next->data);
480 g_list_free(list);
483 /* Read this many bytes into the buffer. TRUE on success. */
484 static gboolean read_exact(int source, char *buffer, ssize_t len)
486 while (len > 0)
488 ssize_t got;
489 got = read(source, buffer, len);
490 if (got < 1)
491 return FALSE;
492 len -= got;
493 buffer += got;
495 return TRUE;
498 static void send_done(void)
500 printf_send(_("'\nDone\n"));
503 /* Notify the filer that this item has been updated */
504 static void send_check_path(const gchar *path)
506 printf_send("s%s", path);
509 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
510 static void send_mount_path(const gchar *path)
512 printf_send("m%s", path);
515 /* Send a message to the filer process. The first character indicates the
516 * type of the message.
518 static gboolean printf_send(const char *msg, ...)
520 va_list args;
521 gchar *tmp;
523 va_start(args, msg);
524 tmp = g_strdup_vprintf(msg, args);
525 va_end(args);
527 g_string_assign(message, tmp);
528 g_free(tmp);
530 return send_msg();
533 /* Send 'message' to our parent process. TRUE on success. */
534 static gboolean send_msg(void)
536 char len_buffer[5];
537 ssize_t len;
539 g_return_val_if_fail(message->len < 0xffff, FALSE);
541 sprintf(len_buffer, "%04x", message->len);
542 fwrite(len_buffer, 1, 4, to_parent);
543 len = fwrite(message->str, 1, message->len, to_parent);
544 fflush(to_parent);
545 return len == (ssize_t) message->len;
548 /* Set the directory indicator at the top of the window */
549 static gboolean send_dir(const char *dir)
551 return printf_send("/%s", dir);
554 static gboolean send_error(void)
556 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
559 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
561 gchar code;
563 if (!gui_side->to_child)
564 return;
566 if (response == GTK_RESPONSE_YES)
567 code = 'Y';
568 else if (response == GTK_RESPONSE_NO)
569 code = 'N';
570 else
571 return;
573 fputc(code, gui_side->to_child);
574 fflush(gui_side->to_child);
575 abox_show_compare(gui_side->abox, FALSE);
578 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
580 if (!gui_side->to_child)
581 return;
583 fputc(flag, gui_side->to_child);
584 fflush(gui_side->to_child);
587 static void read_new_entry_text(void)
589 int len;
590 char c;
591 GString *new;
593 new = g_string_new(NULL);
595 for (;;)
597 len = read(from_parent, &c, 1);
598 if (len != 1)
600 fprintf(stderr, "read() error: %s\n",
601 g_strerror(errno));
602 _exit(1); /* Parent died? */
605 if (c == '\n')
606 break;
607 g_string_append_c(new, c);
610 g_free(new_entry_string);
611 new_entry_string = new->str;
612 g_string_free(new, FALSE);
615 static void process_flag(char flag)
617 switch (flag)
619 case 'Q':
620 quiet = !quiet;
621 break;
622 case 'F':
623 o_force = !o_force;
624 break;
625 case 'R':
626 o_recurse = !o_recurse;
627 break;
628 case 'B':
629 o_brief = !o_brief;
630 break;
631 case 'W':
632 o_newer = !o_newer;
633 break;
634 case 'E':
635 read_new_entry_text();
636 break;
637 default:
638 printf_send("!ERROR: Bad message '%c'\n", flag);
639 break;
643 /* If the parent has sent any flag toggles, read them */
644 static void check_flags(void)
646 fd_set set;
647 int got;
648 char retval;
649 struct timeval tv;
651 FD_ZERO(&set);
653 while (1)
655 FD_SET(from_parent, &set);
656 tv.tv_sec = 0;
657 tv.tv_usec = 0;
658 got = select(from_parent + 1, &set, NULL, NULL, &tv);
660 if (got == -1)
661 g_error("select() failed: %s\n", g_strerror(errno));
662 else if (!got)
663 return;
665 got = read(from_parent, &retval, 1);
666 if (got != 1)
667 g_error("read() error: %s\n", g_strerror(errno));
669 process_flag(retval);
673 /* Read until the user sends a reply. If ignore_quiet is TRUE then
674 * the user MUST click Yes or No, else treat quiet on as Yes.
675 * If the user needs prompting then does send_msg().
677 static gboolean printf_reply(int fd, gboolean ignore_quiet,
678 const char *msg, ...)
680 ssize_t len;
681 char retval;
682 va_list args;
683 gchar *tmp;
685 if (quiet && !ignore_quiet)
686 return TRUE;
688 va_start(args, msg);
689 tmp = g_strdup_vprintf(msg, args);
690 va_end(args);
692 g_string_assign(message, tmp);
693 g_free(tmp);
695 send_msg();
697 while (1)
699 len = read(fd, &retval, 1);
700 if (len != 1)
702 fprintf(stderr, "read() error: %s\n",
703 g_strerror(errno));
704 _exit(1); /* Parent died? */
707 switch (retval)
709 case 'Y':
710 printf_send("' %s\n", _("Yes"));
711 return TRUE;
712 case 'N':
713 printf_send("' %s\n", _("No"));
714 return FALSE;
715 default:
716 process_flag(retval);
717 break;
722 static void abort_operation(GtkWidget *widget, gpointer data)
724 GUIside *gui_side = (GUIside *) data;
726 if (gui_side->child)
728 if (gui_side->abort_attempts == 0)
730 abox_log(ABOX(widget),
731 _("\nAsking child process to terminate...\n"),
732 "error");
733 kill(-gui_side->child, SIGTERM);
735 else
737 abox_log(ABOX(widget),
738 _("\nTrying to KILL run-away process...\n"),
739 "error");
740 kill(-gui_side->child, SIGKILL);
741 kill(-gui_side->child, SIGCONT);
743 gui_side->abort_attempts++;
745 else
746 gtk_widget_destroy(widget);
749 static void destroy_action_window(GtkWidget *widget, gpointer data)
751 GUIside *gui_side = (GUIside *) data;
753 if (gui_side->child)
755 kill(-gui_side->child, SIGTERM);
756 fclose(gui_side->to_child);
757 close(gui_side->from_child);
758 g_source_remove(gui_side->input_tag);
761 g_free(gui_side);
763 one_less_window();
766 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
767 * (NULL on failure). The child calls func().
769 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
770 int force, int brief, int recurse, int newer)
772 gboolean autoq;
773 int filedes[4]; /* 0 and 2 are for reading */
774 GUIside *gui_side;
775 pid_t child;
776 struct sigaction act;
778 if (pipe(filedes))
780 report_error("pipe: %s", g_strerror(errno));
781 gtk_widget_destroy(abox);
782 return NULL;
785 if (pipe(filedes + 2))
787 close(filedes[0]);
788 close(filedes[1]);
789 report_error("pipe: %s", g_strerror(errno));
790 gtk_widget_destroy(abox);
791 return NULL;
794 autoq = gtk_toggle_button_get_active(
795 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
797 o_force = force;
798 o_brief = brief;
799 o_recurse = recurse;
800 o_newer = newer;
802 child = fork();
803 switch (child)
805 case -1:
806 report_error("fork: %s", g_strerror(errno));
807 gtk_widget_destroy(abox);
808 return NULL;
809 case 0:
810 /* We are the child */
812 /* Create a new process group */
813 setpgid(0, 0);
815 quiet = autoq;
817 dir_drop_all_dnotifies();
819 /* Reset the SIGCHLD handler */
820 act.sa_handler = SIG_DFL;
821 sigemptyset(&act.sa_mask);
822 act.sa_flags = 0;
823 sigaction(SIGCHLD, &act, NULL);
825 message = g_string_new(NULL);
826 close(filedes[0]);
827 close(filedes[3]);
828 to_parent = fdopen(filedes[1], "wb");
829 from_parent = filedes[2];
830 func(data);
831 send_dir("");
832 _exit(0);
835 /* We are the parent */
836 close(filedes[1]);
837 close(filedes[2]);
838 gui_side = g_new(GUIside, 1);
839 gui_side->from_child = filedes[0];
840 gui_side->to_child = fdopen(filedes[3], "wb");
841 gui_side->child = child;
842 gui_side->errors = 0;
843 gui_side->show_info = FALSE;
844 gui_side->default_string = NULL;
845 gui_side->entry_string_func = NULL;
846 gui_side->abort_attempts = 0;
848 gui_side->abox = ABOX(abox);
849 g_signal_connect(abox, "destroy",
850 G_CALLBACK(destroy_action_window), gui_side);
852 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
853 g_signal_connect(abox, "flag_toggled",
854 G_CALLBACK(flag_toggled), gui_side);
855 g_signal_connect(abox, "abort_operation",
856 G_CALLBACK(abort_operation), gui_side);
858 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
859 GDK_INPUT_READ,
860 message_from_child,
861 gui_side, NULL);
863 return gui_side;
866 /* ACTIONS ON ONE ITEM */
868 /* These may call themselves recursively, or ask questions, etc */
870 /* Updates the global size_tally, file_counter and dir_counter */
871 static void do_usage(const char *src_path, const char *unused)
873 struct stat info;
875 check_flags();
877 if (mc_lstat(src_path, &info))
879 printf_send("'%s:\n", src_path);
880 send_error();
882 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
884 file_counter++;
885 size_tally += info.st_size;
887 else if (S_ISDIR(info.st_mode))
889 dir_counter++;
890 if (printf_reply(from_parent, FALSE,
891 _("?Count contents of %s?"), src_path))
893 char *safe_path;
894 safe_path = g_strdup(src_path);
895 for_dir_contents(do_usage, safe_path, safe_path);
896 g_free(safe_path);
899 else
900 file_counter++;
903 /* dest_path is the dir containing src_path */
904 static void do_delete(const char *src_path, const char *unused)
906 struct stat info;
907 gboolean write_prot;
908 char *safe_path;
910 check_flags();
912 if (mc_lstat(src_path, &info))
914 send_error();
915 return;
918 write_prot = S_ISLNK(info.st_mode) ? FALSE
919 : access(src_path, W_OK) != 0;
920 if (write_prot || !quiet)
922 int res;
924 printf_send("<%s", src_path);
925 printf_send(">");
926 res=printf_reply(from_parent, write_prot && !o_force,
927 _("?Delete %s'%s'?"),
928 write_prot ? _("WRITE-PROTECTED ") : "",
929 src_path);
930 printf_send("<");
931 if (!res)
932 return;
934 else if (!o_brief)
935 printf_send(_("'Deleting '%s'\n"), src_path);
937 safe_path = g_strdup(src_path);
939 if (S_ISDIR(info.st_mode))
941 for_dir_contents(do_delete, safe_path, safe_path);
942 if (rmdir(safe_path))
944 g_free(safe_path);
945 send_error();
946 return;
948 printf_send(_("'Directory '%s' deleted\n"), safe_path);
949 send_mount_path(safe_path);
951 else if (unlink(src_path))
952 send_error();
953 else
955 send_check_path(safe_path);
956 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
958 gchar *dir;
959 dir = g_path_get_dirname(safe_path);
960 send_check_path(dir);
961 g_free(dir);
965 g_free(safe_path);
968 static void do_eject(const char *path)
970 const char *argv[3] = {NULL, NULL, NULL};
971 char *err;
973 check_flags();
975 if (!quiet)
977 int res;
978 printf_send("<%s", path);
979 printf_send(">");
980 res=printf_reply(from_parent, !o_force,
981 _("?Eject '%s'?"),
982 path);
983 printf_send("<");
984 if (!res)
985 return;
987 else if (!o_brief)
988 printf_send(_("'Eject '%s'\n"), path);
990 /* Need to close all sub-directories now, or we
991 * can't unmount if dnotify is used.
994 char c = '?';
995 printf_send("X%s", path);
996 /* Wait until it's safe... */
997 read(from_parent, &c, 1);
998 g_return_if_fail(c == 'X');
1001 argv[0] = "eject";
1002 argv[1] = path;
1003 argv[2] = NULL;
1004 err = fork_exec_wait(argv);
1005 if (err)
1007 printf_send(_("!%s\neject failed\n"), err);
1008 g_free(err);
1011 printf_send("M%s", path);
1015 /* path is the item to check. If is is a directory then we may recurse
1016 * (unless prune is used).
1018 static void do_find(const char *path, const char *unused)
1020 FindInfo info;
1022 check_flags();
1024 if (!quiet)
1026 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1027 return;
1030 for (;;)
1032 if (new_entry_string)
1034 find_condition_free(find_condition);
1035 find_condition = find_compile(new_entry_string);
1036 null_g_free(&new_entry_string);
1039 if (find_condition)
1040 break;
1042 printf_send(_("!Invalid find condition - "
1043 "change it and try again\n"));
1044 if (!printf_reply(from_parent, TRUE,
1045 _("?Check '%s'?"), path))
1046 return;
1049 if (mc_lstat(path, &info.stats))
1051 send_error();
1052 printf_send(_("'(while checking '%s')\n"), path);
1053 return;
1056 info.fullpath = path;
1057 time(&info.now); /* XXX: Not for each check! */
1059 info.leaf = g_basename(path);
1060 info.prune = FALSE;
1061 if (find_test_condition(find_condition, &info))
1062 printf_send("=%s", path);
1064 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1066 char *safe_path;
1067 safe_path = g_strdup(path);
1068 for_dir_contents(do_find, safe_path, safe_path);
1069 g_free(safe_path);
1073 /* Like mode_compile(), but ignores spaces and bracketed bits */
1074 static struct mode_change *nice_mode_compile(const char *mode_string,
1075 unsigned int masked_ops)
1077 GString *new;
1078 int brackets = 0;
1079 struct mode_change *retval = NULL;
1081 new = g_string_new(NULL);
1083 for (; *mode_string; mode_string++)
1085 if (*mode_string == '(')
1086 brackets++;
1087 if (*mode_string == ')')
1089 brackets--;
1090 if (brackets < 0)
1091 break;
1092 continue;
1095 if (brackets == 0 && *mode_string != ' ')
1096 g_string_append_c(new, *mode_string);
1099 if (brackets == 0)
1100 retval = mode_compile(new->str, masked_ops);
1101 g_string_free(new, TRUE);
1102 return retval;
1105 static void do_chmod(const char *path, const char *unused)
1107 struct stat info;
1108 mode_t new_mode;
1110 check_flags();
1112 if (mc_lstat(path, &info))
1114 send_error();
1115 return;
1117 if (S_ISLNK(info.st_mode))
1118 return;
1120 if (!quiet)
1122 int res;
1123 printf_send("<%s", path);
1124 printf_send(">");
1125 res=printf_reply(from_parent, FALSE,
1126 _("?Change permissions of '%s'?"), path);
1127 printf_send("<");
1128 if (!res)
1129 return;
1131 else if (!o_brief)
1132 printf_send(_("'Changing permissions of '%s'\n"), path);
1134 for (;;)
1136 if (new_entry_string)
1138 if (mode_change)
1139 mode_free(mode_change);
1140 mode_change = nice_mode_compile(new_entry_string,
1141 MODE_MASK_ALL);
1142 null_g_free(&new_entry_string);
1145 if (mode_change)
1146 break;
1148 printf_send(
1149 _("!Invalid mode command - change it and try again\n"));
1150 if (!printf_reply(from_parent, TRUE,
1151 _("?Change permissions of '%s'?"), path))
1152 return;
1155 if (mc_lstat(path, &info))
1157 send_error();
1158 return;
1160 if (S_ISLNK(info.st_mode))
1161 return;
1163 new_mode = mode_adjust(info.st_mode, mode_change);
1164 if (chmod(path, new_mode))
1166 send_error();
1167 return;
1170 send_check_path(path);
1172 if (S_ISDIR(info.st_mode))
1174 send_mount_path(path);
1176 if (o_recurse)
1178 guchar *safe_path;
1179 safe_path = g_strdup(path);
1180 for_dir_contents(do_chmod, safe_path, safe_path);
1181 g_free(safe_path);
1186 static void do_settype(const char *path, const char *unused)
1188 struct stat info;
1190 check_flags();
1192 if (mc_lstat(path, &info))
1194 send_error();
1195 return;
1197 if (S_ISLNK(info.st_mode))
1198 return;
1200 if (!quiet)
1202 int res;
1203 printf_send("<%s", path);
1204 printf_send(">");
1205 res=printf_reply(from_parent, FALSE,
1206 _("?Change type of '%s'?"), path);
1207 printf_send("<");
1208 if (!res)
1209 return;
1212 for (;;)
1214 if (new_entry_string)
1216 type_change = mime_type_lookup(new_entry_string);
1217 null_g_free(&new_entry_string);
1220 if (type_change)
1221 break;
1223 printf_send(_("!Invalid type - "
1224 "change it and try again\n"));
1225 if (!printf_reply(from_parent, TRUE,
1226 _("?Change type of '%s'?"), path))
1227 return;
1230 if (mc_lstat(path, &info))
1232 send_error();
1233 return;
1235 if (S_ISLNK(info.st_mode))
1236 return;
1238 if (S_ISREG(info.st_mode))
1240 if (!o_brief)
1242 const char *comment;
1244 comment = mime_type_comment(type_change);
1245 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1246 comment);
1249 if (xtype_set(path, type_change))
1251 send_error();
1252 return;
1255 send_check_path(path);
1257 else if (S_ISDIR(info.st_mode))
1259 if (o_recurse)
1261 guchar *safe_path;
1262 safe_path = g_strdup(path);
1263 for_dir_contents(do_settype, safe_path, unused);
1264 g_free(safe_path);
1269 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1270 * is set then that is the new leafname, otherwise the leafname stays
1271 * the same.
1273 static const char *make_dest_path(const char *object, const char *dir)
1275 const char *leaf;
1277 if (action_leaf)
1278 leaf = action_leaf;
1279 else
1281 leaf = strrchr(object, '/');
1282 if (!leaf)
1283 leaf = object; /* Error? */
1284 else
1285 leaf++;
1288 return make_path(dir, leaf);
1291 /* If action_leaf is not NULL it specifies the new leaf name */
1292 static void do_copy2(const char *path, const char *dest)
1294 const char *dest_path;
1295 struct stat info;
1296 struct stat dest_info;
1298 check_flags();
1300 dest_path = make_dest_path(path, dest);
1302 if (mc_lstat(path, &info))
1304 send_error();
1305 return;
1308 if (mc_lstat(dest_path, &dest_info) == 0)
1310 int err;
1311 gboolean merge;
1313 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1315 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1317 /* Newer; keep going */
1319 else
1321 printf_send("<%s", path);
1322 printf_send(">%s", dest_path);
1323 if (!printf_reply(from_parent, TRUE,
1324 _("?'%s' already exists - %s?"),
1325 dest_path,
1326 merge ? _("merge contents")
1327 : _("overwrite")))
1328 return;
1331 if (!merge)
1333 if (S_ISDIR(dest_info.st_mode))
1334 err = rmdir(dest_path);
1335 else
1336 err = unlink(dest_path);
1338 if (err)
1340 send_error();
1341 if (errno != ENOENT)
1342 return;
1343 printf_send(_("'Trying copy anyway...\n"));
1347 else if (!quiet)
1349 printf_send("<%s", path);
1350 printf_send(">");
1351 if (!printf_reply(from_parent, FALSE,
1352 _("?Copy %s as %s?"), path, dest_path))
1353 return;
1355 else if (!o_brief || S_ISDIR(info.st_mode))
1356 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1358 if (S_ISDIR(info.st_mode))
1360 mode_t mode = info.st_mode;
1361 char *safe_path, *safe_dest;
1362 struct stat dest_info;
1363 gboolean exists;
1365 safe_path = g_strdup(path);
1366 safe_dest = g_strdup(dest_path);
1368 exists = !mc_lstat(dest_path, &dest_info);
1370 if (exists && !S_ISDIR(dest_info.st_mode))
1371 printf_send(_("!ERROR: Destination already exists, "
1372 "but is not a directory\n"));
1373 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1374 send_error();
1375 else
1377 if (!exists)
1378 /* (just been created then) */
1379 send_check_path(dest_path);
1381 action_leaf = NULL;
1382 for_dir_contents(do_copy2, safe_path, safe_dest);
1383 /* Note: dest_path now invalid... */
1385 if (!exists)
1387 struct utimbuf utb;
1389 /* We may have created the directory with
1390 * more permissions than the source so that
1391 * we could write to it... change it back now.
1393 if (chmod(safe_dest, mode))
1395 /* Some filesystems don't support
1396 * SetGID and SetUID bits. Ignore
1397 * these errors.
1399 if (errno != EPERM)
1400 send_error();
1403 /* Also, try to preserve the timestamps */
1404 utb.actime = info.st_atime;
1405 utb.modtime = info.st_mtime;
1407 utime(safe_dest, &utb);
1411 g_free(safe_path);
1412 g_free(safe_dest);
1414 else if (S_ISLNK(info.st_mode))
1416 char *target;
1418 /* Not all versions of cp(1) can make symlinks,
1419 * so we special-case it.
1422 target = readlink_dup(path);
1423 if (target)
1425 if (symlink(target, dest_path))
1426 send_error();
1427 else
1428 send_check_path(dest_path);
1430 g_free(target);
1432 else
1433 send_error();
1435 else
1437 guchar *error;
1439 error = copy_file(path, dest_path);
1441 if (error)
1443 printf_send(_("!%s\nFailed to copy '%s'\n"),
1444 error, path);
1445 g_free(error);
1447 else
1448 send_check_path(dest_path);
1452 /* If action_leaf is not NULL it specifies the new leaf name */
1453 static void do_move2(const char *path, const char *dest)
1455 const char *dest_path;
1456 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1457 struct stat info2;
1458 gboolean is_dir;
1459 char *err;
1461 check_flags();
1463 dest_path = make_dest_path(path, dest);
1465 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1467 if (access(dest_path, F_OK) == 0)
1469 struct stat info;
1470 int err;
1472 if (mc_lstat(dest_path, &info))
1474 send_error();
1475 return;
1478 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1480 /* Newer; keep going */
1482 else
1484 printf_send("<%s", path);
1485 printf_send(">%s", dest_path);
1486 if (!printf_reply(from_parent, TRUE,
1487 _("?'%s' already exists - overwrite?"),
1488 dest_path))
1489 return;
1492 if (S_ISDIR(info.st_mode))
1493 err = rmdir(dest_path);
1494 else
1495 err = unlink(dest_path);
1497 if (err)
1499 send_error();
1500 if (errno != ENOENT)
1501 return;
1502 printf_send(_("'Trying move anyway...\n"));
1505 else if (!quiet)
1507 printf_send("<%s", path);
1508 printf_send(">");
1509 if (!printf_reply(from_parent, FALSE,
1510 _("?Move %s as %s?"), path, dest_path))
1511 return;
1513 else if (!o_brief)
1514 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1516 argv[2] = path;
1517 argv[3] = dest_path;
1519 err = fork_exec_wait(argv);
1520 if (err)
1522 printf_send(_("!%s\nFailed to move %s as %s\n"),
1523 err, path, dest_path);
1524 g_free(err);
1526 else
1528 send_check_path(dest_path);
1530 if (is_dir)
1531 send_mount_path(path);
1532 else
1533 send_check_path(path);
1537 /* Copy path to dest.
1538 * Check that path not copied into itself.
1540 static void do_copy(const char *path, const char *dest)
1542 if (is_sub_dir(make_dest_path(path, dest), path))
1543 printf_send(_("!ERROR: Can't copy object into itself\n"));
1544 else
1546 do_copy2(path, dest);
1547 send_check_path(dest);
1551 /* Move path to dest.
1552 * Check that path not moved into itself.
1554 static void do_move(const char *path, const char *dest)
1556 if (is_sub_dir(make_dest_path(path, dest), path))
1557 printf_send(
1558 _("!ERROR: Can't move/rename object into itself\n"));
1559 else
1561 do_move2(path, dest);
1562 send_check_path(dest);
1566 /* Common code for do_link_relative() and do_link_absolute(). */
1567 static void do_link(const char *path, const char *dest_path)
1569 if (quiet)
1570 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1571 else {
1572 printf_send("<%s", path);
1573 printf_send(">");
1574 if (!printf_reply(from_parent, FALSE,
1575 _("?Link %s as %s?"), path, dest_path))
1576 return;
1579 if (symlink(path, dest_path))
1580 send_error();
1581 else
1582 send_check_path(dest_path);
1585 static void do_link_relative(const char *path, const char *dest)
1587 char *rel_path;
1588 const char *dest_path;
1590 dest_path = make_dest_path(path, dest);
1592 check_flags();
1594 rel_path = get_relative_path(dest_path, path);
1595 do_link(rel_path, dest_path);
1596 g_free(rel_path);
1599 static void do_link_absolute(const char *path, const char *dest)
1601 check_flags();
1602 do_link(path, make_dest_path(path, dest));
1605 /* Mount/umount this item (depending on 'mount') */
1606 static void do_mount(const guchar *path, gboolean mount)
1608 const char *argv[3] = {NULL, NULL, NULL};
1609 char *err;
1611 check_flags();
1613 argv[0] = mount ? "mount" : "umount";
1614 argv[1] = path;
1616 if (quiet)
1617 printf_send(mount ? _("'Mounting %s\n")
1618 : _("'Unmounting %s\n"),
1619 path);
1620 else if (!printf_reply(from_parent, FALSE,
1621 mount ? _("?Mount %s?")
1622 : _("?Unmount %s?"),
1623 path))
1624 return;
1626 if (!mount)
1628 char c = '?';
1629 /* Need to close all sub-directories now, or we
1630 * can't unmount if dnotify is used.
1632 printf_send("X%s", path);
1633 /* Wait until it's safe... */
1634 read(from_parent, &c, 1);
1635 g_return_if_fail(c == 'X');
1638 err = fork_exec_wait(argv);
1639 if (err)
1641 printf_send(mount ?
1642 _("!%s\nMount failed\n") :
1643 _("!%s\nUnmount failed\n"), err);
1644 g_free(err);
1646 /* Mount may have worked even on error, eg if we try to mount
1647 * a read-only disk read/write, it gets mounted read-only
1648 * with an error.
1650 if (mount && mount_is_mounted(path, NULL, NULL))
1651 printf_send(_("'(seems to be mounted now anyway)\n"));
1652 else
1653 return;
1656 printf_send("M%s", path);
1657 if (mount && mount_open_dir)
1658 printf_send("o%s", path);
1661 /* CHILD MAIN LOOPS */
1663 /* After forking, the child calls one of these functions */
1665 /* We use a double for total size in order to count beyond 4Gb */
1666 static void usage_cb(gpointer data)
1668 GList *paths = (GList *) data;
1669 double total_size = 0;
1670 int n, i, per;
1672 n=g_list_length(paths);
1673 dir_counter = file_counter = 0;
1675 for (i=0; paths; paths = paths->next, i++)
1677 guchar *path = (guchar *) paths->data;
1679 send_dir(path);
1681 size_tally = 0;
1683 if(n>1 && i>0)
1685 per=100*i/n;
1686 printf_send("%%%d", per);
1688 do_usage(path, NULL);
1690 printf_send("'%s: %s\n",
1691 g_basename(path),
1692 format_double_size(size_tally));
1693 total_size += size_tally;
1695 printf_send("%%-1");
1697 g_string_printf(message, _("'\nTotal: %s ("),
1698 format_double_size(total_size));
1700 if (file_counter)
1701 g_string_append_printf(message,
1702 "%ld %s%s", file_counter,
1703 file_counter == 1 ? _("file") : _("files"),
1704 dir_counter ? ", " : ")\n");
1706 if (file_counter == 0 && dir_counter == 0)
1707 g_string_append(message, _("no directories)\n"));
1708 else if (dir_counter)
1709 g_string_append_printf(message,
1710 "%ld %s)\n", dir_counter,
1711 dir_counter == 1 ? _("directory")
1712 : _("directories"));
1714 send_msg();
1717 #ifdef DO_MOUNT_POINTS
1718 static void mount_cb(gpointer data)
1720 GList *paths = (GList *) data;
1721 gboolean mount_points = FALSE;
1722 int n, i, per;
1724 n=g_list_length(paths);
1725 for (i=0; paths; paths = paths->next, i++)
1727 guchar *path = (guchar *) paths->data;
1728 guchar *target;
1730 target = pathdup(path);
1731 if (!target)
1732 target = path;
1734 if(n>1 && i>0)
1736 per=100*i/n;
1737 printf_send("%%%d", per);
1739 if (mount_is_mounted(target, NULL, NULL))
1741 mount_points = TRUE;
1742 do_mount(target, FALSE); /* Unmount */
1744 else if (g_hash_table_lookup(fstab_mounts, target))
1746 mount_points = TRUE;
1747 do_mount(target, TRUE); /* Mount */
1750 if (target != path)
1751 g_free(target);
1754 if (mount_points)
1755 send_done();
1756 else
1757 printf_send(_("!No mount points selected!\n"));
1759 #endif
1761 /* (use g_dirname() instead?) */
1762 static guchar *dirname(guchar *path)
1764 guchar *slash;
1766 slash = strrchr(path, '/');
1767 g_return_val_if_fail(slash != NULL, g_strdup(path));
1769 if (slash != path)
1770 return g_strndup(path, slash - path);
1771 return g_strdup("/");
1774 static void delete_cb(gpointer data)
1776 GList *paths = (GList *) data;
1777 int n, i, per;
1779 n=g_list_length(paths);
1780 for (i=0; paths; paths = paths->next, i++)
1782 guchar *path = (guchar *) paths->data;
1783 guchar *dir;
1785 dir = dirname(path);
1786 send_dir(dir);
1788 if(n>1 && i>0)
1790 per=100*i/n;
1791 printf_send("%%%d", per);
1793 do_delete(path, dir);
1795 g_free(dir);
1798 send_done();
1801 static void eject_cb(gpointer data)
1803 GList *paths = (GList *) data;
1804 int n, i, per;
1806 n=g_list_length(paths);
1808 for (i=0; paths; paths = paths->next, i++)
1810 guchar *path = (guchar *) paths->data;
1812 if(n>1 && i>0)
1814 per=100*i/n;
1815 printf_send("%%%d", per);
1817 send_dir(path);
1819 do_eject(path);
1822 send_done();
1825 static void find_cb(gpointer data)
1827 GList *all_paths = (GList *) data;
1828 GList *paths;
1830 while (1)
1832 for (paths = all_paths; paths; paths = paths->next)
1834 guchar *path = (guchar *) paths->data;
1836 send_dir(path);
1838 do_find(path, NULL);
1841 if (!printf_reply(from_parent, TRUE,
1842 _("?Another search?")))
1843 break;
1844 printf_send("#");
1847 send_done();
1850 static void chmod_cb(gpointer data)
1852 GList *paths = (GList *) data;
1853 int n, i, per;
1855 n=g_list_length(paths);
1857 for (i=0; paths; paths = paths->next, i++)
1859 guchar *path = (guchar *) paths->data;
1860 struct stat info;
1862 if(n>1 && i>0)
1864 per=100*i/n;
1865 printf_send("%%%d", per);
1867 send_dir(path);
1869 if (mc_stat(path, &info) != 0)
1870 send_error();
1871 else if (S_ISLNK(info.st_mode))
1872 printf_send(_("!'%s' is a symbolic link\n"),
1873 g_basename(path));
1874 else
1875 do_chmod(path, NULL);
1878 send_done();
1881 static void settype_cb(gpointer data)
1883 GList *paths = (GList *) data;
1884 int n, i, per;
1886 n=g_list_length(paths);
1888 for (i=0; paths; paths = paths->next, i++)
1890 guchar *path = (guchar *) paths->data;
1891 struct stat info;
1893 if(n>1 && i>0)
1895 per=100*i/n;
1896 printf_send("%%%d", per);
1898 send_dir(path);
1900 if (mc_stat(path, &info) != 0)
1901 send_error();
1902 else if (S_ISLNK(info.st_mode))
1903 printf_send(_("!'%s' is a symbolic link\n"),
1904 g_basename(path));
1905 else
1906 do_settype(path, NULL);
1909 send_done();
1912 static void list_cb(gpointer data)
1914 GList *paths = (GList *) data;
1915 int n, i, per;
1917 n=g_list_length(paths);
1919 for (i=0; paths; paths = paths->next, i++)
1921 if(n>1 && i>0)
1923 per=100*i/n;
1924 printf_send("%%%d", per);
1926 send_dir((char *) paths->data);
1928 action_do_func((char *) paths->data, action_dest);
1931 send_done();
1934 /* EXTERNAL INTERFACE */
1936 void action_find(GList *paths)
1938 GUIside *gui_side;
1939 GtkWidget *abox;
1941 if (!paths)
1943 report_error(_("You need to select some items "
1944 "to search through"));
1945 return;
1948 if (!last_find_string)
1949 last_find_string = g_strdup("'core'");
1951 new_entry_string = last_find_string;
1953 abox = abox_new(_("Find"), FALSE);
1954 gui_side = start_action(abox, find_cb, paths,
1955 o_action_force.int_value,
1956 o_action_brief.int_value,
1957 o_action_recurse.int_value,
1958 o_action_newer.int_value);
1959 if (!gui_side)
1960 return;
1962 abox_add_results(ABOX(abox));
1964 gui_side->default_string = &last_find_string;
1965 abox_add_entry(ABOX(abox), last_find_string,
1966 new_help_button(show_condition_help, NULL));
1967 g_signal_connect(ABOX(abox)->entry, "changed",
1968 G_CALLBACK(entry_changed), gui_side);
1969 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1971 gui_side->show_info = TRUE;
1972 gui_side->entry_string_func = set_find_string_colour;
1974 number_of_windows++;
1975 gtk_widget_show(abox);
1978 /* Count disk space used by selected items */
1979 void action_usage(GList *paths)
1981 GUIside *gui_side;
1982 GtkWidget *abox;
1984 if (!paths)
1986 report_error(_("You need to select some items to count"));
1987 return;
1990 abox = abox_new(_("Disk Usage"), TRUE);
1992 gui_side = start_action(abox, usage_cb, paths,
1993 o_action_force.int_value,
1994 o_action_brief.int_value,
1995 o_action_recurse.int_value,
1996 o_action_newer.int_value);
1997 if (!gui_side)
1998 return;
2000 gui_side->show_info = TRUE;
2002 number_of_windows++;
2004 gtk_widget_show(abox);
2007 /* Mount/unmount listed items (paths).
2008 * Free the list after this function returns.
2009 * If open_dir is TRUE and the dir is successfully mounted, open it.
2010 * quiet can be -1 for default.
2012 void action_mount(GList *paths, gboolean open_dir, int quiet)
2014 #ifdef DO_MOUNT_POINTS
2015 GUIside *gui_side;
2016 GtkWidget *abox;
2018 if (quiet == -1)
2019 quiet = o_action_mount.int_value;
2021 mount_open_dir = open_dir;
2023 abox = abox_new(_("Mount / Unmount"), quiet);
2024 gui_side = start_action(abox, mount_cb, paths,
2025 o_action_force.int_value,
2026 o_action_brief.int_value,
2027 o_action_recurse.int_value,
2028 o_action_newer.int_value);
2029 if (!gui_side)
2030 return;
2032 number_of_windows++;
2033 gtk_widget_show(abox);
2034 #else
2035 report_error(
2036 _("ROX-Filer does not yet support mount points on your "
2037 "system. Sorry."));
2038 #endif /* DO_MOUNT_POINTS */
2041 /* Delete these paths */
2042 void action_delete(GList *paths)
2044 GUIside *gui_side;
2045 GtkWidget *abox;
2047 if (!remove_pinned_ok(paths))
2048 return;
2050 abox = abox_new(_("Delete"), o_action_delete.int_value);
2051 gui_side = start_action(abox, delete_cb, paths,
2052 o_action_force.int_value,
2053 o_action_brief.int_value,
2054 o_action_recurse.int_value,
2055 o_action_newer.int_value);
2056 if (!gui_side)
2057 return;
2059 abox_add_flag(ABOX(abox),
2060 _("Force"), _("Don't confirm deletion of non-writeable items"),
2061 'F', o_action_force.int_value);
2062 abox_add_flag(ABOX(abox),
2063 _("Brief"), _("Only log directories being deleted"),
2064 'B', o_action_brief.int_value);
2066 number_of_windows++;
2067 gtk_widget_show(abox);
2070 /* Change the permissions of the selected items */
2071 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2073 GtkWidget *abox;
2074 GUIside *gui_side;
2075 static GList *presets = NULL;
2076 gboolean recurse = force_recurse || o_action_recurse.int_value;
2078 if (!paths)
2080 report_error(_("You need to select the items "
2081 "whose permissions you want to change"));
2082 return;
2085 if (!presets)
2087 presets = g_list_append(presets, (gchar *)
2088 _("a+x (Make executable/searchable)"));
2089 presets = g_list_append(presets, (gchar *)
2090 _("a-x (Make non-executable/non-searchable)"));
2091 presets = g_list_append(presets, (gchar *)
2092 _("u+rw (Give owner read+write)"));
2093 presets = g_list_append(presets, (gchar *)
2094 _("go-rwx (Private - owner access only)"));
2095 presets = g_list_append(presets, (gchar *)
2096 _("go=u-w (Public access, not write)"));
2099 if (!last_chmod_string)
2100 last_chmod_string = g_strdup((guchar *) presets->data);
2102 if (action)
2103 new_entry_string = g_strdup(action);
2104 else
2105 new_entry_string = g_strdup(last_chmod_string);
2107 abox = abox_new(_("Permissions"), FALSE);
2108 gui_side = start_action(abox, chmod_cb, paths,
2109 o_action_force.int_value,
2110 o_action_brief.int_value,
2111 recurse,
2112 o_action_newer.int_value);
2114 if (!gui_side)
2115 goto out;
2117 abox_add_flag(ABOX(abox),
2118 _("Brief"), _("Don't list processed files"),
2119 'B', o_action_brief.int_value);
2120 abox_add_flag(ABOX(abox),
2121 _("Recurse"), _("Also change contents of subdirectories"),
2122 'R', recurse);
2124 gui_side->default_string = &last_chmod_string;
2125 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2126 new_help_button(show_chmod_help, NULL));
2128 g_signal_connect(ABOX(abox)->entry, "changed",
2129 G_CALLBACK(entry_changed), gui_side);
2130 #if 0
2131 g_signal_connect_swapped(gui_side->entry, "activate",
2132 G_CALLBACK(gtk_button_clicked),
2133 gui_side->yes);
2134 #endif
2136 number_of_windows++;
2137 gtk_widget_show(abox);
2139 out:
2140 null_g_free(&new_entry_string);
2143 /* Set the MIME type of the selected items */
2144 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2146 GtkWidget *abox;
2147 GUIside *gui_side;
2148 GList *presets = NULL;
2149 gboolean recurse = force_recurse || o_action_recurse.int_value;
2151 if (!paths)
2153 report_error(_("You need to select the items "
2154 "whose type you want to change"));
2155 return;
2158 if (!last_settype_string)
2159 last_settype_string = g_strdup("text/plain");
2161 if (oldtype)
2162 new_entry_string = g_strdup(oldtype);
2163 else
2164 new_entry_string = g_strdup(last_settype_string);
2166 abox = abox_new(_("Set type"), FALSE);
2167 gui_side = start_action(abox, settype_cb, paths,
2168 o_action_force.int_value,
2169 o_action_brief.int_value,
2170 recurse,
2171 o_action_newer.int_value);
2173 if (!gui_side)
2174 goto out;
2176 abox_add_flag(ABOX(abox),
2177 _("Brief"), _("Don't list processed files"),
2178 'B', o_action_brief.int_value);
2179 abox_add_flag(ABOX(abox),
2180 _("Recurse"), _("Change contents of subdirectories"),
2181 'R', recurse);
2183 gui_side->default_string = &last_settype_string;
2185 /* Note: get the list again each time -- it can change */
2186 presets = mime_type_name_list();
2187 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2188 new_help_button(show_settype_help, NULL));
2189 g_list_free(presets);
2191 g_signal_connect(ABOX(abox)->entry, "changed",
2192 G_CALLBACK(entry_changed), gui_side);
2194 number_of_windows++;
2195 gtk_widget_show(abox);
2197 out:
2198 null_g_free(&new_entry_string);
2201 /* If leaf is NULL then the copy has the same name as the original.
2202 * quiet can be -1 for default.
2204 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2206 GUIside *gui_side;
2207 GtkWidget *abox;
2209 if (quiet == -1)
2210 quiet = o_action_copy.int_value;
2212 action_dest = dest;
2213 action_leaf = leaf;
2214 action_do_func = do_copy;
2216 abox = abox_new(_("Copy"), quiet);
2217 gui_side = start_action(abox, list_cb, paths,
2218 o_action_force.int_value,
2219 o_action_brief.int_value,
2220 o_action_recurse.int_value,
2221 o_action_newer.int_value);
2222 if (!gui_side)
2223 return;
2225 abox_add_flag(ABOX(abox),
2226 _("Newer"),
2227 _("Only over-write if source is newer than destination."),
2228 'W', o_action_newer.int_value);
2229 abox_add_flag(ABOX(abox),
2230 _("Brief"), _("Only log directories as they are copied"),
2231 'B', o_action_brief.int_value);
2233 number_of_windows++;
2234 gtk_widget_show(abox);
2237 /* If leaf is NULL then the file is not renamed.
2238 * quiet can be -1 for default.
2240 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2242 GUIside *gui_side;
2243 GtkWidget *abox;
2245 if (quiet == -1)
2246 quiet = o_action_move.int_value;
2248 action_dest = dest;
2249 action_leaf = leaf;
2250 action_do_func = do_move;
2252 abox = abox_new(_("Move"), quiet);
2253 gui_side = start_action(abox, list_cb, paths,
2254 o_action_force.int_value,
2255 o_action_brief.int_value,
2256 o_action_recurse.int_value,
2257 o_action_newer.int_value);
2258 if (!gui_side)
2259 return;
2261 abox_add_flag(ABOX(abox),
2262 _("Newer"),
2263 _("Only over-write if source is newer than destination."),
2264 'W', o_action_newer.int_value);
2265 abox_add_flag(ABOX(abox),
2266 _("Brief"), _("Don't log each file as it is moved"),
2267 'B', o_action_brief.int_value);
2268 number_of_windows++;
2269 gtk_widget_show(abox);
2272 /* If leaf is NULL then the link will have the same name */
2273 void action_link(GList *paths, const char *dest, const char *leaf,
2274 gboolean relative)
2276 GtkWidget *abox;
2277 GUIside *gui_side;
2279 action_dest = dest;
2280 action_leaf = leaf;
2281 if (relative)
2282 action_do_func = do_link_relative;
2283 else
2284 action_do_func = do_link_absolute;
2286 abox = abox_new(_("Link"), o_action_link.int_value);
2287 gui_side = start_action(abox, list_cb, paths,
2288 o_action_force.int_value,
2289 o_action_brief.int_value,
2290 o_action_recurse.int_value,
2291 o_action_newer.int_value);
2292 if (!gui_side)
2293 return;
2295 number_of_windows++;
2296 gtk_widget_show(abox);
2299 /* Eject these paths */
2300 void action_eject(GList *paths)
2302 GUIside *gui_side;
2303 GtkWidget *abox;
2305 abox = abox_new(_("Eject"), TRUE);
2306 gui_side = start_action(abox, eject_cb, paths,
2307 o_action_force.int_value,
2308 o_action_brief.int_value,
2309 o_action_recurse.int_value,
2310 o_action_newer.int_value);
2311 if (!gui_side)
2312 return;
2314 number_of_windows++;
2315 gtk_widget_show(abox);
2318 void action_init(void)
2320 option_add_int(&o_action_copy, "action_copy", 1);
2321 option_add_int(&o_action_move, "action_move", 1);
2322 option_add_int(&o_action_link, "action_link", 1);
2323 option_add_int(&o_action_delete, "action_delete", 0);
2324 option_add_int(&o_action_mount, "action_mount", 1);
2325 option_add_int(&o_action_force, "action_force", FALSE);
2326 option_add_int(&o_action_brief, "action_brief", FALSE);
2327 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2328 option_add_int(&o_action_newer, "action_newer", FALSE);
2331 #define MAX_ASK 4
2333 /* Check to see if any of the selected items (or their children) are
2334 * on the pinboard or panel. If so, ask for confirmation.
2336 * TRUE if it's OK to lose them.
2338 static gboolean remove_pinned_ok(GList *paths)
2340 GList *ask = NULL, *next;
2341 GString *message;
2342 int i, ask_n = 0;
2343 gboolean retval;
2345 for (; paths; paths = paths->next)
2347 guchar *path = (guchar *) paths->data;
2349 if (icons_require(path))
2351 if (++ask_n > MAX_ASK)
2352 break;
2353 ask = g_list_append(ask, path);
2357 if (!ask)
2358 return TRUE;
2360 if (ask_n > MAX_ASK)
2362 message = g_string_new(_("Deleting items such as "));
2363 ask_n--;
2365 else if (ask_n == 1)
2366 message = g_string_new(_("Deleting the item "));
2367 else
2368 message = g_string_new(_("Deleting the items "));
2370 i = 0;
2371 for (next = ask; next; next = next->next)
2373 guchar *path = (guchar *) next->data;
2374 guchar *leaf;
2376 leaf = strrchr(path, '/');
2377 if (leaf)
2378 leaf++;
2379 else
2380 leaf = path;
2382 g_string_append_c(message, '`');
2383 g_string_append(message, leaf);
2384 g_string_append_c(message, '\'');
2385 i++;
2386 if (i == ask_n - 1 && i > 0)
2387 g_string_append(message, _(" and "));
2388 else if (i < ask_n)
2389 g_string_append(message, ", ");
2392 g_list_free(ask);
2394 if (ask_n == 1)
2395 message = g_string_append(message,
2396 _(" will affect some items on the pinboard "
2397 "or panel - really delete it?"));
2398 else
2400 if (ask_n > MAX_ASK)
2401 message = g_string_append_c(message, ',');
2402 message = g_string_append(message,
2403 _(" will affect some items on the pinboard "
2404 "or panel - really delete them?"));
2407 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2409 g_string_free(message, TRUE);
2411 return retval;
2414 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2416 FindCondition *cond;
2418 cond = find_compile(string);
2419 entry_set_error(widget, !cond);
2421 find_condition_free(cond);