r4523: Updated headers:
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob82ed015ec37b9e2036752a8f3a6d8cd77ee44f37
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* action.c - code for handling the filer action windows.
21 * These routines generally fork() and talk to us via pipes.
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <sys/time.h>
33 #include <utime.h>
34 #include <stdarg.h>
36 #include "global.h"
38 #include "action.h"
39 #include "abox.h"
40 #include "string.h"
41 #include "support.h"
42 #include "gui_support.h"
43 #include "filer.h"
44 #include "display.h"
45 #include "main.h"
46 #include "options.h"
47 #include "modechange.h"
48 #include "find.h"
49 #include "dir.h"
50 #include "icon.h"
51 #include "mount.h"
52 #include "type.h"
53 #include "xtypes.h"
55 #if defined(HAVE_GETXATTR)
56 # define ATTR_MAN_PAGE N_("See the attr(5) man page for full details.")
57 #elif defined(HAVE_ATTROPEN)
58 # define ATTR_MAN_PAGE N_("See the fsattr(5) man page for full details.")
59 #else
60 # define ATTR_MAN_PAGE N_("You do not appear to have OS support.")
61 #endif
63 /* Parent->Child messages are one character each:
65 * Y/N Yes/No button clicked
66 * F Force deletion of non-writeable items
67 * Q Quiet toggled
68 * E Entry text changed
69 * W neWer toggled
72 typedef struct _GUIside GUIside;
73 typedef void ActionChild(gpointer data);
74 typedef void ForDirCB(const char *path, const char *dest_path);
76 struct _GUIside
78 ABox *abox; /* The action window widget */
80 int from_child; /* File descriptor */
81 FILE *to_child;
82 int input_tag; /* gdk_input_add() */
83 pid_t child; /* Process ID */
84 int errors; /* Number of errors so far */
85 gboolean show_info; /* For Disk Usage */
87 guchar **default_string; /* Changed when the entry changes */
88 void (*entry_string_func)(GtkWidget *widget,
89 const guchar *string);
91 int abort_attempts;
94 /* These don't need to be in a structure because we fork() before
95 * using them again.
97 static gboolean mount_open_dir = FALSE;
98 static gboolean mount_mount = FALSE; /* (FALSE => unmount) */
99 static int from_parent = 0;
100 static FILE *to_parent = NULL;
101 static gboolean quiet = FALSE;
102 static GString *message = NULL;
103 static const char *action_dest = NULL;
104 static const char *action_leaf = NULL;
105 static void (*action_do_func)(const char *source, const char *dest);
106 static double size_tally; /* For Disk Usage */
107 static unsigned long dir_counter; /* For Disk Usage */
108 static unsigned long file_counter; /* For Disk Usage */
110 static struct mode_change *mode_change = NULL; /* For Permissions */
111 static FindCondition *find_condition = NULL; /* For Find */
112 static MIME_type *type_change = NULL;
114 /* Only used by child */
115 static gboolean o_force = FALSE;
116 static gboolean o_brief = FALSE;
117 static gboolean o_recurse = FALSE;
118 static gboolean o_newer = FALSE;
120 static Option o_action_copy, o_action_move, o_action_link;
121 static Option o_action_delete, o_action_mount;
122 static Option o_action_force, o_action_brief, o_action_recurse;
123 static Option o_action_newer;
125 static Option o_action_mount_command;
126 static Option o_action_umount_command;
128 /* Whenever the text in these boxes is changed we store a copy of the new
129 * string to be used as the default next time.
131 static guchar *last_chmod_string = NULL;
132 static guchar *last_find_string = NULL;
133 static guchar *last_settype_string = NULL;
135 /* Set to one of the above before forking. This may change over a call to
136 * reply(). It is reset to NULL once the text is parsed.
138 static guchar *new_entry_string = NULL;
140 /* Static prototypes */
141 static void send_done(void);
142 static void send_check_path(const gchar *path);
143 static void send_mount_path(const gchar *path);
144 static gboolean printf_send(const char *msg, ...);
145 static gboolean send_msg(void);
146 static gboolean send_error(void);
147 static gboolean send_dir(const char *dir);
148 static gboolean read_exact(int source, char *buffer, ssize_t len);
149 static void do_mount(const guchar *path, gboolean mount);
150 static gboolean printf_reply(int fd, gboolean ignore_quiet,
151 const char *msg, ...);
152 static gboolean remove_pinned_ok(GList *paths);
154 /* SUPPORT */
157 /* This is called whenever the user edits the entry box (if any) - send the
158 * new string.
160 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
162 guchar *text;
164 g_return_if_fail(gui_side->default_string != NULL);
166 text = gtk_editable_get_chars(entry, 0, -1);
168 if (gui_side->entry_string_func)
169 gui_side->entry_string_func(GTK_WIDGET(entry), text);
171 g_free(*(gui_side->default_string));
172 *(gui_side->default_string) = text; /* Gets text's ref */
174 if (!gui_side->to_child)
175 return;
177 fputc('E', gui_side->to_child);
178 fputs(text, gui_side->to_child);
179 fputc('\n', gui_side->to_child);
180 fflush(gui_side->to_child);
183 void show_condition_help(gpointer data)
185 GtkWidget *help;
186 GtkWidget *text;
188 help = gtk_dialog_new_with_buttons(
189 _("Find expression reference"),
190 NULL, 0,
191 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
192 NULL);
193 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
195 text = gtk_label_new(NULL);
196 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
197 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
198 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
199 gtk_label_set_markup(GTK_LABEL(text), _(
200 "<u>Quick Start</u>\n"
201 "Just put the name of the file you're looking for in single quotes:\n"
202 "<b>'index.html'</b> (to find a file called 'index.html')\n"
203 "\n"
204 "<u>Examples</u>\n"
205 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
206 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
207 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
208 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
209 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
210 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
211 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
212 "\n"
213 "<u>Simple Tests</u>\n"
214 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
215 "(types)\n"
216 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
217 "(permissions)\n"
218 "<b>IsEmpty, IsMine</b>\n"
219 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
220 "contains a slash then the match is against the full path; otherwise it is\n"
221 "against the leafname only.\n"
222 "\n"
223 "<u>Comparisons</u>\n"
224 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
225 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
226 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
227 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
228 "(values)\n"
229 "\n"
230 "<u>Specials</u>\n"
231 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
232 "a % in 'command' is replaced with the path of the current file)\n"
233 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
235 g_signal_connect(help, "response",
236 G_CALLBACK(gtk_widget_destroy), NULL);
238 gtk_widget_show_all(help);
241 static void show_chmod_help(gpointer data)
243 GtkWidget *help;
244 GtkWidget *text;
246 help = gtk_dialog_new_with_buttons(
247 _("Change permissions reference"),
248 NULL, 0,
249 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
250 NULL);
251 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
253 text = gtk_label_new(NULL);
254 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
255 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
256 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
257 gtk_label_set_markup(GTK_LABEL(text), _(
258 "Normally, you can just select a command from the menu (click \n"
259 "on the arrow beside the command box). Sometimes, you need more...\n"
260 "\n"
261 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
262 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
263 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
264 "determines whether to\n"
265 "change the permissions for the User (owner), Group or Others.\n"
266 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
267 "exactly the permissions.\n"
268 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
269 "\n"
270 "Bracketed text and spaces are ignored.\n"
271 "\n"
272 "<u>Examples</u>\n"
273 "<b>u+rw</b>: the file owner gains read and write permission\n"
274 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
275 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
276 "write permission\n"
277 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
278 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
279 "executable by anyone become executable by everyone\n"
280 "<b>u+rw, go+r</b>: two commands at once!\n"
281 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
282 "<b>755</b>: set the permissions directly\n"
283 "\n"
284 "See the chmod(1) man page for full details."));
286 g_signal_connect(help, "response",
287 G_CALLBACK(gtk_widget_destroy), NULL);
289 gtk_widget_show_all(help);
293 static void show_settype_help(gpointer data)
295 GtkWidget *help;
296 GtkWidget *text;
298 help = gtk_dialog_new_with_buttons(
299 _("Set type reference"),
300 NULL, 0,
301 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
302 NULL);
303 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
305 text = gtk_label_new(NULL);
306 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
307 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
308 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
309 gtk_label_set_markup(GTK_LABEL(text), _(
310 "Normally ROX-Filer determines the type of a regular file\n"
311 "by matching it's name against a pattern. To change the\n"
312 "type of the file you must rename it.\n"
313 "\n"
314 "Newer file systems can support something called 'Extended\n"
315 "Attributes' which can be used to store additional data with\n"
316 "each file as named parameters. ROX-Filer uses the\n"
317 "'user.mime_type' attribute to store file types.\n"
318 "\n"
319 "File types are only supported for regular files, not\n"
320 "directories, devices, pipes or sockets, and then only\n"
321 "on certain file systems and where the OS implements them.\n"));
323 text = gtk_label_new(_(ATTR_MAN_PAGE));
324 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
325 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
327 g_signal_connect(help, "response",
328 G_CALLBACK(gtk_widget_destroy), NULL);
330 gtk_widget_show_all(help);
333 static void process_message(GUIside *gui_side, const gchar *buffer)
335 ABox *abox = gui_side->abox;
337 if (*buffer == '?')
338 abox_ask(abox, buffer + 1);
339 else if (*buffer == 's')
340 dir_check_this(buffer + 1); /* Update this item */
341 else if (*buffer == '=')
342 abox_add_filename(abox, buffer + 1);
343 else if (*buffer == '#')
344 abox_clear_results(abox);
345 else if (*buffer == 'X')
347 filer_close_recursive(buffer + 1);
348 /* Let child know it's safe to continue... */
349 fputc('X', gui_side->to_child);
350 fflush(gui_side->to_child);
352 else if (*buffer == 'm' || *buffer == 'M')
354 /* Mount / major changes to this path */
355 if (*buffer == 'M')
357 mount_update(TRUE);
358 mount_user_mount(buffer + 1);
360 filer_check_mounted(buffer + 1);
362 else if (*buffer == '/')
363 abox_set_current_object(abox, buffer + 1);
364 else if (*buffer == 'o')
365 filer_opendir(buffer + 1, NULL, NULL);
366 else if (*buffer == '!')
368 gui_side->errors++;
369 abox_log(abox, buffer + 1, "error");
371 else if (*buffer == '<')
372 abox_set_file(abox, 0, buffer+1);
373 else if (*buffer == '>')
375 abox_set_file(abox, 1, buffer+1);
376 abox_show_compare(abox, TRUE);
378 else if (*buffer == '%')
380 abox_set_percentage(abox, atoi(buffer+1));
382 else
383 abox_log(abox, buffer + 1, NULL);
386 /* Called when the child sends us a message */
387 static void message_from_child(gpointer data,
388 gint source,
389 GdkInputCondition condition)
391 char buf[5];
392 GUIside *gui_side = (GUIside *) data;
393 ABox *abox = gui_side->abox;
394 GtkTextBuffer *text_buffer;
396 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
398 if (read_exact(source, buf, 4))
400 ssize_t message_len;
401 char *buffer;
403 buf[4] = '\0';
404 message_len = strtol(buf, NULL, 16);
405 buffer = g_malloc(message_len + 1);
406 if (message_len > 0 && read_exact(source, buffer, message_len))
408 buffer[message_len] = '\0';
409 process_message(gui_side, buffer);
410 g_free(buffer);
411 return;
413 g_printerr("Child died in the middle of a message.\n");
416 if (gui_side->abort_attempts)
417 abox_log(abox, _("\nProcess terminated.\n"), "error");
419 /* The child is dead */
420 gui_side->child = 0;
422 fclose(gui_side->to_child);
423 gui_side->to_child = NULL;
424 close(gui_side->from_child);
425 g_source_remove(gui_side->input_tag);
426 abox_cancel_ask(gui_side->abox);
428 if (gui_side->errors)
430 guchar *report;
432 if (gui_side->errors == 1)
433 report = g_strdup(_("There was one error.\n"));
434 else
435 report = g_strdup_printf(_("There were %d errors.\n"),
436 gui_side->errors);
438 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
440 g_free(report);
442 else if (gui_side->show_info == FALSE)
443 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
446 /* Scans src_dir, calling cb(item, dest_path) for each item */
447 static void for_dir_contents(ForDirCB *cb,
448 const char *src_dir,
449 const char *dest_path)
451 DIR *d;
452 struct dirent *ent;
453 GList *list = NULL, *next;
455 d = mc_opendir(src_dir);
456 if (!d)
458 /* Message displayed is "ERROR reading 'path': message" */
459 printf_send("!%s '%s': %s\n", _("ERROR reading"),
460 src_dir, g_strerror(errno));
461 return;
464 send_dir(src_dir);
466 while ((ent = mc_readdir(d)))
468 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
469 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
470 continue;
471 list = g_list_prepend(list, g_strdup(make_path(src_dir,
472 ent->d_name)));
474 mc_closedir(d);
476 for (next = list; next; next = next->next)
478 cb((char *) next->data, dest_path);
480 g_free(next->data);
482 g_list_free(list);
485 /* Read this many bytes into the buffer. TRUE on success. */
486 static gboolean read_exact(int source, char *buffer, ssize_t len)
488 while (len > 0)
490 ssize_t got;
491 got = read(source, buffer, len);
492 if (got < 1)
493 return FALSE;
494 len -= got;
495 buffer += got;
497 return TRUE;
500 static void send_done(void)
502 printf_send(_("'\nDone\n"));
505 /* Notify the filer that this item has been updated */
506 static void send_check_path(const gchar *path)
508 printf_send("s%s", path);
511 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
512 static void send_mount_path(const gchar *path)
514 printf_send("m%s", path);
517 /* Send a message to the filer process. The first character indicates the
518 * type of the message.
520 static gboolean printf_send(const char *msg, ...)
522 va_list args;
523 gchar *tmp;
525 va_start(args, msg);
526 tmp = g_strdup_vprintf(msg, args);
527 va_end(args);
529 g_string_assign(message, tmp);
530 g_free(tmp);
532 return send_msg();
535 /* Send 'message' to our parent process. TRUE on success. */
536 static gboolean send_msg(void)
538 char len_buffer[5];
539 ssize_t len;
541 g_return_val_if_fail(message->len < 0xffff, FALSE);
543 sprintf(len_buffer, "%04" G_GSIZE_MODIFIER "x", message->len);
544 fwrite(len_buffer, 1, 4, to_parent);
545 len = fwrite(message->str, 1, message->len, to_parent);
546 fflush(to_parent);
547 return len == (ssize_t) message->len;
550 /* Set the directory indicator at the top of the window */
551 static gboolean send_dir(const char *dir)
553 return printf_send("/%s", dir);
556 static gboolean send_error(void)
558 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
561 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
563 gchar code;
565 if (!gui_side->to_child)
566 return;
568 if (response == GTK_RESPONSE_YES)
569 code = 'Y';
570 else if (response == GTK_RESPONSE_NO)
571 code = 'N';
572 else
573 return;
575 fputc(code, gui_side->to_child);
576 fflush(gui_side->to_child);
577 abox_show_compare(gui_side->abox, FALSE);
580 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
582 if (!gui_side->to_child)
583 return;
585 fputc(flag, gui_side->to_child);
586 fflush(gui_side->to_child);
589 static void read_new_entry_text(void)
591 int len;
592 char c;
593 GString *new;
595 new = g_string_new(NULL);
597 for (;;)
599 len = read(from_parent, &c, 1);
600 if (len != 1)
602 fprintf(stderr, "read() error: %s\n",
603 g_strerror(errno));
604 _exit(1); /* Parent died? */
607 if (c == '\n')
608 break;
609 g_string_append_c(new, c);
612 g_free(new_entry_string);
613 new_entry_string = new->str;
614 g_string_free(new, FALSE);
617 static void process_flag(char flag)
619 switch (flag)
621 case 'Q':
622 quiet = !quiet;
623 break;
624 case 'F':
625 o_force = !o_force;
626 break;
627 case 'R':
628 o_recurse = !o_recurse;
629 break;
630 case 'B':
631 o_brief = !o_brief;
632 break;
633 case 'W':
634 o_newer = !o_newer;
635 break;
636 case 'E':
637 read_new_entry_text();
638 break;
639 default:
640 printf_send("!ERROR: Bad message '%c'\n", flag);
641 break;
645 /* If the parent has sent any flag toggles, read them */
646 static void check_flags(void)
648 fd_set set;
649 int got;
650 char retval;
651 struct timeval tv;
653 FD_ZERO(&set);
655 while (1)
657 FD_SET(from_parent, &set);
658 tv.tv_sec = 0;
659 tv.tv_usec = 0;
660 got = select(from_parent + 1, &set, NULL, NULL, &tv);
662 if (got == -1)
663 g_error("select() failed: %s\n", g_strerror(errno));
664 else if (!got)
665 return;
667 got = read(from_parent, &retval, 1);
668 if (got != 1)
669 g_error("read() error: %s\n", g_strerror(errno));
671 process_flag(retval);
675 /* Read until the user sends a reply. If ignore_quiet is TRUE then
676 * the user MUST click Yes or No, else treat quiet on as Yes.
677 * If the user needs prompting then does send_msg().
679 static gboolean printf_reply(int fd, gboolean ignore_quiet,
680 const char *msg, ...)
682 ssize_t len;
683 char retval;
684 va_list args;
685 gchar *tmp;
687 if (quiet && !ignore_quiet)
688 return TRUE;
690 va_start(args, msg);
691 tmp = g_strdup_vprintf(msg, args);
692 va_end(args);
694 g_string_assign(message, tmp);
695 g_free(tmp);
697 send_msg();
699 while (1)
701 len = read(fd, &retval, 1);
702 if (len != 1)
704 fprintf(stderr, "read() error: %s\n",
705 g_strerror(errno));
706 _exit(1); /* Parent died? */
709 switch (retval)
711 case 'Y':
712 printf_send("' %s\n", _("Yes"));
713 return TRUE;
714 case 'N':
715 printf_send("' %s\n", _("No"));
716 return FALSE;
717 default:
718 process_flag(retval);
719 break;
724 static void abort_operation(GtkWidget *widget, gpointer data)
726 GUIside *gui_side = (GUIside *) data;
728 if (gui_side->child)
730 if (gui_side->abort_attempts == 0)
732 abox_log(ABOX(widget),
733 _("\nAsking child process to terminate...\n"),
734 "error");
735 kill(-gui_side->child, SIGTERM);
737 else
739 abox_log(ABOX(widget),
740 _("\nTrying to KILL run-away process...\n"),
741 "error");
742 kill(-gui_side->child, SIGKILL);
743 kill(-gui_side->child, SIGCONT);
745 gui_side->abort_attempts++;
747 else
748 gtk_widget_destroy(widget);
751 static void destroy_action_window(GtkWidget *widget, gpointer data)
753 GUIside *gui_side = (GUIside *) data;
755 if (gui_side->child)
757 kill(-gui_side->child, SIGTERM);
758 fclose(gui_side->to_child);
759 close(gui_side->from_child);
760 g_source_remove(gui_side->input_tag);
763 g_free(gui_side);
765 one_less_window();
768 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
769 * (NULL on failure). The child calls func().
771 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
772 int force, int brief, int recurse, int newer)
774 gboolean autoq;
775 int filedes[4]; /* 0 and 2 are for reading */
776 GUIside *gui_side;
777 pid_t child;
778 struct sigaction act;
780 if (pipe(filedes))
782 report_error("pipe: %s", g_strerror(errno));
783 gtk_widget_destroy(abox);
784 return NULL;
787 if (pipe(filedes + 2))
789 close(filedes[0]);
790 close(filedes[1]);
791 report_error("pipe: %s", g_strerror(errno));
792 gtk_widget_destroy(abox);
793 return NULL;
796 autoq = gtk_toggle_button_get_active(
797 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
799 o_force = force;
800 o_brief = brief;
801 o_recurse = recurse;
802 o_newer = newer;
804 child = fork();
805 switch (child)
807 case -1:
808 report_error("fork: %s", g_strerror(errno));
809 gtk_widget_destroy(abox);
810 return NULL;
811 case 0:
812 /* We are the child */
814 /* Create a new process group */
815 setpgid(0, 0);
817 quiet = autoq;
819 dir_drop_all_dnotifies();
821 /* Reset the SIGCHLD handler */
822 act.sa_handler = SIG_DFL;
823 sigemptyset(&act.sa_mask);
824 act.sa_flags = 0;
825 sigaction(SIGCHLD, &act, NULL);
827 message = g_string_new(NULL);
828 close(filedes[0]);
829 close(filedes[3]);
830 to_parent = fdopen(filedes[1], "wb");
831 from_parent = filedes[2];
832 func(data);
833 send_dir("");
834 _exit(0);
837 /* We are the parent */
838 close(filedes[1]);
839 close(filedes[2]);
840 gui_side = g_new(GUIside, 1);
841 gui_side->from_child = filedes[0];
842 gui_side->to_child = fdopen(filedes[3], "wb");
843 gui_side->child = child;
844 gui_side->errors = 0;
845 gui_side->show_info = FALSE;
846 gui_side->default_string = NULL;
847 gui_side->entry_string_func = NULL;
848 gui_side->abort_attempts = 0;
850 gui_side->abox = ABOX(abox);
851 g_signal_connect(abox, "destroy",
852 G_CALLBACK(destroy_action_window), gui_side);
854 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
855 g_signal_connect(abox, "flag_toggled",
856 G_CALLBACK(flag_toggled), gui_side);
857 g_signal_connect(abox, "abort_operation",
858 G_CALLBACK(abort_operation), gui_side);
860 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
861 GDK_INPUT_READ,
862 message_from_child,
863 gui_side, NULL);
865 return gui_side;
868 /* ACTIONS ON ONE ITEM */
870 /* These may call themselves recursively, or ask questions, etc */
872 /* Updates the global size_tally, file_counter and dir_counter */
873 static void do_usage(const char *src_path, const char *unused)
875 struct stat info;
877 check_flags();
879 if (mc_lstat(src_path, &info))
881 printf_send("'%s:\n", src_path);
882 send_error();
884 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
886 file_counter++;
887 size_tally += info.st_size;
889 else if (S_ISDIR(info.st_mode))
891 dir_counter++;
892 if (printf_reply(from_parent, FALSE,
893 _("?Count contents of %s?"), src_path))
895 char *safe_path;
896 safe_path = g_strdup(src_path);
897 for_dir_contents(do_usage, safe_path, safe_path);
898 g_free(safe_path);
901 else
902 file_counter++;
905 /* dest_path is the dir containing src_path */
906 static void do_delete(const char *src_path, const char *unused)
908 struct stat info;
909 gboolean write_prot;
910 char *safe_path;
912 check_flags();
914 if (mc_lstat(src_path, &info))
916 send_error();
917 return;
920 write_prot = S_ISLNK(info.st_mode) ? FALSE
921 : access(src_path, W_OK) != 0;
922 if (write_prot || !quiet)
924 int res;
926 printf_send("<%s", src_path);
927 printf_send(">");
928 res=printf_reply(from_parent, write_prot && !o_force,
929 _("?Delete %s'%s'?"),
930 write_prot ? _("WRITE-PROTECTED ") : "",
931 src_path);
932 printf_send("<");
933 if (!res)
934 return;
936 else if (!o_brief)
937 printf_send(_("'Deleting '%s'\n"), src_path);
939 safe_path = g_strdup(src_path);
941 if (S_ISDIR(info.st_mode))
943 for_dir_contents(do_delete, safe_path, safe_path);
944 if (rmdir(safe_path))
946 g_free(safe_path);
947 send_error();
948 return;
950 printf_send(_("'Directory '%s' deleted\n"), safe_path);
951 send_mount_path(safe_path);
953 else if (unlink(src_path))
954 send_error();
955 else
957 send_check_path(safe_path);
958 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
960 gchar *dir;
961 dir = g_path_get_dirname(safe_path);
962 send_check_path(dir);
963 g_free(dir);
967 g_free(safe_path);
970 static void do_eject(const char *path)
972 const char *argv[3] = {NULL, NULL, NULL};
973 char *err;
975 check_flags();
977 if (!quiet)
979 int res;
980 printf_send("<%s", path);
981 printf_send(">");
982 res=printf_reply(from_parent, !o_force,
983 _("?Eject '%s'?"),
984 path);
985 printf_send("<");
986 if (!res)
987 return;
989 else if (!o_brief)
990 printf_send(_("'Eject '%s'\n"), path);
992 /* Need to close all sub-directories now, or we
993 * can't unmount if dnotify is used.
996 char c = '?';
997 printf_send("X%s", path);
998 /* Wait until it's safe... */
999 read(from_parent, &c, 1);
1000 g_return_if_fail(c == 'X');
1003 argv[0] = "eject";
1004 argv[1] = path;
1005 argv[2] = NULL;
1006 err = fork_exec_wait(argv);
1007 if (err)
1009 printf_send(_("!%s\neject failed\n"), err);
1010 g_free(err);
1013 printf_send("M%s", path);
1017 /* path is the item to check. If is is a directory then we may recurse
1018 * (unless prune is used).
1020 static void do_find(const char *path, const char *unused)
1022 FindInfo info;
1024 check_flags();
1026 if (!quiet)
1028 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1029 return;
1032 for (;;)
1034 if (new_entry_string)
1036 find_condition_free(find_condition);
1037 find_condition = find_compile(new_entry_string);
1038 null_g_free(&new_entry_string);
1041 if (find_condition)
1042 break;
1044 printf_send(_("!Invalid find condition - "
1045 "change it and try again\n"));
1046 if (!printf_reply(from_parent, TRUE,
1047 _("?Check '%s'?"), path))
1048 return;
1051 if (mc_lstat(path, &info.stats))
1053 send_error();
1054 printf_send(_("'(while checking '%s')\n"), path);
1055 return;
1058 info.fullpath = path;
1059 time(&info.now); /* XXX: Not for each check! */
1061 info.leaf = g_basename(path);
1062 info.prune = FALSE;
1063 if (find_test_condition(find_condition, &info))
1064 printf_send("=%s", path);
1066 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1068 char *safe_path;
1069 safe_path = g_strdup(path);
1070 for_dir_contents(do_find, safe_path, safe_path);
1071 g_free(safe_path);
1075 /* Like mode_compile(), but ignores spaces and bracketed bits */
1076 static struct mode_change *nice_mode_compile(const char *mode_string,
1077 unsigned int masked_ops)
1079 GString *new;
1080 int brackets = 0;
1081 struct mode_change *retval = NULL;
1083 new = g_string_new(NULL);
1085 for (; *mode_string; mode_string++)
1087 if (*mode_string == '(')
1088 brackets++;
1089 if (*mode_string == ')')
1091 brackets--;
1092 if (brackets < 0)
1093 break;
1094 continue;
1097 if (brackets == 0 && *mode_string != ' ')
1098 g_string_append_c(new, *mode_string);
1101 if (brackets == 0)
1102 retval = mode_compile(new->str, masked_ops);
1103 g_string_free(new, TRUE);
1104 return retval;
1107 static void do_chmod(const char *path, const char *unused)
1109 struct stat info;
1110 mode_t new_mode;
1112 check_flags();
1114 if (mc_lstat(path, &info))
1116 send_error();
1117 return;
1119 if (S_ISLNK(info.st_mode))
1120 return;
1122 if (!quiet)
1124 int res;
1125 printf_send("<%s", path);
1126 printf_send(">");
1127 res=printf_reply(from_parent, FALSE,
1128 _("?Change permissions of '%s'?"), path);
1129 printf_send("<");
1130 if (!res)
1131 return;
1133 else if (!o_brief)
1134 printf_send(_("'Changing permissions of '%s'\n"), path);
1136 for (;;)
1138 if (new_entry_string)
1140 if (mode_change)
1141 mode_free(mode_change);
1142 mode_change = nice_mode_compile(new_entry_string,
1143 MODE_MASK_ALL);
1144 null_g_free(&new_entry_string);
1147 if (mode_change)
1148 break;
1150 printf_send(
1151 _("!Invalid mode command - change it and try again\n"));
1152 if (!printf_reply(from_parent, TRUE,
1153 _("?Change permissions of '%s'?"), path))
1154 return;
1157 if (mc_lstat(path, &info))
1159 send_error();
1160 return;
1162 if (S_ISLNK(info.st_mode))
1163 return;
1165 new_mode = mode_adjust(info.st_mode, mode_change);
1166 if (chmod(path, new_mode))
1168 send_error();
1169 return;
1172 send_check_path(path);
1174 if (S_ISDIR(info.st_mode))
1176 send_mount_path(path);
1178 if (o_recurse)
1180 guchar *safe_path;
1181 safe_path = g_strdup(path);
1182 for_dir_contents(do_chmod, safe_path, safe_path);
1183 g_free(safe_path);
1188 static void do_settype(const char *path, const char *unused)
1190 struct stat info;
1192 check_flags();
1194 if (mc_lstat(path, &info))
1196 send_error();
1197 return;
1199 if (S_ISLNK(info.st_mode))
1200 return;
1202 if (!quiet)
1204 int res;
1205 printf_send("<%s", path);
1206 printf_send(">");
1207 res=printf_reply(from_parent, FALSE,
1208 _("?Change type of '%s'?"), path);
1209 printf_send("<");
1210 if (!res)
1211 return;
1214 for (;;)
1216 if (new_entry_string)
1218 type_change = mime_type_lookup(new_entry_string);
1219 null_g_free(&new_entry_string);
1222 if (type_change)
1223 break;
1225 printf_send(_("!Invalid type - "
1226 "change it and try again\n"));
1227 if (!printf_reply(from_parent, TRUE,
1228 _("?Change type of '%s'?"), path))
1229 return;
1232 if (mc_lstat(path, &info))
1234 send_error();
1235 return;
1237 if (S_ISLNK(info.st_mode))
1238 return;
1240 if (S_ISREG(info.st_mode))
1242 if (!o_brief)
1244 const char *comment;
1246 comment = mime_type_comment(type_change);
1247 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1248 comment);
1251 if (xtype_set(path, type_change))
1253 send_error();
1254 return;
1257 send_check_path(path);
1259 else if (S_ISDIR(info.st_mode))
1261 if (o_recurse)
1263 guchar *safe_path;
1264 safe_path = g_strdup(path);
1265 for_dir_contents(do_settype, safe_path, unused);
1266 g_free(safe_path);
1271 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1272 * is set then that is the new leafname, otherwise the leafname stays
1273 * the same.
1275 static const char *make_dest_path(const char *object, const char *dir)
1277 const char *leaf;
1279 if (action_leaf)
1280 leaf = action_leaf;
1281 else
1283 leaf = strrchr(object, '/');
1284 if (!leaf)
1285 leaf = object; /* Error? */
1286 else
1287 leaf++;
1290 return make_path(dir, leaf);
1293 /* If action_leaf is not NULL it specifies the new leaf name */
1294 static void do_copy2(const char *path, const char *dest)
1296 const char *dest_path;
1297 struct stat info;
1298 struct stat dest_info;
1300 check_flags();
1302 dest_path = make_dest_path(path, dest);
1304 if (mc_lstat(path, &info))
1306 send_error();
1307 return;
1310 if (mc_lstat(dest_path, &dest_info) == 0)
1312 int err;
1313 gboolean merge;
1315 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1317 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1319 /* Newer; keep going */
1321 else
1323 printf_send("<%s", path);
1324 printf_send(">%s", dest_path);
1325 if (!printf_reply(from_parent, TRUE,
1326 _("?'%s' already exists - %s?"),
1327 dest_path,
1328 merge ? _("merge contents")
1329 : _("overwrite")))
1330 return;
1333 if (!merge)
1335 if (S_ISDIR(dest_info.st_mode))
1336 err = rmdir(dest_path);
1337 else
1338 err = unlink(dest_path);
1340 if (err)
1342 send_error();
1343 if (errno != ENOENT)
1344 return;
1345 printf_send(_("'Trying copy anyway...\n"));
1349 else if (!quiet)
1351 printf_send("<%s", path);
1352 printf_send(">");
1353 if (!printf_reply(from_parent, FALSE,
1354 _("?Copy %s as %s?"), path, dest_path))
1355 return;
1357 else if (!o_brief || S_ISDIR(info.st_mode))
1358 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1360 if (S_ISDIR(info.st_mode))
1362 mode_t mode = info.st_mode;
1363 char *safe_path, *safe_dest;
1364 struct stat dest_info;
1365 gboolean exists;
1367 safe_path = g_strdup(path);
1368 safe_dest = g_strdup(dest_path);
1370 exists = !mc_lstat(dest_path, &dest_info);
1372 if (exists && !S_ISDIR(dest_info.st_mode))
1373 printf_send(_("!ERROR: Destination already exists, "
1374 "but is not a directory\n"));
1375 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1376 send_error();
1377 else
1379 if (!exists)
1380 /* (just been created then) */
1381 send_check_path(dest_path);
1383 action_leaf = NULL;
1384 for_dir_contents(do_copy2, safe_path, safe_dest);
1385 /* Note: dest_path now invalid... */
1387 if (!exists)
1389 struct utimbuf utb;
1391 /* We may have created the directory with
1392 * more permissions than the source so that
1393 * we could write to it... change it back now.
1395 if (chmod(safe_dest, mode))
1397 /* Some filesystems don't support
1398 * SetGID and SetUID bits. Ignore
1399 * these errors.
1401 if (errno != EPERM)
1402 send_error();
1405 /* Also, try to preserve the timestamps */
1406 utb.actime = info.st_atime;
1407 utb.modtime = info.st_mtime;
1409 utime(safe_dest, &utb);
1413 g_free(safe_path);
1414 g_free(safe_dest);
1416 else if (S_ISLNK(info.st_mode))
1418 char *target;
1420 /* Not all versions of cp(1) can make symlinks,
1421 * so we special-case it.
1424 target = readlink_dup(path);
1425 if (target)
1427 if (symlink(target, dest_path))
1428 send_error();
1429 else
1430 send_check_path(dest_path);
1432 g_free(target);
1434 else
1435 send_error();
1437 else
1439 guchar *error;
1441 error = copy_file(path, dest_path);
1443 if (error)
1445 printf_send(_("!%s\nFailed to copy '%s'\n"),
1446 error, path);
1447 g_free(error);
1449 else
1450 send_check_path(dest_path);
1454 /* If action_leaf is not NULL it specifies the new leaf name */
1455 static void do_move2(const char *path, const char *dest)
1457 const char *dest_path;
1458 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1459 struct stat info2;
1460 gboolean is_dir;
1461 char *err;
1463 check_flags();
1465 dest_path = make_dest_path(path, dest);
1467 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1469 if (access(dest_path, F_OK) == 0)
1471 struct stat info;
1472 int err;
1474 if (mc_lstat(dest_path, &info))
1476 send_error();
1477 return;
1480 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1482 /* Newer; keep going */
1484 else
1486 printf_send("<%s", path);
1487 printf_send(">%s", dest_path);
1488 if (!printf_reply(from_parent, TRUE,
1489 _("?'%s' already exists - overwrite?"),
1490 dest_path))
1491 return;
1494 if (S_ISDIR(info.st_mode))
1495 err = rmdir(dest_path);
1496 else
1497 err = unlink(dest_path);
1499 if (err)
1501 send_error();
1502 if (errno != ENOENT)
1503 return;
1504 printf_send(_("'Trying move anyway...\n"));
1507 else if (!quiet)
1509 printf_send("<%s", path);
1510 printf_send(">");
1511 if (!printf_reply(from_parent, FALSE,
1512 _("?Move %s as %s?"), path, dest_path))
1513 return;
1515 else if (!o_brief)
1516 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1518 argv[2] = path;
1519 argv[3] = dest_path;
1521 err = fork_exec_wait(argv);
1522 if (err)
1524 printf_send(_("!%s\nFailed to move %s as %s\n"),
1525 err, path, dest_path);
1526 g_free(err);
1528 else
1530 send_check_path(dest_path);
1532 if (is_dir)
1533 send_mount_path(path);
1534 else
1535 send_check_path(path);
1539 /* Copy path to dest.
1540 * Check that path not copied into itself.
1542 static void do_copy(const char *path, const char *dest)
1544 if (is_sub_dir(make_dest_path(path, dest), path))
1545 printf_send(_("!ERROR: Can't copy object into itself\n"));
1546 else
1548 do_copy2(path, dest);
1549 send_check_path(dest);
1553 /* Move path to dest.
1554 * Check that path not moved into itself.
1556 static void do_move(const char *path, const char *dest)
1558 if (is_sub_dir(make_dest_path(path, dest), path))
1559 printf_send(
1560 _("!ERROR: Can't move/rename object into itself\n"));
1561 else
1563 do_move2(path, dest);
1564 send_check_path(dest);
1568 /* Common code for do_link_relative() and do_link_absolute(). */
1569 static void do_link(const char *path, const char *dest_path)
1571 if (quiet)
1572 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1573 else {
1574 printf_send("<%s", path);
1575 printf_send(">");
1576 if (!printf_reply(from_parent, FALSE,
1577 _("?Link %s as %s?"), path, dest_path))
1578 return;
1581 if (symlink(path, dest_path))
1582 send_error();
1583 else
1584 send_check_path(dest_path);
1587 static void do_link_relative(const char *path, const char *dest)
1589 char *rel_path;
1590 const char *dest_path;
1592 dest_path = make_dest_path(path, dest);
1594 check_flags();
1596 rel_path = get_relative_path(dest_path, path);
1597 do_link(rel_path, dest_path);
1598 g_free(rel_path);
1601 static void do_link_absolute(const char *path, const char *dest)
1603 check_flags();
1604 do_link(path, make_dest_path(path, dest));
1607 /* Mount/umount this item (depending on 'mount') */
1608 static void do_mount(const guchar *path, gboolean mount)
1610 const char *argv[3] = {NULL, NULL, NULL};
1611 char *err;
1613 check_flags();
1615 argv[0] = mount ? o_action_mount_command.value
1616 : o_action_umount_command.value;
1617 argv[1] = path;
1619 if (quiet)
1620 printf_send(mount ? _("'Mounting %s\n")
1621 : _("'Unmounting %s\n"),
1622 path);
1623 else if (!printf_reply(from_parent, FALSE,
1624 mount ? _("?Mount %s?")
1625 : _("?Unmount %s?"),
1626 path))
1627 return;
1629 if (!mount)
1631 char c = '?';
1632 /* Need to close all sub-directories now, or we
1633 * can't unmount if dnotify is used.
1635 printf_send("X%s", path);
1636 /* Wait until it's safe... */
1637 read(from_parent, &c, 1);
1638 g_return_if_fail(c == 'X');
1641 err = fork_exec_wait(argv);
1642 if (err)
1644 printf_send(mount ?
1645 _("!%s\nMount failed\n") :
1646 _("!%s\nUnmount failed\n"), err);
1647 g_free(err);
1649 /* Mount may have worked even on error, eg if we try to mount
1650 * a read-only disk read/write, it gets mounted read-only
1651 * with an error.
1653 if (mount && mount_is_mounted(path, NULL, NULL))
1654 printf_send(_("'(seems to be mounted now anyway)\n"));
1655 else
1656 return;
1659 printf_send("M%s", path);
1660 if (mount && mount_open_dir)
1661 printf_send("o%s", path);
1664 /* CHILD MAIN LOOPS */
1666 /* After forking, the child calls one of these functions */
1668 /* We use a double for total size in order to count beyond 4Gb */
1669 static void usage_cb(gpointer data)
1671 GList *paths = (GList *) data;
1672 double total_size = 0;
1673 int n, i, per;
1675 n=g_list_length(paths);
1676 dir_counter = file_counter = 0;
1678 for (i=0; paths; paths = paths->next, i++)
1680 guchar *path = (guchar *) paths->data;
1682 send_dir(path);
1684 size_tally = 0;
1686 if(n>1 && i>0)
1688 per=100*i/n;
1689 printf_send("%%%d", per);
1691 do_usage(path, NULL);
1693 printf_send("'%s: %s\n",
1694 g_basename(path),
1695 format_double_size(size_tally));
1696 total_size += size_tally;
1698 printf_send("%%-1");
1700 g_string_printf(message, _("'\nTotal: %s ("),
1701 format_double_size(total_size));
1703 if (file_counter)
1704 g_string_append_printf(message,
1705 "%ld %s%s", file_counter,
1706 file_counter == 1 ? _("file") : _("files"),
1707 dir_counter ? ", " : ")\n");
1709 if (file_counter == 0 && dir_counter == 0)
1710 g_string_append(message, _("no directories)\n"));
1711 else if (dir_counter)
1712 g_string_append_printf(message,
1713 "%ld %s)\n", dir_counter,
1714 dir_counter == 1 ? _("directory")
1715 : _("directories"));
1717 send_msg();
1720 #ifdef DO_MOUNT_POINTS
1721 static void mount_cb(gpointer data)
1723 GList *paths = (GList *) data;
1724 gboolean mount_points = FALSE;
1725 int n, i, per;
1727 n=g_list_length(paths);
1728 for (i=0; paths; paths = paths->next, i++)
1730 guchar *path = (guchar *) paths->data;
1731 guchar *target;
1733 target = pathdup(path);
1734 if (!target)
1735 target = path;
1737 if(n>1 && i>0)
1739 per=100*i/n;
1740 printf_send("%%%d", per);
1742 if (mount_is_mounted(target, NULL, NULL) ||
1743 g_hash_table_lookup(fstab_mounts, target))
1745 mount_points = TRUE;
1746 do_mount(target, mount_mount); /* Mount */
1749 if (target != path)
1750 g_free(target);
1753 if (mount_points)
1754 send_done();
1755 else
1756 printf_send(_("!No mount points selected!\n"));
1758 #endif
1760 /* (use g_dirname() instead?) */
1761 static guchar *dirname(guchar *path)
1763 guchar *slash;
1765 slash = strrchr(path, '/');
1766 g_return_val_if_fail(slash != NULL, g_strdup(path));
1768 if (slash != path)
1769 return g_strndup(path, slash - path);
1770 return g_strdup("/");
1773 static void delete_cb(gpointer data)
1775 GList *paths = (GList *) data;
1776 int n, i, per;
1778 n=g_list_length(paths);
1779 for (i=0; paths; paths = paths->next, i++)
1781 guchar *path = (guchar *) paths->data;
1782 guchar *dir;
1784 dir = dirname(path);
1785 send_dir(dir);
1787 if(n>1 && i>0)
1789 per=100*i/n;
1790 printf_send("%%%d", per);
1792 do_delete(path, dir);
1794 g_free(dir);
1797 send_done();
1800 static void eject_cb(gpointer data)
1802 GList *paths = (GList *) data;
1803 int n, i, per;
1805 n=g_list_length(paths);
1807 for (i=0; paths; paths = paths->next, i++)
1809 guchar *path = (guchar *) paths->data;
1811 if(n>1 && i>0)
1813 per=100*i/n;
1814 printf_send("%%%d", per);
1816 send_dir(path);
1818 do_eject(path);
1821 send_done();
1824 static void find_cb(gpointer data)
1826 GList *all_paths = (GList *) data;
1827 GList *paths;
1829 while (1)
1831 for (paths = all_paths; paths; paths = paths->next)
1833 guchar *path = (guchar *) paths->data;
1835 send_dir(path);
1837 do_find(path, NULL);
1840 if (!printf_reply(from_parent, TRUE,
1841 _("?Another search?")))
1842 break;
1843 printf_send("#");
1846 send_done();
1849 static void chmod_cb(gpointer data)
1851 GList *paths = (GList *) data;
1852 int n, i, per;
1854 n=g_list_length(paths);
1856 for (i=0; paths; paths = paths->next, i++)
1858 guchar *path = (guchar *) paths->data;
1859 struct stat info;
1861 if(n>1 && i>0)
1863 per=100*i/n;
1864 printf_send("%%%d", per);
1866 send_dir(path);
1868 if (mc_stat(path, &info) != 0)
1869 send_error();
1870 else if (S_ISLNK(info.st_mode))
1871 printf_send(_("!'%s' is a symbolic link\n"),
1872 g_basename(path));
1873 else
1874 do_chmod(path, NULL);
1877 send_done();
1880 static void settype_cb(gpointer data)
1882 GList *paths = (GList *) data;
1883 int n, i, per;
1885 n=g_list_length(paths);
1887 for (i=0; paths; paths = paths->next, i++)
1889 guchar *path = (guchar *) paths->data;
1890 struct stat info;
1892 if(n>1 && i>0)
1894 per=100*i/n;
1895 printf_send("%%%d", per);
1897 send_dir(path);
1899 if (mc_stat(path, &info) != 0)
1900 send_error();
1901 else if (S_ISLNK(info.st_mode))
1902 printf_send(_("!'%s' is a symbolic link\n"),
1903 g_basename(path));
1904 else
1905 do_settype(path, NULL);
1908 send_done();
1911 static void list_cb(gpointer data)
1913 GList *paths = (GList *) data;
1914 int n, i, per;
1916 n=g_list_length(paths);
1918 for (i=0; paths; paths = paths->next, i++)
1920 if(n>1 && i>0)
1922 per=100*i/n;
1923 printf_send("%%%d", per);
1925 send_dir((char *) paths->data);
1927 action_do_func((char *) paths->data, action_dest);
1930 send_done();
1933 /* EXTERNAL INTERFACE */
1935 void action_find(GList *paths)
1937 GUIside *gui_side;
1938 GtkWidget *abox;
1940 if (!paths)
1942 report_error(_("You need to select some items "
1943 "to search through"));
1944 return;
1947 if (!last_find_string)
1948 last_find_string = g_strdup("'core'");
1950 new_entry_string = last_find_string;
1952 abox = abox_new(_("Find"), FALSE);
1953 gui_side = start_action(abox, find_cb, paths,
1954 o_action_force.int_value,
1955 o_action_brief.int_value,
1956 o_action_recurse.int_value,
1957 o_action_newer.int_value);
1958 if (!gui_side)
1959 return;
1961 abox_add_results(ABOX(abox));
1963 gui_side->default_string = &last_find_string;
1964 abox_add_entry(ABOX(abox), last_find_string,
1965 new_help_button(show_condition_help, NULL));
1966 g_signal_connect(ABOX(abox)->entry, "changed",
1967 G_CALLBACK(entry_changed), gui_side);
1968 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1970 gui_side->show_info = TRUE;
1971 gui_side->entry_string_func = set_find_string_colour;
1973 number_of_windows++;
1974 gtk_widget_show(abox);
1977 /* Count disk space used by selected items */
1978 void action_usage(GList *paths)
1980 GUIside *gui_side;
1981 GtkWidget *abox;
1983 if (!paths)
1985 report_error(_("You need to select some items to count"));
1986 return;
1989 abox = abox_new(_("Disk Usage"), TRUE);
1990 if(paths && paths->next)
1991 abox_set_percentage(ABOX(abox), 0);
1993 gui_side = start_action(abox, usage_cb, paths,
1994 o_action_force.int_value,
1995 o_action_brief.int_value,
1996 o_action_recurse.int_value,
1997 o_action_newer.int_value);
1998 if (!gui_side)
1999 return;
2001 gui_side->show_info = TRUE;
2003 number_of_windows++;
2005 gtk_widget_show(abox);
2008 /* Mount/unmount listed items (paths).
2009 * Free the list after this function returns.
2010 * If open_dir is TRUE and the dir is successfully mounted, open it.
2011 * quiet can be -1 for default.
2013 void action_mount(GList *paths, gboolean open_dir, gboolean mount, int quiet)
2015 #ifdef DO_MOUNT_POINTS
2016 GUIside *gui_side;
2017 GtkWidget *abox;
2019 if (quiet == -1)
2020 quiet = o_action_mount.int_value;
2022 mount_open_dir = open_dir;
2023 mount_mount = mount;
2025 abox = abox_new(_("Mount / Unmount"), quiet);
2026 if(paths && paths->next)
2027 abox_set_percentage(ABOX(abox), 0);
2028 gui_side = start_action(abox, mount_cb, paths,
2029 o_action_force.int_value,
2030 o_action_brief.int_value,
2031 o_action_recurse.int_value,
2032 o_action_newer.int_value);
2033 if (!gui_side)
2034 return;
2036 number_of_windows++;
2037 gtk_widget_show(abox);
2038 #else
2039 report_error(
2040 _("ROX-Filer does not yet support mount points on your "
2041 "system. Sorry."));
2042 #endif /* DO_MOUNT_POINTS */
2045 /* Delete these paths */
2046 void action_delete(GList *paths)
2048 GUIside *gui_side;
2049 GtkWidget *abox;
2051 if (!remove_pinned_ok(paths))
2052 return;
2054 abox = abox_new(_("Delete"), o_action_delete.int_value);
2055 if(paths && paths->next)
2056 abox_set_percentage(ABOX(abox), 0);
2057 gui_side = start_action(abox, delete_cb, paths,
2058 o_action_force.int_value,
2059 o_action_brief.int_value,
2060 o_action_recurse.int_value,
2061 o_action_newer.int_value);
2062 if (!gui_side)
2063 return;
2065 abox_add_flag(ABOX(abox),
2066 _("Force"), _("Don't confirm deletion of non-writeable items"),
2067 'F', o_action_force.int_value);
2068 abox_add_flag(ABOX(abox),
2069 _("Brief"), _("Only log directories being deleted"),
2070 'B', o_action_brief.int_value);
2072 number_of_windows++;
2073 gtk_widget_show(abox);
2076 /* Change the permissions of the selected items */
2077 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2079 GtkWidget *abox;
2080 GUIside *gui_side;
2081 static GList *presets = NULL;
2082 gboolean recurse = force_recurse || o_action_recurse.int_value;
2084 if (!paths)
2086 report_error(_("You need to select the items "
2087 "whose permissions you want to change"));
2088 return;
2091 if (!presets)
2093 presets = g_list_append(presets, (gchar *)
2094 _("a+x (Make executable/searchable)"));
2095 presets = g_list_append(presets, (gchar *)
2096 _("a-x (Make non-executable/non-searchable)"));
2097 presets = g_list_append(presets, (gchar *)
2098 _("u+rw (Give owner read+write)"));
2099 presets = g_list_append(presets, (gchar *)
2100 _("go-rwx (Private - owner access only)"));
2101 presets = g_list_append(presets, (gchar *)
2102 _("go=u-w (Public access, not write)"));
2105 if (!last_chmod_string)
2106 last_chmod_string = g_strdup((guchar *) presets->data);
2108 if (action)
2109 new_entry_string = g_strdup(action);
2110 else
2111 new_entry_string = g_strdup(last_chmod_string);
2113 abox = abox_new(_("Permissions"), FALSE);
2114 if(paths && paths->next)
2115 abox_set_percentage(ABOX(abox), 0);
2116 gui_side = start_action(abox, chmod_cb, paths,
2117 o_action_force.int_value,
2118 o_action_brief.int_value,
2119 recurse,
2120 o_action_newer.int_value);
2122 if (!gui_side)
2123 goto out;
2125 abox_add_flag(ABOX(abox),
2126 _("Brief"), _("Don't list processed files"),
2127 'B', o_action_brief.int_value);
2128 abox_add_flag(ABOX(abox),
2129 _("Recurse"), _("Also change contents of subdirectories"),
2130 'R', recurse);
2132 gui_side->default_string = &last_chmod_string;
2133 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2134 new_help_button(show_chmod_help, NULL));
2136 g_signal_connect(ABOX(abox)->entry, "changed",
2137 G_CALLBACK(entry_changed), gui_side);
2138 #if 0
2139 g_signal_connect_swapped(gui_side->entry, "activate",
2140 G_CALLBACK(gtk_button_clicked),
2141 gui_side->yes);
2142 #endif
2144 number_of_windows++;
2145 gtk_widget_show(abox);
2147 out:
2148 null_g_free(&new_entry_string);
2151 /* Set the MIME type of the selected items */
2152 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2154 GtkWidget *abox;
2155 GUIside *gui_side;
2156 GList *presets = NULL;
2157 gboolean recurse = force_recurse || o_action_recurse.int_value;
2159 if (!paths)
2161 report_error(_("You need to select the items "
2162 "whose type you want to change"));
2163 return;
2166 if (!last_settype_string)
2167 last_settype_string = g_strdup("text/plain");
2169 if (oldtype)
2170 new_entry_string = g_strdup(oldtype);
2171 else
2172 new_entry_string = g_strdup(last_settype_string);
2174 abox = abox_new(_("Set type"), FALSE);
2175 if(paths && paths->next)
2176 abox_set_percentage(ABOX(abox), 0);
2177 gui_side = start_action(abox, settype_cb, paths,
2178 o_action_force.int_value,
2179 o_action_brief.int_value,
2180 recurse,
2181 o_action_newer.int_value);
2183 if (!gui_side)
2184 goto out;
2186 abox_add_flag(ABOX(abox),
2187 _("Brief"), _("Don't list processed files"),
2188 'B', o_action_brief.int_value);
2189 abox_add_flag(ABOX(abox),
2190 _("Recurse"), _("Change contents of subdirectories"),
2191 'R', recurse);
2193 gui_side->default_string = &last_settype_string;
2195 /* Note: get the list again each time -- it can change */
2196 presets = mime_type_name_list();
2197 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2198 new_help_button(show_settype_help, NULL));
2199 g_list_free(presets);
2201 g_signal_connect(ABOX(abox)->entry, "changed",
2202 G_CALLBACK(entry_changed), gui_side);
2204 number_of_windows++;
2205 gtk_widget_show(abox);
2207 out:
2208 null_g_free(&new_entry_string);
2211 /* If leaf is NULL then the copy has the same name as the original.
2212 * quiet can be -1 for default.
2214 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2216 GUIside *gui_side;
2217 GtkWidget *abox;
2219 if (quiet == -1)
2220 quiet = o_action_copy.int_value;
2222 action_dest = dest;
2223 action_leaf = leaf;
2224 action_do_func = do_copy;
2226 abox = abox_new(_("Copy"), quiet);
2227 if(paths && paths->next)
2228 abox_set_percentage(ABOX(abox), 0);
2229 gui_side = start_action(abox, list_cb, paths,
2230 o_action_force.int_value,
2231 o_action_brief.int_value,
2232 o_action_recurse.int_value,
2233 o_action_newer.int_value);
2234 if (!gui_side)
2235 return;
2237 abox_add_flag(ABOX(abox),
2238 _("Newer"),
2239 _("Only over-write if source is newer than destination."),
2240 'W', o_action_newer.int_value);
2241 abox_add_flag(ABOX(abox),
2242 _("Brief"), _("Only log directories as they are copied"),
2243 'B', o_action_brief.int_value);
2245 number_of_windows++;
2246 gtk_widget_show(abox);
2249 /* If leaf is NULL then the file is not renamed.
2250 * quiet can be -1 for default.
2252 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2254 GUIside *gui_side;
2255 GtkWidget *abox;
2257 if (quiet == -1)
2258 quiet = o_action_move.int_value;
2260 action_dest = dest;
2261 action_leaf = leaf;
2262 action_do_func = do_move;
2264 abox = abox_new(_("Move"), quiet);
2265 if(paths && paths->next)
2266 abox_set_percentage(ABOX(abox), 0);
2267 gui_side = start_action(abox, list_cb, paths,
2268 o_action_force.int_value,
2269 o_action_brief.int_value,
2270 o_action_recurse.int_value,
2271 o_action_newer.int_value);
2272 if (!gui_side)
2273 return;
2275 abox_add_flag(ABOX(abox),
2276 _("Newer"),
2277 _("Only over-write if source is newer than destination."),
2278 'W', o_action_newer.int_value);
2279 abox_add_flag(ABOX(abox),
2280 _("Brief"), _("Don't log each file as it is moved"),
2281 'B', o_action_brief.int_value);
2282 number_of_windows++;
2283 gtk_widget_show(abox);
2286 /* If leaf is NULL then the link will have the same name */
2287 void action_link(GList *paths, const char *dest, const char *leaf,
2288 gboolean relative)
2290 GtkWidget *abox;
2291 GUIside *gui_side;
2293 action_dest = dest;
2294 action_leaf = leaf;
2295 if (relative)
2296 action_do_func = do_link_relative;
2297 else
2298 action_do_func = do_link_absolute;
2300 abox = abox_new(_("Link"), o_action_link.int_value);
2301 if(paths && paths->next)
2302 abox_set_percentage(ABOX(abox), 0);
2303 gui_side = start_action(abox, list_cb, paths,
2304 o_action_force.int_value,
2305 o_action_brief.int_value,
2306 o_action_recurse.int_value,
2307 o_action_newer.int_value);
2308 if (!gui_side)
2309 return;
2311 number_of_windows++;
2312 gtk_widget_show(abox);
2315 /* Eject these paths */
2316 void action_eject(GList *paths)
2318 GUIside *gui_side;
2319 GtkWidget *abox;
2321 abox = abox_new(_("Eject"), TRUE);
2322 if(paths && paths->next)
2323 abox_set_percentage(ABOX(abox), 0);
2324 gui_side = start_action(abox, eject_cb, paths,
2325 o_action_force.int_value,
2326 o_action_brief.int_value,
2327 o_action_recurse.int_value,
2328 o_action_newer.int_value);
2329 if (!gui_side)
2330 return;
2332 number_of_windows++;
2333 gtk_widget_show(abox);
2336 void action_init(void)
2338 option_add_int(&o_action_copy, "action_copy", 1);
2339 option_add_int(&o_action_move, "action_move", 1);
2340 option_add_int(&o_action_link, "action_link", 1);
2341 option_add_int(&o_action_delete, "action_delete", 0);
2342 option_add_int(&o_action_mount, "action_mount", 1);
2343 option_add_int(&o_action_force, "action_force", FALSE);
2344 option_add_int(&o_action_brief, "action_brief", FALSE);
2345 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2346 option_add_int(&o_action_newer, "action_newer", FALSE);
2348 option_add_string(&o_action_mount_command,
2349 "action_mount_command", "mount");
2350 option_add_string(&o_action_umount_command,
2351 "action_umount_command", "umount");
2354 #define MAX_ASK 4
2356 /* Check to see if any of the selected items (or their children) are
2357 * on the pinboard or panel. If so, ask for confirmation.
2359 * TRUE if it's OK to lose them.
2361 static gboolean remove_pinned_ok(GList *paths)
2363 GList *ask = NULL, *next;
2364 GString *message;
2365 int i, ask_n = 0;
2366 gboolean retval;
2368 for (; paths; paths = paths->next)
2370 guchar *path = (guchar *) paths->data;
2372 if (icons_require(path))
2374 if (++ask_n > MAX_ASK)
2375 break;
2376 ask = g_list_append(ask, path);
2380 if (!ask)
2381 return TRUE;
2383 if (ask_n > MAX_ASK)
2385 message = g_string_new(_("Deleting items such as "));
2386 ask_n--;
2388 else if (ask_n == 1)
2389 message = g_string_new(_("Deleting the item "));
2390 else
2391 message = g_string_new(_("Deleting the items "));
2393 i = 0;
2394 for (next = ask; next; next = next->next)
2396 guchar *path = (guchar *) next->data;
2397 guchar *leaf;
2399 leaf = strrchr(path, '/');
2400 if (leaf)
2401 leaf++;
2402 else
2403 leaf = path;
2405 g_string_append_c(message, '`');
2406 g_string_append(message, leaf);
2407 g_string_append_c(message, '\'');
2408 i++;
2409 if (i == ask_n - 1 && i > 0)
2410 g_string_append(message, _(" and "));
2411 else if (i < ask_n)
2412 g_string_append(message, ", ");
2415 g_list_free(ask);
2417 if (ask_n == 1)
2418 message = g_string_append(message,
2419 _(" will affect some items on the pinboard "
2420 "or panel - really delete it?"));
2421 else
2423 if (ask_n > MAX_ASK)
2424 message = g_string_append_c(message, ',');
2425 message = g_string_append(message,
2426 _(" will affect some items on the pinboard "
2427 "or panel - really delete them?"));
2430 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2432 g_string_free(message, TRUE);
2434 return retval;
2437 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2439 FindCondition *cond;
2441 cond = find_compile(string);
2442 entry_set_error(widget, !cond);
2444 find_condition_free(cond);