r4474: Added options to change the "mount" and "umount" commands. This may be useful
[rox-filer/ma.git] / ROX-Filer / src / action.c
blob9178c3b127e2d722d455f2ff228a6141a67e1f6f
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 gboolean mount_mount = FALSE; /* (FALSE => unmount) */
101 static int from_parent = 0;
102 static FILE *to_parent = NULL;
103 static gboolean quiet = FALSE;
104 static GString *message = NULL;
105 static const char *action_dest = NULL;
106 static const char *action_leaf = NULL;
107 static void (*action_do_func)(const char *source, const char *dest);
108 static double size_tally; /* For Disk Usage */
109 static unsigned long dir_counter; /* For Disk Usage */
110 static unsigned long file_counter; /* For Disk Usage */
112 static struct mode_change *mode_change = NULL; /* For Permissions */
113 static FindCondition *find_condition = NULL; /* For Find */
114 static MIME_type *type_change = NULL;
116 /* Only used by child */
117 static gboolean o_force = FALSE;
118 static gboolean o_brief = FALSE;
119 static gboolean o_recurse = FALSE;
120 static gboolean o_newer = FALSE;
122 static Option o_action_copy, o_action_move, o_action_link;
123 static Option o_action_delete, o_action_mount;
124 static Option o_action_force, o_action_brief, o_action_recurse;
125 static Option o_action_newer;
127 static Option o_action_mount_command;
128 static Option o_action_umount_command;
130 /* Whenever the text in these boxes is changed we store a copy of the new
131 * string to be used as the default next time.
133 static guchar *last_chmod_string = NULL;
134 static guchar *last_find_string = NULL;
135 static guchar *last_settype_string = NULL;
137 /* Set to one of the above before forking. This may change over a call to
138 * reply(). It is reset to NULL once the text is parsed.
140 static guchar *new_entry_string = NULL;
142 /* Static prototypes */
143 static void send_done(void);
144 static void send_check_path(const gchar *path);
145 static void send_mount_path(const gchar *path);
146 static gboolean printf_send(const char *msg, ...);
147 static gboolean send_msg(void);
148 static gboolean send_error(void);
149 static gboolean send_dir(const char *dir);
150 static gboolean read_exact(int source, char *buffer, ssize_t len);
151 static void do_mount(const guchar *path, gboolean mount);
152 static gboolean printf_reply(int fd, gboolean ignore_quiet,
153 const char *msg, ...);
154 static gboolean remove_pinned_ok(GList *paths);
156 /* SUPPORT */
159 /* This is called whenever the user edits the entry box (if any) - send the
160 * new string.
162 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
164 guchar *text;
166 g_return_if_fail(gui_side->default_string != NULL);
168 text = gtk_editable_get_chars(entry, 0, -1);
170 if (gui_side->entry_string_func)
171 gui_side->entry_string_func(GTK_WIDGET(entry), text);
173 g_free(*(gui_side->default_string));
174 *(gui_side->default_string) = text; /* Gets text's ref */
176 if (!gui_side->to_child)
177 return;
179 fputc('E', gui_side->to_child);
180 fputs(text, gui_side->to_child);
181 fputc('\n', gui_side->to_child);
182 fflush(gui_side->to_child);
185 void show_condition_help(gpointer data)
187 GtkWidget *help;
188 GtkWidget *text;
190 help = gtk_dialog_new_with_buttons(
191 _("Find expression reference"),
192 NULL, 0,
193 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
194 NULL);
195 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
197 text = gtk_label_new(NULL);
198 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
199 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
200 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
201 gtk_label_set_markup(GTK_LABEL(text), _(
202 "<u>Quick Start</u>\n"
203 "Just put the name of the file you're looking for in single quotes:\n"
204 "<b>'index.html'</b> (to find a file called 'index.html')\n"
205 "\n"
206 "<u>Examples</u>\n"
207 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
208 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
209 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
210 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
211 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
212 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
213 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
214 "\n"
215 "<u>Simple Tests</u>\n"
216 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
217 "(types)\n"
218 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
219 "(permissions)\n"
220 "<b>IsEmpty, IsMine</b>\n"
221 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
222 "contains a slash then the match is against the full path; otherwise it is\n"
223 "against the leafname only.\n"
224 "\n"
225 "<u>Comparisons</u>\n"
226 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
227 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
228 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
229 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
230 "(values)\n"
231 "\n"
232 "<u>Specials</u>\n"
233 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
234 "a % in 'command' is replaced with the path of the current file)\n"
235 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
237 g_signal_connect(help, "response",
238 G_CALLBACK(gtk_widget_destroy), NULL);
240 gtk_widget_show_all(help);
243 static void show_chmod_help(gpointer data)
245 GtkWidget *help;
246 GtkWidget *text;
248 help = gtk_dialog_new_with_buttons(
249 _("Change permissions reference"),
250 NULL, 0,
251 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
252 NULL);
253 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
255 text = gtk_label_new(NULL);
256 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
257 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
258 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
259 gtk_label_set_markup(GTK_LABEL(text), _(
260 "Normally, you can just select a command from the menu (click \n"
261 "on the arrow beside the command box). Sometimes, you need more...\n"
262 "\n"
263 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
264 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
265 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
266 "determines whether to\n"
267 "change the permissions for the User (owner), Group or Others.\n"
268 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
269 "exactly the permissions.\n"
270 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
271 "\n"
272 "Bracketed text and spaces are ignored.\n"
273 "\n"
274 "<u>Examples</u>\n"
275 "<b>u+rw</b>: the file owner gains read and write permission\n"
276 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
277 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
278 "write permission\n"
279 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
280 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
281 "executable by anyone become executable by everyone\n"
282 "<b>u+rw, go+r</b>: two commands at once!\n"
283 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
284 "<b>755</b>: set the permissions directly\n"
285 "\n"
286 "See the chmod(1) man page for full details."));
288 g_signal_connect(help, "response",
289 G_CALLBACK(gtk_widget_destroy), NULL);
291 gtk_widget_show_all(help);
295 static void show_settype_help(gpointer data)
297 GtkWidget *help;
298 GtkWidget *text;
300 help = gtk_dialog_new_with_buttons(
301 _("Set type reference"),
302 NULL, 0,
303 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
304 NULL);
305 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
307 text = gtk_label_new(NULL);
308 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
309 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
310 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
311 gtk_label_set_markup(GTK_LABEL(text), _(
312 "Normally ROX-Filer determines the type of a regular file\n"
313 "by matching it's name against a pattern. To change the\n"
314 "type of the file you must rename it.\n"
315 "\n"
316 "Newer file systems can support something called 'Extended\n"
317 "Attributes' which can be used to store additional data with\n"
318 "each file as named parameters. ROX-Filer uses the\n"
319 "'user.mime_type' attribute to store file types.\n"
320 "\n"
321 "File types are only supported for regular files, not\n"
322 "directories, devices, pipes or sockets, and then only\n"
323 "on certain file systems and where the OS implements them.\n"));
325 text = gtk_label_new(_(ATTR_MAN_PAGE));
326 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
327 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
329 g_signal_connect(help, "response",
330 G_CALLBACK(gtk_widget_destroy), NULL);
332 gtk_widget_show_all(help);
335 static void process_message(GUIside *gui_side, const gchar *buffer)
337 ABox *abox = gui_side->abox;
339 if (*buffer == '?')
340 abox_ask(abox, buffer + 1);
341 else if (*buffer == 's')
342 dir_check_this(buffer + 1); /* Update this item */
343 else if (*buffer == '=')
344 abox_add_filename(abox, buffer + 1);
345 else if (*buffer == '#')
346 abox_clear_results(abox);
347 else if (*buffer == 'X')
349 filer_close_recursive(buffer + 1);
350 /* Let child know it's safe to continue... */
351 fputc('X', gui_side->to_child);
352 fflush(gui_side->to_child);
354 else if (*buffer == 'm' || *buffer == 'M')
356 /* Mount / major changes to this path */
357 if (*buffer == 'M')
359 mount_update(TRUE);
360 mount_user_mount(buffer + 1);
362 filer_check_mounted(buffer + 1);
364 else if (*buffer == '/')
365 abox_set_current_object(abox, buffer + 1);
366 else if (*buffer == 'o')
367 filer_opendir(buffer + 1, NULL, NULL);
368 else if (*buffer == '!')
370 gui_side->errors++;
371 abox_log(abox, buffer + 1, "error");
373 else if (*buffer == '<')
374 abox_set_file(abox, 0, buffer+1);
375 else if (*buffer == '>')
377 abox_set_file(abox, 1, buffer+1);
378 abox_show_compare(abox, TRUE);
380 else if (*buffer == '%')
382 abox_set_percentage(abox, atoi(buffer+1));
384 else
385 abox_log(abox, buffer + 1, NULL);
388 /* Called when the child sends us a message */
389 static void message_from_child(gpointer data,
390 gint source,
391 GdkInputCondition condition)
393 char buf[5];
394 GUIside *gui_side = (GUIside *) data;
395 ABox *abox = gui_side->abox;
396 GtkTextBuffer *text_buffer;
398 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
400 if (read_exact(source, buf, 4))
402 ssize_t message_len;
403 char *buffer;
405 buf[4] = '\0';
406 message_len = strtol(buf, NULL, 16);
407 buffer = g_malloc(message_len + 1);
408 if (message_len > 0 && read_exact(source, buffer, message_len))
410 buffer[message_len] = '\0';
411 process_message(gui_side, buffer);
412 g_free(buffer);
413 return;
415 g_printerr("Child died in the middle of a message.\n");
418 if (gui_side->abort_attempts)
419 abox_log(abox, _("\nProcess terminated.\n"), "error");
421 /* The child is dead */
422 gui_side->child = 0;
424 fclose(gui_side->to_child);
425 gui_side->to_child = NULL;
426 close(gui_side->from_child);
427 g_source_remove(gui_side->input_tag);
428 abox_cancel_ask(gui_side->abox);
430 if (gui_side->errors)
432 guchar *report;
434 if (gui_side->errors == 1)
435 report = g_strdup(_("There was one error.\n"));
436 else
437 report = g_strdup_printf(_("There were %d errors.\n"),
438 gui_side->errors);
440 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
442 g_free(report);
444 else if (gui_side->show_info == FALSE)
445 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
448 /* Scans src_dir, calling cb(item, dest_path) for each item */
449 static void for_dir_contents(ForDirCB *cb,
450 const char *src_dir,
451 const char *dest_path)
453 DIR *d;
454 struct dirent *ent;
455 GList *list = NULL, *next;
457 d = mc_opendir(src_dir);
458 if (!d)
460 /* Message displayed is "ERROR reading 'path': message" */
461 printf_send("!%s '%s': %s\n", _("ERROR reading"),
462 src_dir, g_strerror(errno));
463 return;
466 send_dir(src_dir);
468 while ((ent = mc_readdir(d)))
470 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
471 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
472 continue;
473 list = g_list_prepend(list, g_strdup(make_path(src_dir,
474 ent->d_name)));
476 mc_closedir(d);
478 for (next = list; next; next = next->next)
480 cb((char *) next->data, dest_path);
482 g_free(next->data);
484 g_list_free(list);
487 /* Read this many bytes into the buffer. TRUE on success. */
488 static gboolean read_exact(int source, char *buffer, ssize_t len)
490 while (len > 0)
492 ssize_t got;
493 got = read(source, buffer, len);
494 if (got < 1)
495 return FALSE;
496 len -= got;
497 buffer += got;
499 return TRUE;
502 static void send_done(void)
504 printf_send(_("'\nDone\n"));
507 /* Notify the filer that this item has been updated */
508 static void send_check_path(const gchar *path)
510 printf_send("s%s", path);
513 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
514 static void send_mount_path(const gchar *path)
516 printf_send("m%s", path);
519 /* Send a message to the filer process. The first character indicates the
520 * type of the message.
522 static gboolean printf_send(const char *msg, ...)
524 va_list args;
525 gchar *tmp;
527 va_start(args, msg);
528 tmp = g_strdup_vprintf(msg, args);
529 va_end(args);
531 g_string_assign(message, tmp);
532 g_free(tmp);
534 return send_msg();
537 /* Send 'message' to our parent process. TRUE on success. */
538 static gboolean send_msg(void)
540 char len_buffer[5];
541 ssize_t len;
543 g_return_val_if_fail(message->len < 0xffff, FALSE);
545 sprintf(len_buffer, "%04" G_GSIZE_MODIFIER "x", message->len);
546 fwrite(len_buffer, 1, 4, to_parent);
547 len = fwrite(message->str, 1, message->len, to_parent);
548 fflush(to_parent);
549 return len == (ssize_t) message->len;
552 /* Set the directory indicator at the top of the window */
553 static gboolean send_dir(const char *dir)
555 return printf_send("/%s", dir);
558 static gboolean send_error(void)
560 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
563 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
565 gchar code;
567 if (!gui_side->to_child)
568 return;
570 if (response == GTK_RESPONSE_YES)
571 code = 'Y';
572 else if (response == GTK_RESPONSE_NO)
573 code = 'N';
574 else
575 return;
577 fputc(code, gui_side->to_child);
578 fflush(gui_side->to_child);
579 abox_show_compare(gui_side->abox, FALSE);
582 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
584 if (!gui_side->to_child)
585 return;
587 fputc(flag, gui_side->to_child);
588 fflush(gui_side->to_child);
591 static void read_new_entry_text(void)
593 int len;
594 char c;
595 GString *new;
597 new = g_string_new(NULL);
599 for (;;)
601 len = read(from_parent, &c, 1);
602 if (len != 1)
604 fprintf(stderr, "read() error: %s\n",
605 g_strerror(errno));
606 _exit(1); /* Parent died? */
609 if (c == '\n')
610 break;
611 g_string_append_c(new, c);
614 g_free(new_entry_string);
615 new_entry_string = new->str;
616 g_string_free(new, FALSE);
619 static void process_flag(char flag)
621 switch (flag)
623 case 'Q':
624 quiet = !quiet;
625 break;
626 case 'F':
627 o_force = !o_force;
628 break;
629 case 'R':
630 o_recurse = !o_recurse;
631 break;
632 case 'B':
633 o_brief = !o_brief;
634 break;
635 case 'W':
636 o_newer = !o_newer;
637 break;
638 case 'E':
639 read_new_entry_text();
640 break;
641 default:
642 printf_send("!ERROR: Bad message '%c'\n", flag);
643 break;
647 /* If the parent has sent any flag toggles, read them */
648 static void check_flags(void)
650 fd_set set;
651 int got;
652 char retval;
653 struct timeval tv;
655 FD_ZERO(&set);
657 while (1)
659 FD_SET(from_parent, &set);
660 tv.tv_sec = 0;
661 tv.tv_usec = 0;
662 got = select(from_parent + 1, &set, NULL, NULL, &tv);
664 if (got == -1)
665 g_error("select() failed: %s\n", g_strerror(errno));
666 else if (!got)
667 return;
669 got = read(from_parent, &retval, 1);
670 if (got != 1)
671 g_error("read() error: %s\n", g_strerror(errno));
673 process_flag(retval);
677 /* Read until the user sends a reply. If ignore_quiet is TRUE then
678 * the user MUST click Yes or No, else treat quiet on as Yes.
679 * If the user needs prompting then does send_msg().
681 static gboolean printf_reply(int fd, gboolean ignore_quiet,
682 const char *msg, ...)
684 ssize_t len;
685 char retval;
686 va_list args;
687 gchar *tmp;
689 if (quiet && !ignore_quiet)
690 return TRUE;
692 va_start(args, msg);
693 tmp = g_strdup_vprintf(msg, args);
694 va_end(args);
696 g_string_assign(message, tmp);
697 g_free(tmp);
699 send_msg();
701 while (1)
703 len = read(fd, &retval, 1);
704 if (len != 1)
706 fprintf(stderr, "read() error: %s\n",
707 g_strerror(errno));
708 _exit(1); /* Parent died? */
711 switch (retval)
713 case 'Y':
714 printf_send("' %s\n", _("Yes"));
715 return TRUE;
716 case 'N':
717 printf_send("' %s\n", _("No"));
718 return FALSE;
719 default:
720 process_flag(retval);
721 break;
726 static void abort_operation(GtkWidget *widget, gpointer data)
728 GUIside *gui_side = (GUIside *) data;
730 if (gui_side->child)
732 if (gui_side->abort_attempts == 0)
734 abox_log(ABOX(widget),
735 _("\nAsking child process to terminate...\n"),
736 "error");
737 kill(-gui_side->child, SIGTERM);
739 else
741 abox_log(ABOX(widget),
742 _("\nTrying to KILL run-away process...\n"),
743 "error");
744 kill(-gui_side->child, SIGKILL);
745 kill(-gui_side->child, SIGCONT);
747 gui_side->abort_attempts++;
749 else
750 gtk_widget_destroy(widget);
753 static void destroy_action_window(GtkWidget *widget, gpointer data)
755 GUIside *gui_side = (GUIside *) data;
757 if (gui_side->child)
759 kill(-gui_side->child, SIGTERM);
760 fclose(gui_side->to_child);
761 close(gui_side->from_child);
762 g_source_remove(gui_side->input_tag);
765 g_free(gui_side);
767 one_less_window();
770 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
771 * (NULL on failure). The child calls func().
773 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
774 int force, int brief, int recurse, int newer)
776 gboolean autoq;
777 int filedes[4]; /* 0 and 2 are for reading */
778 GUIside *gui_side;
779 pid_t child;
780 struct sigaction act;
782 if (pipe(filedes))
784 report_error("pipe: %s", g_strerror(errno));
785 gtk_widget_destroy(abox);
786 return NULL;
789 if (pipe(filedes + 2))
791 close(filedes[0]);
792 close(filedes[1]);
793 report_error("pipe: %s", g_strerror(errno));
794 gtk_widget_destroy(abox);
795 return NULL;
798 autoq = gtk_toggle_button_get_active(
799 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
801 o_force = force;
802 o_brief = brief;
803 o_recurse = recurse;
804 o_newer = newer;
806 child = fork();
807 switch (child)
809 case -1:
810 report_error("fork: %s", g_strerror(errno));
811 gtk_widget_destroy(abox);
812 return NULL;
813 case 0:
814 /* We are the child */
816 /* Create a new process group */
817 setpgid(0, 0);
819 quiet = autoq;
821 dir_drop_all_dnotifies();
823 /* Reset the SIGCHLD handler */
824 act.sa_handler = SIG_DFL;
825 sigemptyset(&act.sa_mask);
826 act.sa_flags = 0;
827 sigaction(SIGCHLD, &act, NULL);
829 message = g_string_new(NULL);
830 close(filedes[0]);
831 close(filedes[3]);
832 to_parent = fdopen(filedes[1], "wb");
833 from_parent = filedes[2];
834 func(data);
835 send_dir("");
836 _exit(0);
839 /* We are the parent */
840 close(filedes[1]);
841 close(filedes[2]);
842 gui_side = g_new(GUIside, 1);
843 gui_side->from_child = filedes[0];
844 gui_side->to_child = fdopen(filedes[3], "wb");
845 gui_side->child = child;
846 gui_side->errors = 0;
847 gui_side->show_info = FALSE;
848 gui_side->default_string = NULL;
849 gui_side->entry_string_func = NULL;
850 gui_side->abort_attempts = 0;
852 gui_side->abox = ABOX(abox);
853 g_signal_connect(abox, "destroy",
854 G_CALLBACK(destroy_action_window), gui_side);
856 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
857 g_signal_connect(abox, "flag_toggled",
858 G_CALLBACK(flag_toggled), gui_side);
859 g_signal_connect(abox, "abort_operation",
860 G_CALLBACK(abort_operation), gui_side);
862 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
863 GDK_INPUT_READ,
864 message_from_child,
865 gui_side, NULL);
867 return gui_side;
870 /* ACTIONS ON ONE ITEM */
872 /* These may call themselves recursively, or ask questions, etc */
874 /* Updates the global size_tally, file_counter and dir_counter */
875 static void do_usage(const char *src_path, const char *unused)
877 struct stat info;
879 check_flags();
881 if (mc_lstat(src_path, &info))
883 printf_send("'%s:\n", src_path);
884 send_error();
886 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
888 file_counter++;
889 size_tally += info.st_size;
891 else if (S_ISDIR(info.st_mode))
893 dir_counter++;
894 if (printf_reply(from_parent, FALSE,
895 _("?Count contents of %s?"), src_path))
897 char *safe_path;
898 safe_path = g_strdup(src_path);
899 for_dir_contents(do_usage, safe_path, safe_path);
900 g_free(safe_path);
903 else
904 file_counter++;
907 /* dest_path is the dir containing src_path */
908 static void do_delete(const char *src_path, const char *unused)
910 struct stat info;
911 gboolean write_prot;
912 char *safe_path;
914 check_flags();
916 if (mc_lstat(src_path, &info))
918 send_error();
919 return;
922 write_prot = S_ISLNK(info.st_mode) ? FALSE
923 : access(src_path, W_OK) != 0;
924 if (write_prot || !quiet)
926 int res;
928 printf_send("<%s", src_path);
929 printf_send(">");
930 res=printf_reply(from_parent, write_prot && !o_force,
931 _("?Delete %s'%s'?"),
932 write_prot ? _("WRITE-PROTECTED ") : "",
933 src_path);
934 printf_send("<");
935 if (!res)
936 return;
938 else if (!o_brief)
939 printf_send(_("'Deleting '%s'\n"), src_path);
941 safe_path = g_strdup(src_path);
943 if (S_ISDIR(info.st_mode))
945 for_dir_contents(do_delete, safe_path, safe_path);
946 if (rmdir(safe_path))
948 g_free(safe_path);
949 send_error();
950 return;
952 printf_send(_("'Directory '%s' deleted\n"), safe_path);
953 send_mount_path(safe_path);
955 else if (unlink(src_path))
956 send_error();
957 else
959 send_check_path(safe_path);
960 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
962 gchar *dir;
963 dir = g_path_get_dirname(safe_path);
964 send_check_path(dir);
965 g_free(dir);
969 g_free(safe_path);
972 static void do_eject(const char *path)
974 const char *argv[3] = {NULL, NULL, NULL};
975 char *err;
977 check_flags();
979 if (!quiet)
981 int res;
982 printf_send("<%s", path);
983 printf_send(">");
984 res=printf_reply(from_parent, !o_force,
985 _("?Eject '%s'?"),
986 path);
987 printf_send("<");
988 if (!res)
989 return;
991 else if (!o_brief)
992 printf_send(_("'Eject '%s'\n"), path);
994 /* Need to close all sub-directories now, or we
995 * can't unmount if dnotify is used.
998 char c = '?';
999 printf_send("X%s", path);
1000 /* Wait until it's safe... */
1001 read(from_parent, &c, 1);
1002 g_return_if_fail(c == 'X');
1005 argv[0] = "eject";
1006 argv[1] = path;
1007 argv[2] = NULL;
1008 err = fork_exec_wait(argv);
1009 if (err)
1011 printf_send(_("!%s\neject failed\n"), err);
1012 g_free(err);
1015 printf_send("M%s", path);
1019 /* path is the item to check. If is is a directory then we may recurse
1020 * (unless prune is used).
1022 static void do_find(const char *path, const char *unused)
1024 FindInfo info;
1026 check_flags();
1028 if (!quiet)
1030 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1031 return;
1034 for (;;)
1036 if (new_entry_string)
1038 find_condition_free(find_condition);
1039 find_condition = find_compile(new_entry_string);
1040 null_g_free(&new_entry_string);
1043 if (find_condition)
1044 break;
1046 printf_send(_("!Invalid find condition - "
1047 "change it and try again\n"));
1048 if (!printf_reply(from_parent, TRUE,
1049 _("?Check '%s'?"), path))
1050 return;
1053 if (mc_lstat(path, &info.stats))
1055 send_error();
1056 printf_send(_("'(while checking '%s')\n"), path);
1057 return;
1060 info.fullpath = path;
1061 time(&info.now); /* XXX: Not for each check! */
1063 info.leaf = g_basename(path);
1064 info.prune = FALSE;
1065 if (find_test_condition(find_condition, &info))
1066 printf_send("=%s", path);
1068 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1070 char *safe_path;
1071 safe_path = g_strdup(path);
1072 for_dir_contents(do_find, safe_path, safe_path);
1073 g_free(safe_path);
1077 /* Like mode_compile(), but ignores spaces and bracketed bits */
1078 static struct mode_change *nice_mode_compile(const char *mode_string,
1079 unsigned int masked_ops)
1081 GString *new;
1082 int brackets = 0;
1083 struct mode_change *retval = NULL;
1085 new = g_string_new(NULL);
1087 for (; *mode_string; mode_string++)
1089 if (*mode_string == '(')
1090 brackets++;
1091 if (*mode_string == ')')
1093 brackets--;
1094 if (brackets < 0)
1095 break;
1096 continue;
1099 if (brackets == 0 && *mode_string != ' ')
1100 g_string_append_c(new, *mode_string);
1103 if (brackets == 0)
1104 retval = mode_compile(new->str, masked_ops);
1105 g_string_free(new, TRUE);
1106 return retval;
1109 static void do_chmod(const char *path, const char *unused)
1111 struct stat info;
1112 mode_t new_mode;
1114 check_flags();
1116 if (mc_lstat(path, &info))
1118 send_error();
1119 return;
1121 if (S_ISLNK(info.st_mode))
1122 return;
1124 if (!quiet)
1126 int res;
1127 printf_send("<%s", path);
1128 printf_send(">");
1129 res=printf_reply(from_parent, FALSE,
1130 _("?Change permissions of '%s'?"), path);
1131 printf_send("<");
1132 if (!res)
1133 return;
1135 else if (!o_brief)
1136 printf_send(_("'Changing permissions of '%s'\n"), path);
1138 for (;;)
1140 if (new_entry_string)
1142 if (mode_change)
1143 mode_free(mode_change);
1144 mode_change = nice_mode_compile(new_entry_string,
1145 MODE_MASK_ALL);
1146 null_g_free(&new_entry_string);
1149 if (mode_change)
1150 break;
1152 printf_send(
1153 _("!Invalid mode command - change it and try again\n"));
1154 if (!printf_reply(from_parent, TRUE,
1155 _("?Change permissions of '%s'?"), path))
1156 return;
1159 if (mc_lstat(path, &info))
1161 send_error();
1162 return;
1164 if (S_ISLNK(info.st_mode))
1165 return;
1167 new_mode = mode_adjust(info.st_mode, mode_change);
1168 if (chmod(path, new_mode))
1170 send_error();
1171 return;
1174 send_check_path(path);
1176 if (S_ISDIR(info.st_mode))
1178 send_mount_path(path);
1180 if (o_recurse)
1182 guchar *safe_path;
1183 safe_path = g_strdup(path);
1184 for_dir_contents(do_chmod, safe_path, safe_path);
1185 g_free(safe_path);
1190 static void do_settype(const char *path, const char *unused)
1192 struct stat info;
1194 check_flags();
1196 if (mc_lstat(path, &info))
1198 send_error();
1199 return;
1201 if (S_ISLNK(info.st_mode))
1202 return;
1204 if (!quiet)
1206 int res;
1207 printf_send("<%s", path);
1208 printf_send(">");
1209 res=printf_reply(from_parent, FALSE,
1210 _("?Change type of '%s'?"), path);
1211 printf_send("<");
1212 if (!res)
1213 return;
1216 for (;;)
1218 if (new_entry_string)
1220 type_change = mime_type_lookup(new_entry_string);
1221 null_g_free(&new_entry_string);
1224 if (type_change)
1225 break;
1227 printf_send(_("!Invalid type - "
1228 "change it and try again\n"));
1229 if (!printf_reply(from_parent, TRUE,
1230 _("?Change type of '%s'?"), path))
1231 return;
1234 if (mc_lstat(path, &info))
1236 send_error();
1237 return;
1239 if (S_ISLNK(info.st_mode))
1240 return;
1242 if (S_ISREG(info.st_mode))
1244 if (!o_brief)
1246 const char *comment;
1248 comment = mime_type_comment(type_change);
1249 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1250 comment);
1253 if (xtype_set(path, type_change))
1255 send_error();
1256 return;
1259 send_check_path(path);
1261 else if (S_ISDIR(info.st_mode))
1263 if (o_recurse)
1265 guchar *safe_path;
1266 safe_path = g_strdup(path);
1267 for_dir_contents(do_settype, safe_path, unused);
1268 g_free(safe_path);
1273 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1274 * is set then that is the new leafname, otherwise the leafname stays
1275 * the same.
1277 static const char *make_dest_path(const char *object, const char *dir)
1279 const char *leaf;
1281 if (action_leaf)
1282 leaf = action_leaf;
1283 else
1285 leaf = strrchr(object, '/');
1286 if (!leaf)
1287 leaf = object; /* Error? */
1288 else
1289 leaf++;
1292 return make_path(dir, leaf);
1295 /* If action_leaf is not NULL it specifies the new leaf name */
1296 static void do_copy2(const char *path, const char *dest)
1298 const char *dest_path;
1299 struct stat info;
1300 struct stat dest_info;
1302 check_flags();
1304 dest_path = make_dest_path(path, dest);
1306 if (mc_lstat(path, &info))
1308 send_error();
1309 return;
1312 if (mc_lstat(dest_path, &dest_info) == 0)
1314 int err;
1315 gboolean merge;
1317 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1319 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1321 /* Newer; keep going */
1323 else
1325 printf_send("<%s", path);
1326 printf_send(">%s", dest_path);
1327 if (!printf_reply(from_parent, TRUE,
1328 _("?'%s' already exists - %s?"),
1329 dest_path,
1330 merge ? _("merge contents")
1331 : _("overwrite")))
1332 return;
1335 if (!merge)
1337 if (S_ISDIR(dest_info.st_mode))
1338 err = rmdir(dest_path);
1339 else
1340 err = unlink(dest_path);
1342 if (err)
1344 send_error();
1345 if (errno != ENOENT)
1346 return;
1347 printf_send(_("'Trying copy anyway...\n"));
1351 else if (!quiet)
1353 printf_send("<%s", path);
1354 printf_send(">");
1355 if (!printf_reply(from_parent, FALSE,
1356 _("?Copy %s as %s?"), path, dest_path))
1357 return;
1359 else if (!o_brief || S_ISDIR(info.st_mode))
1360 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1362 if (S_ISDIR(info.st_mode))
1364 mode_t mode = info.st_mode;
1365 char *safe_path, *safe_dest;
1366 struct stat dest_info;
1367 gboolean exists;
1369 safe_path = g_strdup(path);
1370 safe_dest = g_strdup(dest_path);
1372 exists = !mc_lstat(dest_path, &dest_info);
1374 if (exists && !S_ISDIR(dest_info.st_mode))
1375 printf_send(_("!ERROR: Destination already exists, "
1376 "but is not a directory\n"));
1377 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1378 send_error();
1379 else
1381 if (!exists)
1382 /* (just been created then) */
1383 send_check_path(dest_path);
1385 action_leaf = NULL;
1386 for_dir_contents(do_copy2, safe_path, safe_dest);
1387 /* Note: dest_path now invalid... */
1389 if (!exists)
1391 struct utimbuf utb;
1393 /* We may have created the directory with
1394 * more permissions than the source so that
1395 * we could write to it... change it back now.
1397 if (chmod(safe_dest, mode))
1399 /* Some filesystems don't support
1400 * SetGID and SetUID bits. Ignore
1401 * these errors.
1403 if (errno != EPERM)
1404 send_error();
1407 /* Also, try to preserve the timestamps */
1408 utb.actime = info.st_atime;
1409 utb.modtime = info.st_mtime;
1411 utime(safe_dest, &utb);
1415 g_free(safe_path);
1416 g_free(safe_dest);
1418 else if (S_ISLNK(info.st_mode))
1420 char *target;
1422 /* Not all versions of cp(1) can make symlinks,
1423 * so we special-case it.
1426 target = readlink_dup(path);
1427 if (target)
1429 if (symlink(target, dest_path))
1430 send_error();
1431 else
1432 send_check_path(dest_path);
1434 g_free(target);
1436 else
1437 send_error();
1439 else
1441 guchar *error;
1443 error = copy_file(path, dest_path);
1445 if (error)
1447 printf_send(_("!%s\nFailed to copy '%s'\n"),
1448 error, path);
1449 g_free(error);
1451 else
1452 send_check_path(dest_path);
1456 /* If action_leaf is not NULL it specifies the new leaf name */
1457 static void do_move2(const char *path, const char *dest)
1459 const char *dest_path;
1460 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1461 struct stat info2;
1462 gboolean is_dir;
1463 char *err;
1465 check_flags();
1467 dest_path = make_dest_path(path, dest);
1469 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1471 if (access(dest_path, F_OK) == 0)
1473 struct stat info;
1474 int err;
1476 if (mc_lstat(dest_path, &info))
1478 send_error();
1479 return;
1482 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1484 /* Newer; keep going */
1486 else
1488 printf_send("<%s", path);
1489 printf_send(">%s", dest_path);
1490 if (!printf_reply(from_parent, TRUE,
1491 _("?'%s' already exists - overwrite?"),
1492 dest_path))
1493 return;
1496 if (S_ISDIR(info.st_mode))
1497 err = rmdir(dest_path);
1498 else
1499 err = unlink(dest_path);
1501 if (err)
1503 send_error();
1504 if (errno != ENOENT)
1505 return;
1506 printf_send(_("'Trying move anyway...\n"));
1509 else if (!quiet)
1511 printf_send("<%s", path);
1512 printf_send(">");
1513 if (!printf_reply(from_parent, FALSE,
1514 _("?Move %s as %s?"), path, dest_path))
1515 return;
1517 else if (!o_brief)
1518 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1520 argv[2] = path;
1521 argv[3] = dest_path;
1523 err = fork_exec_wait(argv);
1524 if (err)
1526 printf_send(_("!%s\nFailed to move %s as %s\n"),
1527 err, path, dest_path);
1528 g_free(err);
1530 else
1532 send_check_path(dest_path);
1534 if (is_dir)
1535 send_mount_path(path);
1536 else
1537 send_check_path(path);
1541 /* Copy path to dest.
1542 * Check that path not copied into itself.
1544 static void do_copy(const char *path, const char *dest)
1546 if (is_sub_dir(make_dest_path(path, dest), path))
1547 printf_send(_("!ERROR: Can't copy object into itself\n"));
1548 else
1550 do_copy2(path, dest);
1551 send_check_path(dest);
1555 /* Move path to dest.
1556 * Check that path not moved into itself.
1558 static void do_move(const char *path, const char *dest)
1560 if (is_sub_dir(make_dest_path(path, dest), path))
1561 printf_send(
1562 _("!ERROR: Can't move/rename object into itself\n"));
1563 else
1565 do_move2(path, dest);
1566 send_check_path(dest);
1570 /* Common code for do_link_relative() and do_link_absolute(). */
1571 static void do_link(const char *path, const char *dest_path)
1573 if (quiet)
1574 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1575 else {
1576 printf_send("<%s", path);
1577 printf_send(">");
1578 if (!printf_reply(from_parent, FALSE,
1579 _("?Link %s as %s?"), path, dest_path))
1580 return;
1583 if (symlink(path, dest_path))
1584 send_error();
1585 else
1586 send_check_path(dest_path);
1589 static void do_link_relative(const char *path, const char *dest)
1591 char *rel_path;
1592 const char *dest_path;
1594 dest_path = make_dest_path(path, dest);
1596 check_flags();
1598 rel_path = get_relative_path(dest_path, path);
1599 do_link(rel_path, dest_path);
1600 g_free(rel_path);
1603 static void do_link_absolute(const char *path, const char *dest)
1605 check_flags();
1606 do_link(path, make_dest_path(path, dest));
1609 /* Mount/umount this item (depending on 'mount') */
1610 static void do_mount(const guchar *path, gboolean mount)
1612 const char *argv[3] = {NULL, NULL, NULL};
1613 char *err;
1615 check_flags();
1617 argv[0] = mount ? o_action_mount_command.value
1618 : o_action_umount_command.value;
1619 argv[1] = path;
1621 if (quiet)
1622 printf_send(mount ? _("'Mounting %s\n")
1623 : _("'Unmounting %s\n"),
1624 path);
1625 else if (!printf_reply(from_parent, FALSE,
1626 mount ? _("?Mount %s?")
1627 : _("?Unmount %s?"),
1628 path))
1629 return;
1631 if (!mount)
1633 char c = '?';
1634 /* Need to close all sub-directories now, or we
1635 * can't unmount if dnotify is used.
1637 printf_send("X%s", path);
1638 /* Wait until it's safe... */
1639 read(from_parent, &c, 1);
1640 g_return_if_fail(c == 'X');
1643 err = fork_exec_wait(argv);
1644 if (err)
1646 printf_send(mount ?
1647 _("!%s\nMount failed\n") :
1648 _("!%s\nUnmount failed\n"), err);
1649 g_free(err);
1651 /* Mount may have worked even on error, eg if we try to mount
1652 * a read-only disk read/write, it gets mounted read-only
1653 * with an error.
1655 if (mount && mount_is_mounted(path, NULL, NULL))
1656 printf_send(_("'(seems to be mounted now anyway)\n"));
1657 else
1658 return;
1661 printf_send("M%s", path);
1662 if (mount && mount_open_dir)
1663 printf_send("o%s", path);
1666 /* CHILD MAIN LOOPS */
1668 /* After forking, the child calls one of these functions */
1670 /* We use a double for total size in order to count beyond 4Gb */
1671 static void usage_cb(gpointer data)
1673 GList *paths = (GList *) data;
1674 double total_size = 0;
1675 int n, i, per;
1677 n=g_list_length(paths);
1678 dir_counter = file_counter = 0;
1680 for (i=0; paths; paths = paths->next, i++)
1682 guchar *path = (guchar *) paths->data;
1684 send_dir(path);
1686 size_tally = 0;
1688 if(n>1 && i>0)
1690 per=100*i/n;
1691 printf_send("%%%d", per);
1693 do_usage(path, NULL);
1695 printf_send("'%s: %s\n",
1696 g_basename(path),
1697 format_double_size(size_tally));
1698 total_size += size_tally;
1700 printf_send("%%-1");
1702 g_string_printf(message, _("'\nTotal: %s ("),
1703 format_double_size(total_size));
1705 if (file_counter)
1706 g_string_append_printf(message,
1707 "%ld %s%s", file_counter,
1708 file_counter == 1 ? _("file") : _("files"),
1709 dir_counter ? ", " : ")\n");
1711 if (file_counter == 0 && dir_counter == 0)
1712 g_string_append(message, _("no directories)\n"));
1713 else if (dir_counter)
1714 g_string_append_printf(message,
1715 "%ld %s)\n", dir_counter,
1716 dir_counter == 1 ? _("directory")
1717 : _("directories"));
1719 send_msg();
1722 #ifdef DO_MOUNT_POINTS
1723 static void mount_cb(gpointer data)
1725 GList *paths = (GList *) data;
1726 gboolean mount_points = FALSE;
1727 int n, i, per;
1729 n=g_list_length(paths);
1730 for (i=0; paths; paths = paths->next, i++)
1732 guchar *path = (guchar *) paths->data;
1733 guchar *target;
1735 target = pathdup(path);
1736 if (!target)
1737 target = path;
1739 if(n>1 && i>0)
1741 per=100*i/n;
1742 printf_send("%%%d", per);
1744 if (mount_is_mounted(target, NULL, NULL) ||
1745 g_hash_table_lookup(fstab_mounts, target))
1747 mount_points = TRUE;
1748 do_mount(target, mount_mount); /* Mount */
1751 if (target != path)
1752 g_free(target);
1755 if (mount_points)
1756 send_done();
1757 else
1758 printf_send(_("!No mount points selected!\n"));
1760 #endif
1762 /* (use g_dirname() instead?) */
1763 static guchar *dirname(guchar *path)
1765 guchar *slash;
1767 slash = strrchr(path, '/');
1768 g_return_val_if_fail(slash != NULL, g_strdup(path));
1770 if (slash != path)
1771 return g_strndup(path, slash - path);
1772 return g_strdup("/");
1775 static void delete_cb(gpointer data)
1777 GList *paths = (GList *) data;
1778 int n, i, per;
1780 n=g_list_length(paths);
1781 for (i=0; paths; paths = paths->next, i++)
1783 guchar *path = (guchar *) paths->data;
1784 guchar *dir;
1786 dir = dirname(path);
1787 send_dir(dir);
1789 if(n>1 && i>0)
1791 per=100*i/n;
1792 printf_send("%%%d", per);
1794 do_delete(path, dir);
1796 g_free(dir);
1799 send_done();
1802 static void eject_cb(gpointer data)
1804 GList *paths = (GList *) data;
1805 int n, i, per;
1807 n=g_list_length(paths);
1809 for (i=0; paths; paths = paths->next, i++)
1811 guchar *path = (guchar *) paths->data;
1813 if(n>1 && i>0)
1815 per=100*i/n;
1816 printf_send("%%%d", per);
1818 send_dir(path);
1820 do_eject(path);
1823 send_done();
1826 static void find_cb(gpointer data)
1828 GList *all_paths = (GList *) data;
1829 GList *paths;
1831 while (1)
1833 for (paths = all_paths; paths; paths = paths->next)
1835 guchar *path = (guchar *) paths->data;
1837 send_dir(path);
1839 do_find(path, NULL);
1842 if (!printf_reply(from_parent, TRUE,
1843 _("?Another search?")))
1844 break;
1845 printf_send("#");
1848 send_done();
1851 static void chmod_cb(gpointer data)
1853 GList *paths = (GList *) data;
1854 int n, i, per;
1856 n=g_list_length(paths);
1858 for (i=0; paths; paths = paths->next, i++)
1860 guchar *path = (guchar *) paths->data;
1861 struct stat info;
1863 if(n>1 && i>0)
1865 per=100*i/n;
1866 printf_send("%%%d", per);
1868 send_dir(path);
1870 if (mc_stat(path, &info) != 0)
1871 send_error();
1872 else if (S_ISLNK(info.st_mode))
1873 printf_send(_("!'%s' is a symbolic link\n"),
1874 g_basename(path));
1875 else
1876 do_chmod(path, NULL);
1879 send_done();
1882 static void settype_cb(gpointer data)
1884 GList *paths = (GList *) data;
1885 int n, i, per;
1887 n=g_list_length(paths);
1889 for (i=0; paths; paths = paths->next, i++)
1891 guchar *path = (guchar *) paths->data;
1892 struct stat info;
1894 if(n>1 && i>0)
1896 per=100*i/n;
1897 printf_send("%%%d", per);
1899 send_dir(path);
1901 if (mc_stat(path, &info) != 0)
1902 send_error();
1903 else if (S_ISLNK(info.st_mode))
1904 printf_send(_("!'%s' is a symbolic link\n"),
1905 g_basename(path));
1906 else
1907 do_settype(path, NULL);
1910 send_done();
1913 static void list_cb(gpointer data)
1915 GList *paths = (GList *) data;
1916 int n, i, per;
1918 n=g_list_length(paths);
1920 for (i=0; paths; paths = paths->next, i++)
1922 if(n>1 && i>0)
1924 per=100*i/n;
1925 printf_send("%%%d", per);
1927 send_dir((char *) paths->data);
1929 action_do_func((char *) paths->data, action_dest);
1932 send_done();
1935 /* EXTERNAL INTERFACE */
1937 void action_find(GList *paths)
1939 GUIside *gui_side;
1940 GtkWidget *abox;
1942 if (!paths)
1944 report_error(_("You need to select some items "
1945 "to search through"));
1946 return;
1949 if (!last_find_string)
1950 last_find_string = g_strdup("'core'");
1952 new_entry_string = last_find_string;
1954 abox = abox_new(_("Find"), FALSE);
1955 gui_side = start_action(abox, find_cb, paths,
1956 o_action_force.int_value,
1957 o_action_brief.int_value,
1958 o_action_recurse.int_value,
1959 o_action_newer.int_value);
1960 if (!gui_side)
1961 return;
1963 abox_add_results(ABOX(abox));
1965 gui_side->default_string = &last_find_string;
1966 abox_add_entry(ABOX(abox), last_find_string,
1967 new_help_button(show_condition_help, NULL));
1968 g_signal_connect(ABOX(abox)->entry, "changed",
1969 G_CALLBACK(entry_changed), gui_side);
1970 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1972 gui_side->show_info = TRUE;
1973 gui_side->entry_string_func = set_find_string_colour;
1975 number_of_windows++;
1976 gtk_widget_show(abox);
1979 /* Count disk space used by selected items */
1980 void action_usage(GList *paths)
1982 GUIside *gui_side;
1983 GtkWidget *abox;
1985 if (!paths)
1987 report_error(_("You need to select some items to count"));
1988 return;
1991 abox = abox_new(_("Disk Usage"), TRUE);
1992 if(paths && paths->next)
1993 abox_set_percentage(ABOX(abox), 0);
1995 gui_side = start_action(abox, usage_cb, paths,
1996 o_action_force.int_value,
1997 o_action_brief.int_value,
1998 o_action_recurse.int_value,
1999 o_action_newer.int_value);
2000 if (!gui_side)
2001 return;
2003 gui_side->show_info = TRUE;
2005 number_of_windows++;
2007 gtk_widget_show(abox);
2010 /* Mount/unmount listed items (paths).
2011 * Free the list after this function returns.
2012 * If open_dir is TRUE and the dir is successfully mounted, open it.
2013 * quiet can be -1 for default.
2015 void action_mount(GList *paths, gboolean open_dir, gboolean mount, int quiet)
2017 #ifdef DO_MOUNT_POINTS
2018 GUIside *gui_side;
2019 GtkWidget *abox;
2021 if (quiet == -1)
2022 quiet = o_action_mount.int_value;
2024 mount_open_dir = open_dir;
2025 mount_mount = mount;
2027 abox = abox_new(_("Mount / Unmount"), quiet);
2028 if(paths && paths->next)
2029 abox_set_percentage(ABOX(abox), 0);
2030 gui_side = start_action(abox, mount_cb, paths,
2031 o_action_force.int_value,
2032 o_action_brief.int_value,
2033 o_action_recurse.int_value,
2034 o_action_newer.int_value);
2035 if (!gui_side)
2036 return;
2038 number_of_windows++;
2039 gtk_widget_show(abox);
2040 #else
2041 report_error(
2042 _("ROX-Filer does not yet support mount points on your "
2043 "system. Sorry."));
2044 #endif /* DO_MOUNT_POINTS */
2047 /* Delete these paths */
2048 void action_delete(GList *paths)
2050 GUIside *gui_side;
2051 GtkWidget *abox;
2053 if (!remove_pinned_ok(paths))
2054 return;
2056 abox = abox_new(_("Delete"), o_action_delete.int_value);
2057 if(paths && paths->next)
2058 abox_set_percentage(ABOX(abox), 0);
2059 gui_side = start_action(abox, delete_cb, paths,
2060 o_action_force.int_value,
2061 o_action_brief.int_value,
2062 o_action_recurse.int_value,
2063 o_action_newer.int_value);
2064 if (!gui_side)
2065 return;
2067 abox_add_flag(ABOX(abox),
2068 _("Force"), _("Don't confirm deletion of non-writeable items"),
2069 'F', o_action_force.int_value);
2070 abox_add_flag(ABOX(abox),
2071 _("Brief"), _("Only log directories being deleted"),
2072 'B', o_action_brief.int_value);
2074 number_of_windows++;
2075 gtk_widget_show(abox);
2078 /* Change the permissions of the selected items */
2079 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2081 GtkWidget *abox;
2082 GUIside *gui_side;
2083 static GList *presets = NULL;
2084 gboolean recurse = force_recurse || o_action_recurse.int_value;
2086 if (!paths)
2088 report_error(_("You need to select the items "
2089 "whose permissions you want to change"));
2090 return;
2093 if (!presets)
2095 presets = g_list_append(presets, (gchar *)
2096 _("a+x (Make executable/searchable)"));
2097 presets = g_list_append(presets, (gchar *)
2098 _("a-x (Make non-executable/non-searchable)"));
2099 presets = g_list_append(presets, (gchar *)
2100 _("u+rw (Give owner read+write)"));
2101 presets = g_list_append(presets, (gchar *)
2102 _("go-rwx (Private - owner access only)"));
2103 presets = g_list_append(presets, (gchar *)
2104 _("go=u-w (Public access, not write)"));
2107 if (!last_chmod_string)
2108 last_chmod_string = g_strdup((guchar *) presets->data);
2110 if (action)
2111 new_entry_string = g_strdup(action);
2112 else
2113 new_entry_string = g_strdup(last_chmod_string);
2115 abox = abox_new(_("Permissions"), FALSE);
2116 if(paths && paths->next)
2117 abox_set_percentage(ABOX(abox), 0);
2118 gui_side = start_action(abox, chmod_cb, paths,
2119 o_action_force.int_value,
2120 o_action_brief.int_value,
2121 recurse,
2122 o_action_newer.int_value);
2124 if (!gui_side)
2125 goto out;
2127 abox_add_flag(ABOX(abox),
2128 _("Brief"), _("Don't list processed files"),
2129 'B', o_action_brief.int_value);
2130 abox_add_flag(ABOX(abox),
2131 _("Recurse"), _("Also change contents of subdirectories"),
2132 'R', recurse);
2134 gui_side->default_string = &last_chmod_string;
2135 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2136 new_help_button(show_chmod_help, NULL));
2138 g_signal_connect(ABOX(abox)->entry, "changed",
2139 G_CALLBACK(entry_changed), gui_side);
2140 #if 0
2141 g_signal_connect_swapped(gui_side->entry, "activate",
2142 G_CALLBACK(gtk_button_clicked),
2143 gui_side->yes);
2144 #endif
2146 number_of_windows++;
2147 gtk_widget_show(abox);
2149 out:
2150 null_g_free(&new_entry_string);
2153 /* Set the MIME type of the selected items */
2154 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2156 GtkWidget *abox;
2157 GUIside *gui_side;
2158 GList *presets = NULL;
2159 gboolean recurse = force_recurse || o_action_recurse.int_value;
2161 if (!paths)
2163 report_error(_("You need to select the items "
2164 "whose type you want to change"));
2165 return;
2168 if (!last_settype_string)
2169 last_settype_string = g_strdup("text/plain");
2171 if (oldtype)
2172 new_entry_string = g_strdup(oldtype);
2173 else
2174 new_entry_string = g_strdup(last_settype_string);
2176 abox = abox_new(_("Set type"), FALSE);
2177 if(paths && paths->next)
2178 abox_set_percentage(ABOX(abox), 0);
2179 gui_side = start_action(abox, settype_cb, paths,
2180 o_action_force.int_value,
2181 o_action_brief.int_value,
2182 recurse,
2183 o_action_newer.int_value);
2185 if (!gui_side)
2186 goto out;
2188 abox_add_flag(ABOX(abox),
2189 _("Brief"), _("Don't list processed files"),
2190 'B', o_action_brief.int_value);
2191 abox_add_flag(ABOX(abox),
2192 _("Recurse"), _("Change contents of subdirectories"),
2193 'R', recurse);
2195 gui_side->default_string = &last_settype_string;
2197 /* Note: get the list again each time -- it can change */
2198 presets = mime_type_name_list();
2199 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2200 new_help_button(show_settype_help, NULL));
2201 g_list_free(presets);
2203 g_signal_connect(ABOX(abox)->entry, "changed",
2204 G_CALLBACK(entry_changed), gui_side);
2206 number_of_windows++;
2207 gtk_widget_show(abox);
2209 out:
2210 null_g_free(&new_entry_string);
2213 /* If leaf is NULL then the copy has the same name as the original.
2214 * quiet can be -1 for default.
2216 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2218 GUIside *gui_side;
2219 GtkWidget *abox;
2221 if (quiet == -1)
2222 quiet = o_action_copy.int_value;
2224 action_dest = dest;
2225 action_leaf = leaf;
2226 action_do_func = do_copy;
2228 abox = abox_new(_("Copy"), quiet);
2229 if(paths && paths->next)
2230 abox_set_percentage(ABOX(abox), 0);
2231 gui_side = start_action(abox, list_cb, paths,
2232 o_action_force.int_value,
2233 o_action_brief.int_value,
2234 o_action_recurse.int_value,
2235 o_action_newer.int_value);
2236 if (!gui_side)
2237 return;
2239 abox_add_flag(ABOX(abox),
2240 _("Newer"),
2241 _("Only over-write if source is newer than destination."),
2242 'W', o_action_newer.int_value);
2243 abox_add_flag(ABOX(abox),
2244 _("Brief"), _("Only log directories as they are copied"),
2245 'B', o_action_brief.int_value);
2247 number_of_windows++;
2248 gtk_widget_show(abox);
2251 /* If leaf is NULL then the file is not renamed.
2252 * quiet can be -1 for default.
2254 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2256 GUIside *gui_side;
2257 GtkWidget *abox;
2259 if (quiet == -1)
2260 quiet = o_action_move.int_value;
2262 action_dest = dest;
2263 action_leaf = leaf;
2264 action_do_func = do_move;
2266 abox = abox_new(_("Move"), quiet);
2267 if(paths && paths->next)
2268 abox_set_percentage(ABOX(abox), 0);
2269 gui_side = start_action(abox, list_cb, paths,
2270 o_action_force.int_value,
2271 o_action_brief.int_value,
2272 o_action_recurse.int_value,
2273 o_action_newer.int_value);
2274 if (!gui_side)
2275 return;
2277 abox_add_flag(ABOX(abox),
2278 _("Newer"),
2279 _("Only over-write if source is newer than destination."),
2280 'W', o_action_newer.int_value);
2281 abox_add_flag(ABOX(abox),
2282 _("Brief"), _("Don't log each file as it is moved"),
2283 'B', o_action_brief.int_value);
2284 number_of_windows++;
2285 gtk_widget_show(abox);
2288 /* If leaf is NULL then the link will have the same name */
2289 void action_link(GList *paths, const char *dest, const char *leaf,
2290 gboolean relative)
2292 GtkWidget *abox;
2293 GUIside *gui_side;
2295 action_dest = dest;
2296 action_leaf = leaf;
2297 if (relative)
2298 action_do_func = do_link_relative;
2299 else
2300 action_do_func = do_link_absolute;
2302 abox = abox_new(_("Link"), o_action_link.int_value);
2303 if(paths && paths->next)
2304 abox_set_percentage(ABOX(abox), 0);
2305 gui_side = start_action(abox, list_cb, paths,
2306 o_action_force.int_value,
2307 o_action_brief.int_value,
2308 o_action_recurse.int_value,
2309 o_action_newer.int_value);
2310 if (!gui_side)
2311 return;
2313 number_of_windows++;
2314 gtk_widget_show(abox);
2317 /* Eject these paths */
2318 void action_eject(GList *paths)
2320 GUIside *gui_side;
2321 GtkWidget *abox;
2323 abox = abox_new(_("Eject"), TRUE);
2324 if(paths && paths->next)
2325 abox_set_percentage(ABOX(abox), 0);
2326 gui_side = start_action(abox, eject_cb, paths,
2327 o_action_force.int_value,
2328 o_action_brief.int_value,
2329 o_action_recurse.int_value,
2330 o_action_newer.int_value);
2331 if (!gui_side)
2332 return;
2334 number_of_windows++;
2335 gtk_widget_show(abox);
2338 void action_init(void)
2340 option_add_int(&o_action_copy, "action_copy", 1);
2341 option_add_int(&o_action_move, "action_move", 1);
2342 option_add_int(&o_action_link, "action_link", 1);
2343 option_add_int(&o_action_delete, "action_delete", 0);
2344 option_add_int(&o_action_mount, "action_mount", 1);
2345 option_add_int(&o_action_force, "action_force", FALSE);
2346 option_add_int(&o_action_brief, "action_brief", FALSE);
2347 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2348 option_add_int(&o_action_newer, "action_newer", FALSE);
2350 option_add_string(&o_action_mount_command,
2351 "action_mount_command", "mount");
2352 option_add_string(&o_action_umount_command,
2353 "action_umount_command", "umount");
2356 #define MAX_ASK 4
2358 /* Check to see if any of the selected items (or their children) are
2359 * on the pinboard or panel. If so, ask for confirmation.
2361 * TRUE if it's OK to lose them.
2363 static gboolean remove_pinned_ok(GList *paths)
2365 GList *ask = NULL, *next;
2366 GString *message;
2367 int i, ask_n = 0;
2368 gboolean retval;
2370 for (; paths; paths = paths->next)
2372 guchar *path = (guchar *) paths->data;
2374 if (icons_require(path))
2376 if (++ask_n > MAX_ASK)
2377 break;
2378 ask = g_list_append(ask, path);
2382 if (!ask)
2383 return TRUE;
2385 if (ask_n > MAX_ASK)
2387 message = g_string_new(_("Deleting items such as "));
2388 ask_n--;
2390 else if (ask_n == 1)
2391 message = g_string_new(_("Deleting the item "));
2392 else
2393 message = g_string_new(_("Deleting the items "));
2395 i = 0;
2396 for (next = ask; next; next = next->next)
2398 guchar *path = (guchar *) next->data;
2399 guchar *leaf;
2401 leaf = strrchr(path, '/');
2402 if (leaf)
2403 leaf++;
2404 else
2405 leaf = path;
2407 g_string_append_c(message, '`');
2408 g_string_append(message, leaf);
2409 g_string_append_c(message, '\'');
2410 i++;
2411 if (i == ask_n - 1 && i > 0)
2412 g_string_append(message, _(" and "));
2413 else if (i < ask_n)
2414 g_string_append(message, ", ");
2417 g_list_free(ask);
2419 if (ask_n == 1)
2420 message = g_string_append(message,
2421 _(" will affect some items on the pinboard "
2422 "or panel - really delete it?"));
2423 else
2425 if (ask_n > MAX_ASK)
2426 message = g_string_append_c(message, ',');
2427 message = g_string_append(message,
2428 _(" will affect some items on the pinboard "
2429 "or panel - really delete them?"));
2432 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2434 g_string_free(message, TRUE);
2436 return retval;
2439 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2441 FindCondition *cond;
2443 cond = find_compile(string);
2444 entry_set_error(widget, !cond);
2446 find_condition_free(cond);