Use Name entry of .desktop files for pinboard/panel icons.
[rox-filer/dt.git] / ROX-Filer / src / action.c
blobc601e1b55a7a08830eed56ce81f874070ca57a9d
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;
127 static Option o_action_eject_command;
129 /* Whenever the text in these boxes is changed we store a copy of the new
130 * string to be used as the default next time.
132 static guchar *last_chmod_string = NULL;
133 static guchar *last_find_string = NULL;
134 static guchar *last_settype_string = NULL;
136 /* Set to one of the above before forking. This may change over a call to
137 * reply(). It is reset to NULL once the text is parsed.
139 static guchar *new_entry_string = NULL;
141 /* Static prototypes */
142 static void send_done(void);
143 static void send_check_path(const gchar *path);
144 static void send_mount_path(const gchar *path);
145 static gboolean printf_send(const char *msg, ...);
146 static gboolean send_msg(void);
147 static gboolean send_error(void);
148 static gboolean send_dir(const char *dir);
149 static gboolean read_exact(int source, char *buffer, ssize_t len);
150 static void do_mount(const guchar *path, gboolean mount);
151 static gboolean printf_reply(int fd, gboolean ignore_quiet,
152 const char *msg, ...);
153 static gboolean remove_pinned_ok(GList *paths);
155 /* SUPPORT */
158 /* This is called whenever the user edits the entry box (if any) - send the
159 * new string.
161 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
163 guchar *text;
165 g_return_if_fail(gui_side->default_string != NULL);
167 text = gtk_editable_get_chars(entry, 0, -1);
169 if (gui_side->entry_string_func)
170 gui_side->entry_string_func(GTK_WIDGET(entry), text);
172 g_free(*(gui_side->default_string));
173 *(gui_side->default_string) = text; /* Gets text's ref */
175 if (!gui_side->to_child)
176 return;
178 fputc('E', gui_side->to_child);
179 fputs(text, gui_side->to_child);
180 fputc('\n', gui_side->to_child);
181 fflush(gui_side->to_child);
184 void show_condition_help(gpointer data)
186 GtkWidget *help;
187 GtkWidget *text;
189 help = gtk_dialog_new_with_buttons(
190 _("Find expression reference"),
191 NULL, 0,
192 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
193 NULL);
194 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
196 text = gtk_label_new(NULL);
197 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
198 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
199 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
200 gtk_label_set_markup(GTK_LABEL(text), _(
201 "<u>Quick Start</u>\n"
202 "Just put the name of the file you're looking for in single quotes:\n"
203 "<b>'index.html'</b> (to find a file called 'index.html')\n"
204 "\n"
205 "<u>Examples</u>\n"
206 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
207 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
208 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
209 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
210 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
211 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
212 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
213 "\n"
214 "<u>Simple Tests</u>\n"
215 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
216 "(types)\n"
217 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
218 "(permissions)\n"
219 "<b>IsEmpty, IsMine</b>\n"
220 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
221 "contains a slash then the match is against the full path; otherwise it is\n"
222 "against the leafname only.\n"
223 "\n"
224 "<u>Comparisons</u>\n"
225 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
226 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
227 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
228 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
229 "(values)\n"
230 "\n"
231 "<u>Specials</u>\n"
232 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
233 "a % in 'command' is replaced with the path of the current file)\n"
234 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
236 g_signal_connect(help, "response",
237 G_CALLBACK(gtk_widget_destroy), NULL);
239 gtk_widget_show_all(help);
242 static void show_chmod_help(gpointer data)
244 GtkWidget *help;
245 GtkWidget *text;
247 help = gtk_dialog_new_with_buttons(
248 _("Change permissions reference"),
249 NULL, 0,
250 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
251 NULL);
252 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
254 text = gtk_label_new(NULL);
255 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
256 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
257 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
258 gtk_label_set_markup(GTK_LABEL(text), _(
259 "Normally, you can just select a command from the menu (click \n"
260 "on the arrow beside the command box). Sometimes, you need more...\n"
261 "\n"
262 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
263 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
264 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
265 "determines whether to\n"
266 "change the permissions for the User (owner), Group or Others.\n"
267 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
268 "exactly the permissions.\n"
269 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
270 "\n"
271 "Bracketed text and spaces are ignored.\n"
272 "\n"
273 "<u>Examples</u>\n"
274 "<b>u+rw</b>: the file owner gains read and write permission\n"
275 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
276 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
277 "write permission\n"
278 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
279 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
280 "executable by anyone become executable by everyone\n"
281 "<b>u+rw, go+r</b>: two commands at once!\n"
282 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
283 "<b>755</b>: set the permissions directly\n"
284 "\n"
285 "See the chmod(1) man page for full details."));
287 g_signal_connect(help, "response",
288 G_CALLBACK(gtk_widget_destroy), NULL);
290 gtk_widget_show_all(help);
294 static void show_settype_help(gpointer data)
296 GtkWidget *help;
297 GtkWidget *text;
299 help = gtk_dialog_new_with_buttons(
300 _("Set type reference"),
301 NULL, 0,
302 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
303 NULL);
304 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
306 text = gtk_label_new(NULL);
307 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
308 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
309 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
310 gtk_label_set_markup(GTK_LABEL(text), _(
311 "Normally ROX-Filer determines the type of a regular file\n"
312 "by matching it's name against a pattern. To change the\n"
313 "type of the file you must rename it.\n"
314 "\n"
315 "Newer file systems can support something called 'Extended\n"
316 "Attributes' which can be used to store additional data with\n"
317 "each file as named parameters. ROX-Filer uses the\n"
318 "'user.mime_type' attribute to store file types.\n"
319 "\n"
320 "File types are only supported for regular files, not\n"
321 "directories, devices, pipes or sockets, and then only\n"
322 "on certain file systems and where the OS implements them.\n"));
324 text = gtk_label_new(_(ATTR_MAN_PAGE));
325 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
326 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
328 g_signal_connect(help, "response",
329 G_CALLBACK(gtk_widget_destroy), NULL);
331 gtk_widget_show_all(help);
334 static void process_message(GUIside *gui_side, const gchar *buffer)
336 ABox *abox = gui_side->abox;
338 if (*buffer == '?')
339 abox_ask(abox, buffer + 1);
340 else if (*buffer == 's')
341 dir_check_this(buffer + 1); /* Update this item */
342 else if (*buffer == '=')
343 abox_add_filename(abox, buffer + 1);
344 else if (*buffer == '#')
345 abox_clear_results(abox);
346 else if (*buffer == 'X')
348 filer_close_recursive(buffer + 1);
349 /* Let child know it's safe to continue... */
350 fputc('X', gui_side->to_child);
351 fflush(gui_side->to_child);
353 else if (*buffer == 'm' || *buffer == 'M')
355 /* Mount / major changes to this path */
356 if (*buffer == 'M')
358 mount_update(TRUE);
359 mount_user_mount(buffer + 1);
361 filer_check_mounted(buffer + 1);
363 else if (*buffer == '/')
364 abox_set_current_object(abox, buffer + 1);
365 else if (*buffer == 'o')
366 filer_opendir(buffer + 1, NULL, NULL);
367 else if (*buffer == '!')
369 gui_side->errors++;
370 abox_log(abox, buffer + 1, "error");
372 else if (*buffer == '<')
373 abox_set_file(abox, 0, buffer+1);
374 else if (*buffer == '>')
376 abox_set_file(abox, 1, buffer+1);
377 abox_show_compare(abox, TRUE);
379 else if (*buffer == '%')
381 abox_set_percentage(abox, atoi(buffer+1));
383 else
384 abox_log(abox, buffer + 1, NULL);
387 /* Called when the child sends us a message */
388 static void message_from_child(gpointer data,
389 gint source,
390 GdkInputCondition condition)
392 char buf[5];
393 GUIside *gui_side = (GUIside *) data;
394 ABox *abox = gui_side->abox;
395 GtkTextBuffer *text_buffer;
397 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
399 if (read_exact(source, buf, 4))
401 ssize_t message_len;
402 char *buffer;
404 buf[4] = '\0';
405 message_len = strtol(buf, NULL, 16);
406 buffer = g_malloc(message_len + 1);
407 if (message_len > 0 && read_exact(source, buffer, message_len))
409 buffer[message_len] = '\0';
410 process_message(gui_side, buffer);
411 g_free(buffer);
412 return;
414 g_printerr("Child died in the middle of a message.\n");
417 if (gui_side->abort_attempts)
418 abox_log(abox, _("\nProcess terminated.\n"), "error");
420 /* The child is dead */
421 gui_side->child = 0;
423 fclose(gui_side->to_child);
424 gui_side->to_child = NULL;
425 close(gui_side->from_child);
426 g_source_remove(gui_side->input_tag);
427 abox_cancel_ask(gui_side->abox);
429 if (gui_side->errors)
431 guchar *report;
433 if (gui_side->errors == 1)
434 report = g_strdup(_("There was one error.\n"));
435 else
436 report = g_strdup_printf(_("There were %d errors.\n"),
437 gui_side->errors);
439 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
441 g_free(report);
443 else if (gui_side->show_info == FALSE)
444 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
447 /* Scans src_dir, calling cb(item, dest_path) for each item */
448 static void for_dir_contents(ForDirCB *cb,
449 const char *src_dir,
450 const char *dest_path)
452 DIR *d;
453 struct dirent *ent;
454 GList *list = NULL, *next;
456 d = mc_opendir(src_dir);
457 if (!d)
459 /* Message displayed is "ERROR reading 'path': message" */
460 printf_send("!%s '%s': %s\n", _("ERROR reading"),
461 src_dir, g_strerror(errno));
462 return;
465 send_dir(src_dir);
467 while ((ent = mc_readdir(d)))
469 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
470 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
471 continue;
472 list = g_list_prepend(list, g_strdup(make_path(src_dir,
473 ent->d_name)));
475 mc_closedir(d);
477 for (next = list; next; next = next->next)
479 cb((char *) next->data, dest_path);
481 g_free(next->data);
483 g_list_free(list);
486 /* Read this many bytes into the buffer. TRUE on success. */
487 static gboolean read_exact(int source, char *buffer, ssize_t len)
489 while (len > 0)
491 ssize_t got;
492 got = read(source, buffer, len);
493 if (got < 1)
494 return FALSE;
495 len -= got;
496 buffer += got;
498 return TRUE;
501 static void send_done(void)
503 printf_send(_("'\nDone\n"));
506 /* Notify the filer that this item has been updated */
507 static void send_check_path(const gchar *path)
509 printf_send("s%s", path);
512 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
513 static void send_mount_path(const gchar *path)
515 printf_send("m%s", path);
518 /* Send a message to the filer process. The first character indicates the
519 * type of the message.
521 static gboolean printf_send(const char *msg, ...)
523 va_list args;
524 gchar *tmp;
526 va_start(args, msg);
527 tmp = g_strdup_vprintf(msg, args);
528 va_end(args);
530 g_string_assign(message, tmp);
531 g_free(tmp);
533 return send_msg();
536 /* Send 'message' to our parent process. TRUE on success. */
537 static gboolean send_msg(void)
539 char len_buffer[5];
540 ssize_t len;
542 g_return_val_if_fail(message->len < 0xffff, FALSE);
544 sprintf(len_buffer, "%04" G_GSIZE_MODIFIER "x", message->len);
545 fwrite(len_buffer, 1, 4, to_parent);
546 len = fwrite(message->str, 1, message->len, to_parent);
547 fflush(to_parent);
548 return len == (ssize_t) message->len;
551 /* Set the directory indicator at the top of the window */
552 static gboolean send_dir(const char *dir)
554 return printf_send("/%s", dir);
557 static gboolean send_error(void)
559 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
562 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
564 gchar code;
566 if (!gui_side->to_child)
567 return;
569 if (response == GTK_RESPONSE_YES)
570 code = 'Y';
571 else if (response == GTK_RESPONSE_NO)
572 code = 'N';
573 else
574 return;
576 fputc(code, gui_side->to_child);
577 fflush(gui_side->to_child);
578 abox_show_compare(gui_side->abox, FALSE);
581 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
583 if (!gui_side->to_child)
584 return;
586 fputc(flag, gui_side->to_child);
587 fflush(gui_side->to_child);
590 static void read_new_entry_text(void)
592 int len;
593 char c;
594 GString *new;
596 new = g_string_new(NULL);
598 for (;;)
600 len = read(from_parent, &c, 1);
601 if (len != 1)
603 fprintf(stderr, "read() error: %s\n",
604 g_strerror(errno));
605 _exit(1); /* Parent died? */
608 if (c == '\n')
609 break;
610 g_string_append_c(new, c);
613 g_free(new_entry_string);
614 new_entry_string = new->str;
615 g_string_free(new, FALSE);
618 static void process_flag(char flag)
620 switch (flag)
622 case 'Q':
623 quiet = !quiet;
624 break;
625 case 'F':
626 o_force = !o_force;
627 break;
628 case 'R':
629 o_recurse = !o_recurse;
630 break;
631 case 'B':
632 o_brief = !o_brief;
633 break;
634 case 'W':
635 o_newer = !o_newer;
636 break;
637 case 'E':
638 read_new_entry_text();
639 break;
640 default:
641 printf_send("!ERROR: Bad message '%c'\n", flag);
642 break;
646 /* If the parent has sent any flag toggles, read them */
647 static void check_flags(void)
649 fd_set set;
650 int got;
651 char retval;
652 struct timeval tv;
654 FD_ZERO(&set);
656 while (1)
658 FD_SET(from_parent, &set);
659 tv.tv_sec = 0;
660 tv.tv_usec = 0;
661 got = select(from_parent + 1, &set, NULL, NULL, &tv);
663 if (got == -1)
664 g_error("select() failed: %s\n", g_strerror(errno));
665 else if (!got)
666 return;
668 got = read(from_parent, &retval, 1);
669 if (got != 1)
670 g_error("read() error: %s\n", g_strerror(errno));
672 process_flag(retval);
676 /* Read until the user sends a reply. If ignore_quiet is TRUE then
677 * the user MUST click Yes or No, else treat quiet on as Yes.
678 * If the user needs prompting then does send_msg().
680 static gboolean printf_reply(int fd, gboolean ignore_quiet,
681 const char *msg, ...)
683 ssize_t len;
684 char retval;
685 va_list args;
686 gchar *tmp;
688 if (quiet && !ignore_quiet)
689 return TRUE;
691 va_start(args, msg);
692 tmp = g_strdup_vprintf(msg, args);
693 va_end(args);
695 g_string_assign(message, tmp);
696 g_free(tmp);
698 send_msg();
700 while (1)
702 len = read(fd, &retval, 1);
703 if (len != 1)
705 fprintf(stderr, "read() error: %s\n",
706 g_strerror(errno));
707 _exit(1); /* Parent died? */
710 switch (retval)
712 case 'Y':
713 printf_send("' %s\n", _("Yes"));
714 return TRUE;
715 case 'N':
716 printf_send("' %s\n", _("No"));
717 return FALSE;
718 default:
719 process_flag(retval);
720 break;
725 static void abort_operation(GtkWidget *widget, gpointer data)
727 GUIside *gui_side = (GUIside *) data;
729 if (gui_side->child)
731 if (gui_side->abort_attempts == 0)
733 abox_log(ABOX(widget),
734 _("\nAsking child process to terminate...\n"),
735 "error");
736 kill(-gui_side->child, SIGTERM);
738 else
740 abox_log(ABOX(widget),
741 _("\nTrying to KILL run-away process...\n"),
742 "error");
743 kill(-gui_side->child, SIGKILL);
744 kill(-gui_side->child, SIGCONT);
746 gui_side->abort_attempts++;
748 else
749 gtk_widget_destroy(widget);
752 static void destroy_action_window(GtkWidget *widget, gpointer data)
754 GUIside *gui_side = (GUIside *) data;
756 if (gui_side->child)
758 kill(-gui_side->child, SIGTERM);
759 fclose(gui_side->to_child);
760 close(gui_side->from_child);
761 g_source_remove(gui_side->input_tag);
764 g_free(gui_side);
766 one_less_window();
769 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
770 * (NULL on failure). The child calls func().
772 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
773 int force, int brief, int recurse, int newer)
775 gboolean autoq;
776 int filedes[4]; /* 0 and 2 are for reading */
777 GUIside *gui_side;
778 pid_t child;
779 struct sigaction act;
781 if (pipe(filedes))
783 report_error("pipe: %s", g_strerror(errno));
784 gtk_widget_destroy(abox);
785 return NULL;
788 if (pipe(filedes + 2))
790 close(filedes[0]);
791 close(filedes[1]);
792 report_error("pipe: %s", g_strerror(errno));
793 gtk_widget_destroy(abox);
794 return NULL;
797 autoq = gtk_toggle_button_get_active(
798 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
800 o_force = force;
801 o_brief = brief;
802 o_recurse = recurse;
803 o_newer = newer;
805 child = fork();
806 switch (child)
808 case -1:
809 report_error("fork: %s", g_strerror(errno));
810 gtk_widget_destroy(abox);
811 return NULL;
812 case 0:
813 /* We are the child */
815 /* Create a new process group */
816 setpgid(0, 0);
818 quiet = autoq;
820 dir_drop_all_dnotifies();
822 /* Reset the SIGCHLD handler */
823 act.sa_handler = SIG_DFL;
824 sigemptyset(&act.sa_mask);
825 act.sa_flags = 0;
826 sigaction(SIGCHLD, &act, NULL);
828 message = g_string_new(NULL);
829 close(filedes[0]);
830 close(filedes[3]);
831 to_parent = fdopen(filedes[1], "wb");
832 from_parent = filedes[2];
833 func(data);
834 send_dir("");
835 _exit(0);
838 /* We are the parent */
839 close(filedes[1]);
840 close(filedes[2]);
841 gui_side = g_new(GUIside, 1);
842 gui_side->from_child = filedes[0];
843 gui_side->to_child = fdopen(filedes[3], "wb");
844 gui_side->child = child;
845 gui_side->errors = 0;
846 gui_side->show_info = FALSE;
847 gui_side->default_string = NULL;
848 gui_side->entry_string_func = NULL;
849 gui_side->abort_attempts = 0;
851 gui_side->abox = ABOX(abox);
852 g_signal_connect(abox, "destroy",
853 G_CALLBACK(destroy_action_window), gui_side);
855 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
856 g_signal_connect(abox, "flag_toggled",
857 G_CALLBACK(flag_toggled), gui_side);
858 g_signal_connect(abox, "abort_operation",
859 G_CALLBACK(abort_operation), gui_side);
861 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
862 GDK_INPUT_READ,
863 message_from_child,
864 gui_side, NULL);
866 return gui_side;
869 /* ACTIONS ON ONE ITEM */
871 /* These may call themselves recursively, or ask questions, etc */
873 /* Updates the global size_tally, file_counter and dir_counter */
874 static void do_usage(const char *src_path, const char *unused)
876 struct stat info;
878 check_flags();
880 if (mc_lstat(src_path, &info))
882 printf_send("'%s:\n", src_path);
883 send_error();
885 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
887 file_counter++;
888 size_tally += info.st_size;
890 else if (S_ISDIR(info.st_mode))
892 dir_counter++;
893 if (printf_reply(from_parent, FALSE,
894 _("?Count contents of %s?"), src_path))
896 char *safe_path;
897 safe_path = g_strdup(src_path);
898 for_dir_contents(do_usage, safe_path, safe_path);
899 g_free(safe_path);
902 else
903 file_counter++;
906 /* dest_path is the dir containing src_path */
907 static void do_delete(const char *src_path, const char *unused)
909 struct stat info;
910 gboolean write_prot;
911 char *safe_path;
913 check_flags();
915 if (mc_lstat(src_path, &info))
917 send_error();
918 return;
921 write_prot = S_ISLNK(info.st_mode) ? FALSE
922 : access(src_path, W_OK) != 0;
923 if (write_prot || !quiet)
925 int res;
927 printf_send("<%s", src_path);
928 printf_send(">");
929 res=printf_reply(from_parent, write_prot && !o_force,
930 _("?Delete %s'%s'?"),
931 write_prot ? _("WRITE-PROTECTED ") : "",
932 src_path);
933 printf_send("<");
934 if (!res)
935 return;
937 else if (!o_brief)
938 printf_send(_("'Deleting '%s'\n"), src_path);
940 safe_path = g_strdup(src_path);
942 if (S_ISDIR(info.st_mode))
944 for_dir_contents(do_delete, safe_path, safe_path);
945 if (rmdir(safe_path))
947 g_free(safe_path);
948 send_error();
949 return;
951 printf_send(_("'Directory '%s' deleted\n"), safe_path);
952 send_mount_path(safe_path);
954 else if (unlink(src_path))
955 send_error();
956 else
958 send_check_path(safe_path);
959 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
961 gchar *dir;
962 dir = g_path_get_dirname(safe_path);
963 send_check_path(dir);
964 g_free(dir);
968 g_free(safe_path);
971 static void do_eject(const char *path)
973 const char *argv[]={"sh", "-c", NULL, NULL};
974 char *err;
976 check_flags();
978 if (!quiet)
980 int res;
981 printf_send("<%s", path);
982 printf_send(">");
983 res=printf_reply(from_parent, !o_force,
984 _("?Eject '%s'?"),
985 path);
986 printf_send("<");
987 if (!res)
988 return;
990 else if (!o_brief)
991 printf_send(_("'Eject '%s'\n"), path);
993 /* Need to close all sub-directories now, or we
994 * can't unmount if dnotify is used.
997 char c = '?';
998 printf_send("X%s", path);
999 /* Wait until it's safe... */
1000 read(from_parent, &c, 1);
1001 g_return_if_fail(c == 'X');
1004 argv[2] = build_command_with_path(o_action_eject_command.value,
1005 path);
1006 err = fork_exec_wait((const char**)argv);
1007 g_free((gchar *) argv[2]);
1008 if (err)
1010 printf_send(_("!%s\neject failed\n"), err);
1011 g_free(err);
1014 printf_send("M%s", path);
1018 /* path is the item to check. If is is a directory then we may recurse
1019 * (unless prune is used).
1021 static void do_find(const char *path, const char *unused)
1023 FindInfo info;
1025 check_flags();
1027 if (!quiet)
1029 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1030 return;
1033 for (;;)
1035 if (new_entry_string)
1037 find_condition_free(find_condition);
1038 find_condition = find_compile(new_entry_string);
1039 null_g_free(&new_entry_string);
1042 if (find_condition)
1043 break;
1045 printf_send(_("!Invalid find condition - "
1046 "change it and try again\n"));
1047 if (!printf_reply(from_parent, TRUE,
1048 _("?Check '%s'?"), path))
1049 return;
1052 if (mc_lstat(path, &info.stats))
1054 send_error();
1055 printf_send(_("'(while checking '%s')\n"), path);
1056 return;
1059 info.fullpath = path;
1060 time(&info.now); /* XXX: Not for each check! */
1062 info.leaf = g_basename(path);
1063 info.prune = FALSE;
1064 if (find_test_condition(find_condition, &info))
1065 printf_send("=%s", path);
1067 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1069 char *safe_path;
1070 safe_path = g_strdup(path);
1071 for_dir_contents(do_find, safe_path, safe_path);
1072 g_free(safe_path);
1076 /* Like mode_compile(), but ignores spaces and bracketed bits */
1077 static struct mode_change *nice_mode_compile(const char *mode_string,
1078 unsigned int masked_ops)
1080 GString *new;
1081 int brackets = 0;
1082 struct mode_change *retval = NULL;
1084 new = g_string_new(NULL);
1086 for (; *mode_string; mode_string++)
1088 if (*mode_string == '(')
1089 brackets++;
1090 if (*mode_string == ')')
1092 brackets--;
1093 if (brackets < 0)
1094 break;
1095 continue;
1098 if (brackets == 0 && *mode_string != ' ')
1099 g_string_append_c(new, *mode_string);
1102 if (brackets == 0)
1103 retval = mode_compile(new->str, masked_ops);
1104 g_string_free(new, TRUE);
1105 return retval;
1108 static void do_chmod(const char *path, const char *unused)
1110 struct stat info;
1111 mode_t new_mode;
1113 check_flags();
1115 if (mc_lstat(path, &info))
1117 send_error();
1118 return;
1120 if (S_ISLNK(info.st_mode))
1121 return;
1123 if (!quiet)
1125 int res;
1126 printf_send("<%s", path);
1127 printf_send(">");
1128 res=printf_reply(from_parent, FALSE,
1129 _("?Change permissions of '%s'?"), path);
1130 printf_send("<");
1131 if (!res)
1132 return;
1134 else if (!o_brief)
1135 printf_send(_("'Changing permissions of '%s'\n"), path);
1137 for (;;)
1139 if (new_entry_string)
1141 if (mode_change)
1142 mode_free(mode_change);
1143 mode_change = nice_mode_compile(new_entry_string,
1144 MODE_MASK_ALL);
1145 null_g_free(&new_entry_string);
1148 if (mode_change)
1149 break;
1151 printf_send(
1152 _("!Invalid mode command - change it and try again\n"));
1153 if (!printf_reply(from_parent, TRUE,
1154 _("?Change permissions of '%s'?"), path))
1155 return;
1158 if (mc_lstat(path, &info))
1160 send_error();
1161 return;
1163 if (S_ISLNK(info.st_mode))
1164 return;
1166 new_mode = mode_adjust(info.st_mode, mode_change);
1167 if (chmod(path, new_mode))
1169 send_error();
1170 return;
1173 send_check_path(path);
1175 if (S_ISDIR(info.st_mode))
1177 send_mount_path(path);
1179 if (o_recurse)
1181 guchar *safe_path;
1182 safe_path = g_strdup(path);
1183 for_dir_contents(do_chmod, safe_path, safe_path);
1184 g_free(safe_path);
1189 static void do_settype(const char *path, const char *unused)
1191 struct stat info;
1193 check_flags();
1195 if (mc_lstat(path, &info))
1197 send_error();
1198 return;
1200 if (S_ISLNK(info.st_mode))
1201 return;
1203 if (!quiet)
1205 int res;
1206 printf_send("<%s", path);
1207 printf_send(">");
1208 if (S_ISDIR(info.st_mode))
1209 res=printf_reply(from_parent, FALSE,
1210 _("?Change contents of '%s'?"), path);
1211 else
1212 res=printf_reply(from_parent, FALSE,
1213 _("?Change type of '%s'?"), path);
1214 printf_send("<");
1215 if (!res)
1216 return;
1219 for (;;)
1221 if (new_entry_string)
1223 type_change = mime_type_lookup(new_entry_string);
1224 null_g_free(&new_entry_string);
1227 if (type_change)
1228 break;
1230 printf_send(_("!Invalid type - "
1231 "change it and try again\n"));
1232 if (!printf_reply(from_parent, TRUE,
1233 _("?Change type of '%s'?"), path))
1234 return;
1237 if (mc_lstat(path, &info))
1239 send_error();
1240 return;
1242 if (S_ISLNK(info.st_mode))
1243 return;
1245 if (S_ISREG(info.st_mode))
1247 if (!o_brief)
1249 const char *comment;
1251 comment = mime_type_comment(type_change);
1252 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1253 comment);
1256 if (xtype_set(path, type_change))
1258 send_error();
1259 return;
1262 send_check_path(path);
1264 else if (S_ISDIR(info.st_mode))
1266 if (o_recurse)
1268 guchar *safe_path;
1269 safe_path = g_strdup(path);
1270 for_dir_contents(do_settype, safe_path, unused);
1271 g_free(safe_path);
1273 else if(!o_brief)
1275 printf_send(_("'Not changing type of directory '%s'\n"),
1276 path);
1279 else if(!o_brief)
1281 printf_send(_("'Non-regular file '%s' not changed\n"),
1282 path);
1286 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1287 * is set then that is the new leafname, otherwise the leafname stays
1288 * the same.
1290 static const char *make_dest_path(const char *object, const char *dir)
1292 const char *leaf;
1294 if (action_leaf)
1295 leaf = action_leaf;
1296 else
1298 leaf = strrchr(object, '/');
1299 if (!leaf)
1300 leaf = object; /* Error? */
1301 else
1302 leaf++;
1305 return make_path(dir, leaf);
1308 /* If action_leaf is not NULL it specifies the new leaf name */
1309 static void do_copy2(const char *path, const char *dest)
1311 const char *dest_path;
1312 struct stat info;
1313 struct stat dest_info;
1315 check_flags();
1317 dest_path = make_dest_path(path, dest);
1319 if (mc_lstat(path, &info))
1321 send_error();
1322 return;
1325 if (mc_lstat(dest_path, &dest_info) == 0)
1327 int err;
1328 gboolean merge;
1330 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1332 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1334 /* Newer; keep going */
1336 else
1338 printf_send("<%s", path);
1339 printf_send(">%s", dest_path);
1340 if (!printf_reply(from_parent, TRUE,
1341 _("?'%s' already exists - %s?"),
1342 dest_path,
1343 merge ? _("merge contents")
1344 : _("overwrite")))
1345 return;
1348 if (!merge)
1350 if (S_ISDIR(dest_info.st_mode))
1351 err = rmdir(dest_path);
1352 else
1353 err = unlink(dest_path);
1355 if (err)
1357 send_error();
1358 if (errno != ENOENT)
1359 return;
1360 printf_send(_("'Trying copy anyway...\n"));
1364 else if (!quiet)
1366 printf_send("<%s", path);
1367 printf_send(">");
1368 if (!printf_reply(from_parent, FALSE,
1369 _("?Copy %s as %s?"), path, dest_path))
1370 return;
1372 else if (!o_brief || S_ISDIR(info.st_mode))
1373 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1375 if (S_ISDIR(info.st_mode))
1377 mode_t mode = info.st_mode;
1378 char *safe_path, *safe_dest;
1379 struct stat dest_info;
1380 gboolean exists;
1382 safe_path = g_strdup(path);
1383 safe_dest = g_strdup(dest_path);
1385 exists = !mc_lstat(dest_path, &dest_info);
1387 if (exists && !S_ISDIR(dest_info.st_mode))
1388 printf_send(_("!ERROR: Destination already exists, "
1389 "but is not a directory\n"));
1390 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1391 send_error();
1392 else
1394 if (!exists)
1395 /* (just been created then) */
1396 send_check_path(dest_path);
1398 action_leaf = NULL;
1399 for_dir_contents(do_copy2, safe_path, safe_dest);
1400 /* Note: dest_path now invalid... */
1402 if (!exists)
1404 struct utimbuf utb;
1406 /* We may have created the directory with
1407 * more permissions than the source so that
1408 * we could write to it... change it back now.
1410 if (chmod(safe_dest, mode))
1412 /* Some filesystems don't support
1413 * SetGID and SetUID bits. Ignore
1414 * these errors.
1416 if (errno != EPERM)
1417 send_error();
1420 /* Also, try to preserve the timestamps */
1421 utb.actime = info.st_atime;
1422 utb.modtime = info.st_mtime;
1424 utime(safe_dest, &utb);
1428 g_free(safe_path);
1429 g_free(safe_dest);
1431 else if (S_ISLNK(info.st_mode))
1433 char *target;
1435 /* Not all versions of cp(1) can make symlinks,
1436 * so we special-case it.
1439 target = readlink_dup(path);
1440 if (target)
1442 if (symlink(target, dest_path))
1443 send_error();
1444 else
1445 send_check_path(dest_path);
1447 g_free(target);
1449 else
1450 send_error();
1452 else
1454 guchar *error;
1456 error = copy_file(path, dest_path);
1458 if (error)
1460 printf_send(_("!%s\nFailed to copy '%s'\n"),
1461 error, path);
1462 g_free(error);
1464 else
1465 send_check_path(dest_path);
1469 /* If action_leaf is not NULL it specifies the new leaf name */
1470 static void do_move2(const char *path, const char *dest)
1472 const char *dest_path;
1473 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1474 struct stat info2;
1475 gboolean is_dir;
1476 char *err;
1478 check_flags();
1480 dest_path = make_dest_path(path, dest);
1482 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1484 if (access(dest_path, F_OK) == 0)
1486 struct stat info;
1487 int err;
1489 if (mc_lstat(dest_path, &info))
1491 send_error();
1492 return;
1495 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1497 /* Newer; keep going */
1499 else
1501 printf_send("<%s", path);
1502 printf_send(">%s", dest_path);
1503 if (!printf_reply(from_parent, TRUE,
1504 _("?'%s' already exists - overwrite?"),
1505 dest_path))
1506 return;
1509 if (S_ISDIR(info.st_mode))
1510 err = rmdir(dest_path);
1511 else
1512 err = unlink(dest_path);
1514 if (err)
1516 send_error();
1517 if (errno != ENOENT)
1518 return;
1519 printf_send(_("'Trying move anyway...\n"));
1522 else if (!quiet)
1524 printf_send("<%s", path);
1525 printf_send(">");
1526 if (!printf_reply(from_parent, FALSE,
1527 _("?Move %s as %s?"), path, dest_path))
1528 return;
1530 else if (!o_brief)
1531 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1533 argv[2] = path;
1534 argv[3] = dest_path;
1536 err = fork_exec_wait(argv);
1537 if (err)
1539 printf_send(_("!%s\nFailed to move %s as %s\n"),
1540 err, path, dest_path);
1541 g_free(err);
1543 else
1545 send_check_path(dest_path);
1547 if (is_dir)
1548 send_mount_path(path);
1549 else
1550 send_check_path(path);
1554 /* Copy path to dest.
1555 * Check that path not copied into itself.
1557 static void do_copy(const char *path, const char *dest)
1559 if (is_sub_dir(make_dest_path(path, dest), path))
1560 printf_send(_("!ERROR: Can't copy object into itself\n"));
1561 else
1563 do_copy2(path, dest);
1564 send_check_path(dest);
1568 /* Move path to dest.
1569 * Check that path not moved into itself.
1571 static void do_move(const char *path, const char *dest)
1573 if (is_sub_dir(make_dest_path(path, dest), path))
1574 printf_send(
1575 _("!ERROR: Can't move/rename object into itself\n"));
1576 else
1578 do_move2(path, dest);
1579 send_check_path(dest);
1583 /* Common code for do_link_relative() and do_link_absolute(). */
1584 static void do_link(const char *path, const char *dest_path)
1586 if (quiet)
1587 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1588 else {
1589 printf_send("<%s", path);
1590 printf_send(">");
1591 if (!printf_reply(from_parent, FALSE,
1592 _("?Link %s as %s?"), path, dest_path))
1593 return;
1596 if (symlink(path, dest_path))
1597 send_error();
1598 else
1599 send_check_path(dest_path);
1602 static void do_link_relative(const char *path, const char *dest)
1604 char *rel_path;
1605 const char *dest_path;
1607 dest_path = make_dest_path(path, dest);
1609 check_flags();
1611 rel_path = get_relative_path(dest_path, path);
1612 do_link(rel_path, dest_path);
1613 g_free(rel_path);
1616 static void do_link_absolute(const char *path, const char *dest)
1618 check_flags();
1619 do_link(path, make_dest_path(path, dest));
1622 /* Mount/umount this item (depending on 'mount') */
1623 static void do_mount(const guchar *path, gboolean mount)
1625 const char *argv[] = {"sh", "-c", NULL, NULL};
1626 char *err;
1628 check_flags();
1630 argv[2] = build_command_with_path(mount ? o_action_mount_command.value
1631 : o_action_umount_command.value,
1632 path);
1634 if (quiet)
1635 printf_send(mount ? _("'Mounting %s\n")
1636 : _("'Unmounting %s\n"),
1637 path);
1638 else if (!printf_reply(from_parent, FALSE,
1639 mount ? _("?Mount %s?")
1640 : _("?Unmount %s?"),
1641 path))
1642 return;
1644 if (!mount)
1646 char c = '?';
1647 /* Need to close all sub-directories now, or we
1648 * can't unmount if dnotify is used.
1650 printf_send("X%s", path);
1651 /* Wait until it's safe... */
1652 read(from_parent, &c, 1);
1653 g_return_if_fail(c == 'X');
1656 err = fork_exec_wait(argv);
1657 g_free((gchar *) argv[2]);
1658 if (err)
1660 printf_send(mount ?
1661 _("!%s\nMount failed\n") :
1662 _("!%s\nUnmount failed\n"), err);
1663 g_free(err);
1665 /* Mount may have worked even on error, eg if we try to mount
1666 * a read-only disk read/write, it gets mounted read-only
1667 * with an error.
1669 if (mount && mount_is_mounted(path, NULL, NULL))
1670 printf_send(_("'(seems to be mounted now anyway)\n"));
1671 else
1672 return;
1675 printf_send("M%s", path);
1676 if (mount && mount_open_dir)
1677 printf_send("o%s", path);
1680 /* CHILD MAIN LOOPS */
1682 /* After forking, the child calls one of these functions */
1684 /* We use a double for total size in order to count beyond 4Gb */
1685 static void usage_cb(gpointer data)
1687 GList *paths = (GList *) data;
1688 double total_size = 0;
1689 int n, i, per;
1691 n=g_list_length(paths);
1692 dir_counter = file_counter = 0;
1694 for (i=0; paths; paths = paths->next, i++)
1696 guchar *path = (guchar *) paths->data;
1698 send_dir(path);
1700 size_tally = 0;
1702 if(n>1 && i>0)
1704 per=100*i/n;
1705 printf_send("%%%d", per);
1707 do_usage(path, NULL);
1709 printf_send("'%s: %s\n",
1710 g_basename(path),
1711 format_double_size(size_tally));
1712 total_size += size_tally;
1714 printf_send("%%-1");
1716 g_string_printf(message, _("'\nTotal: %s ("),
1717 format_double_size(total_size));
1719 if (file_counter)
1720 g_string_append_printf(message,
1721 "%ld %s%s", file_counter,
1722 file_counter == 1 ? _("file") : _("files"),
1723 dir_counter ? ", " : ")\n");
1725 if (file_counter == 0 && dir_counter == 0)
1726 g_string_append(message, _("no directories)\n"));
1727 else if (dir_counter)
1728 g_string_append_printf(message,
1729 "%ld %s)\n", dir_counter,
1730 dir_counter == 1 ? _("directory")
1731 : _("directories"));
1733 send_msg();
1736 #ifdef DO_MOUNT_POINTS
1737 static void mount_cb(gpointer data)
1739 GList *paths = (GList *) data;
1740 gboolean mount_points = FALSE;
1741 int n, i, per;
1743 n=g_list_length(paths);
1744 for (i=0; paths; paths = paths->next, i++)
1746 guchar *path = (guchar *) paths->data;
1747 guchar *target;
1749 target = pathdup(path);
1750 if (!target)
1751 target = path;
1753 if(n>1 && i>0)
1755 per=100*i/n;
1756 printf_send("%%%d", per);
1758 if (mount_is_mounted(target, NULL, NULL) ||
1759 g_hash_table_lookup(fstab_mounts, target))
1761 mount_points = TRUE;
1762 do_mount(target, mount_mount); /* Mount */
1765 if (target != path)
1766 g_free(target);
1769 if (mount_points)
1770 send_done();
1771 else
1772 printf_send(_("!No mount points selected!\n"));
1774 #endif
1776 /* (use g_dirname() instead?) */
1777 static guchar *dirname(guchar *path)
1779 guchar *slash;
1781 slash = strrchr(path, '/');
1782 g_return_val_if_fail(slash != NULL, g_strdup(path));
1784 if (slash != path)
1785 return g_strndup(path, slash - path);
1786 return g_strdup("/");
1789 static void delete_cb(gpointer data)
1791 GList *paths = (GList *) data;
1792 int n, i, per;
1794 n=g_list_length(paths);
1795 for (i=0; paths; paths = paths->next, i++)
1797 guchar *path = (guchar *) paths->data;
1798 guchar *dir;
1800 dir = dirname(path);
1801 send_dir(dir);
1803 if(n>1 && i>0)
1805 per=100*i/n;
1806 printf_send("%%%d", per);
1808 do_delete(path, dir);
1810 g_free(dir);
1813 send_done();
1816 static void eject_cb(gpointer data)
1818 GList *paths = (GList *) data;
1819 int n, i, per;
1821 n=g_list_length(paths);
1823 for (i=0; paths; paths = paths->next, i++)
1825 guchar *path = (guchar *) paths->data;
1827 if(n>1 && i>0)
1829 per=100*i/n;
1830 printf_send("%%%d", per);
1832 send_dir(path);
1834 do_eject(path);
1837 send_done();
1840 static void find_cb(gpointer data)
1842 GList *all_paths = (GList *) data;
1843 GList *paths;
1845 while (1)
1847 for (paths = all_paths; paths; paths = paths->next)
1849 guchar *path = (guchar *) paths->data;
1851 send_dir(path);
1853 do_find(path, NULL);
1856 if (!printf_reply(from_parent, TRUE,
1857 _("?Another search?")))
1858 break;
1859 printf_send("#");
1862 send_done();
1865 static void chmod_cb(gpointer data)
1867 GList *paths = (GList *) data;
1868 int n, i, per;
1870 n=g_list_length(paths);
1872 for (i=0; paths; paths = paths->next, i++)
1874 guchar *path = (guchar *) paths->data;
1875 struct stat info;
1877 if(n>1 && i>0)
1879 per=100*i/n;
1880 printf_send("%%%d", per);
1882 send_dir(path);
1884 if (mc_stat(path, &info) != 0)
1885 send_error();
1886 else if (S_ISLNK(info.st_mode))
1887 printf_send(_("!'%s' is a symbolic link\n"),
1888 g_basename(path));
1889 else
1890 do_chmod(path, NULL);
1893 send_done();
1896 static void settype_cb(gpointer data)
1898 GList *paths = (GList *) data;
1899 int n, i, per;
1901 n=g_list_length(paths);
1903 for (i=0; paths; paths = paths->next, i++)
1905 guchar *path = (guchar *) paths->data;
1906 struct stat info;
1908 if(n>1 && i>0)
1910 per=100*i/n;
1911 printf_send("%%%d", per);
1913 send_dir(path);
1915 if (mc_stat(path, &info) != 0)
1916 send_error();
1917 else if (S_ISLNK(info.st_mode))
1918 printf_send(_("!'%s' is a symbolic link\n"),
1919 g_basename(path));
1920 else
1921 do_settype(path, NULL);
1924 send_done();
1927 static void list_cb(gpointer data)
1929 GList *paths = (GList *) data;
1930 int n, i, per;
1932 n=g_list_length(paths);
1934 for (i=0; paths; paths = paths->next, i++)
1936 if(n>1 && i>0)
1938 per=100*i/n;
1939 printf_send("%%%d", per);
1941 send_dir((char *) paths->data);
1943 action_do_func((char *) paths->data, action_dest);
1946 send_done();
1949 /* EXTERNAL INTERFACE */
1951 void action_find(GList *paths)
1953 GUIside *gui_side;
1954 GtkWidget *abox;
1956 if (!paths)
1958 report_error(_("You need to select some items "
1959 "to search through"));
1960 return;
1963 if (!last_find_string)
1964 last_find_string = g_strdup("'core'");
1966 new_entry_string = last_find_string;
1968 abox = abox_new(_("Find"), FALSE);
1969 gui_side = start_action(abox, find_cb, paths,
1970 o_action_force.int_value,
1971 o_action_brief.int_value,
1972 o_action_recurse.int_value,
1973 o_action_newer.int_value);
1974 if (!gui_side)
1975 return;
1977 abox_add_results(ABOX(abox));
1979 gui_side->default_string = &last_find_string;
1980 abox_add_entry(ABOX(abox), last_find_string,
1981 new_help_button(show_condition_help, NULL));
1982 g_signal_connect(ABOX(abox)->entry, "changed",
1983 G_CALLBACK(entry_changed), gui_side);
1984 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1986 gui_side->show_info = TRUE;
1987 gui_side->entry_string_func = set_find_string_colour;
1989 number_of_windows++;
1990 gtk_widget_show(abox);
1993 /* Count disk space used by selected items */
1994 void action_usage(GList *paths)
1996 GUIside *gui_side;
1997 GtkWidget *abox;
1999 if (!paths)
2001 report_error(_("You need to select some items to count"));
2002 return;
2005 abox = abox_new(_("Disk Usage"), TRUE);
2006 if(paths && paths->next)
2007 abox_set_percentage(ABOX(abox), 0);
2009 gui_side = start_action(abox, usage_cb, paths,
2010 o_action_force.int_value,
2011 o_action_brief.int_value,
2012 o_action_recurse.int_value,
2013 o_action_newer.int_value);
2014 if (!gui_side)
2015 return;
2017 gui_side->show_info = TRUE;
2019 number_of_windows++;
2021 gtk_widget_show(abox);
2024 /* Mount/unmount listed items (paths).
2025 * Free the list after this function returns.
2026 * If open_dir is TRUE and the dir is successfully mounted, open it.
2027 * quiet can be -1 for default.
2029 void action_mount(GList *paths, gboolean open_dir, gboolean mount, int quiet)
2031 #ifdef DO_MOUNT_POINTS
2032 GUIside *gui_side;
2033 GtkWidget *abox;
2035 if (quiet == -1)
2036 quiet = o_action_mount.int_value;
2038 mount_open_dir = open_dir;
2039 mount_mount = mount;
2041 abox = abox_new(_("Mount / Unmount"), quiet);
2042 if(paths && paths->next)
2043 abox_set_percentage(ABOX(abox), 0);
2044 gui_side = start_action(abox, mount_cb, paths,
2045 o_action_force.int_value,
2046 o_action_brief.int_value,
2047 o_action_recurse.int_value,
2048 o_action_newer.int_value);
2049 if (!gui_side)
2050 return;
2052 number_of_windows++;
2053 gtk_widget_show(abox);
2054 #else
2055 report_error(
2056 _("ROX-Filer does not yet support mount points on your "
2057 "system. Sorry."));
2058 #endif /* DO_MOUNT_POINTS */
2061 /* Delete these paths */
2062 void action_delete(GList *paths)
2064 GUIside *gui_side;
2065 GtkWidget *abox;
2067 if (!remove_pinned_ok(paths))
2068 return;
2070 abox = abox_new(_("Delete"), o_action_delete.int_value);
2071 if(paths && paths->next)
2072 abox_set_percentage(ABOX(abox), 0);
2073 gui_side = start_action(abox, delete_cb, paths,
2074 o_action_force.int_value,
2075 o_action_brief.int_value,
2076 o_action_recurse.int_value,
2077 o_action_newer.int_value);
2078 if (!gui_side)
2079 return;
2081 abox_add_flag(ABOX(abox),
2082 _("Force"), _("Don't confirm deletion of non-writeable items"),
2083 'F', o_action_force.int_value);
2084 abox_add_flag(ABOX(abox),
2085 _("Brief"), _("Only log directories being deleted"),
2086 'B', o_action_brief.int_value);
2088 number_of_windows++;
2089 gtk_widget_show(abox);
2092 /* Change the permissions of the selected items */
2093 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2095 GtkWidget *abox;
2096 GUIside *gui_side;
2097 static GList *presets = NULL;
2098 gboolean recurse = force_recurse || o_action_recurse.int_value;
2100 if (!paths)
2102 report_error(_("You need to select the items "
2103 "whose permissions you want to change"));
2104 return;
2107 if (!presets)
2109 presets = g_list_append(presets, (gchar *)
2110 _("a+x (Make executable/searchable)"));
2111 presets = g_list_append(presets, (gchar *)
2112 _("a-x (Make non-executable/non-searchable)"));
2113 presets = g_list_append(presets, (gchar *)
2114 _("u+rw (Give owner read+write)"));
2115 presets = g_list_append(presets, (gchar *)
2116 _("go-rwx (Private - owner access only)"));
2117 presets = g_list_append(presets, (gchar *)
2118 _("go=u-w (Public access, not write)"));
2121 if (!last_chmod_string)
2122 last_chmod_string = g_strdup((guchar *) presets->data);
2124 if (action)
2125 new_entry_string = g_strdup(action);
2126 else
2127 new_entry_string = g_strdup(last_chmod_string);
2129 abox = abox_new(_("Permissions"), FALSE);
2130 if(paths && paths->next)
2131 abox_set_percentage(ABOX(abox), 0);
2132 gui_side = start_action(abox, chmod_cb, paths,
2133 o_action_force.int_value,
2134 o_action_brief.int_value,
2135 recurse,
2136 o_action_newer.int_value);
2138 if (!gui_side)
2139 goto out;
2141 abox_add_flag(ABOX(abox),
2142 _("Brief"), _("Don't list processed files"),
2143 'B', o_action_brief.int_value);
2144 abox_add_flag(ABOX(abox),
2145 _("Recurse"), _("Also change contents of subdirectories"),
2146 'R', recurse);
2148 gui_side->default_string = &last_chmod_string;
2149 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2150 new_help_button(show_chmod_help, NULL));
2152 g_signal_connect(ABOX(abox)->entry, "changed",
2153 G_CALLBACK(entry_changed), gui_side);
2154 #if 0
2155 g_signal_connect_swapped(gui_side->entry, "activate",
2156 G_CALLBACK(gtk_button_clicked),
2157 gui_side->yes);
2158 #endif
2160 number_of_windows++;
2161 gtk_widget_show(abox);
2163 out:
2164 null_g_free(&new_entry_string);
2167 /* Set the MIME type of the selected items */
2168 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2170 GtkWidget *abox;
2171 GUIside *gui_side;
2172 GList *presets = NULL;
2173 gboolean recurse = force_recurse || o_action_recurse.int_value;
2175 if (!paths)
2177 report_error(_("You need to select the items "
2178 "whose type you want to change"));
2179 return;
2182 if (!last_settype_string)
2183 last_settype_string = g_strdup("text/plain");
2185 if (oldtype)
2186 new_entry_string = g_strdup(oldtype);
2187 else
2188 new_entry_string = g_strdup(last_settype_string);
2190 abox = abox_new(_("Set type"), FALSE);
2191 if(paths && paths->next)
2192 abox_set_percentage(ABOX(abox), 0);
2193 gui_side = start_action(abox, settype_cb, paths,
2194 o_action_force.int_value,
2195 o_action_brief.int_value,
2196 recurse,
2197 o_action_newer.int_value);
2199 if (!gui_side)
2200 goto out;
2202 abox_add_flag(ABOX(abox),
2203 _("Brief"), _("Don't list processed files"),
2204 'B', o_action_brief.int_value);
2205 abox_add_flag(ABOX(abox),
2206 _("Recurse"), _("Change contents of subdirectories"),
2207 'R', recurse);
2209 gui_side->default_string = &last_settype_string;
2211 /* Note: get the list again each time -- it can change */
2212 presets = mime_type_name_list(TRUE);
2213 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2214 new_help_button(show_settype_help, NULL));
2215 g_list_free(presets);
2217 g_signal_connect(ABOX(abox)->entry, "changed",
2218 G_CALLBACK(entry_changed), gui_side);
2220 number_of_windows++;
2221 gtk_widget_show(abox);
2223 out:
2224 null_g_free(&new_entry_string);
2227 /* If leaf is NULL then the copy has the same name as the original.
2228 * quiet can be -1 for default.
2230 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2232 GUIside *gui_side;
2233 GtkWidget *abox;
2235 if (quiet == -1)
2236 quiet = o_action_copy.int_value;
2238 action_dest = dest;
2239 action_leaf = leaf;
2240 action_do_func = do_copy;
2242 abox = abox_new(_("Copy"), quiet);
2243 if(paths && paths->next)
2244 abox_set_percentage(ABOX(abox), 0);
2245 gui_side = start_action(abox, list_cb, paths,
2246 o_action_force.int_value,
2247 o_action_brief.int_value,
2248 o_action_recurse.int_value,
2249 o_action_newer.int_value);
2250 if (!gui_side)
2251 return;
2253 abox_add_flag(ABOX(abox),
2254 _("Newer"),
2255 _("Only over-write if source is newer than destination."),
2256 'W', o_action_newer.int_value);
2257 abox_add_flag(ABOX(abox),
2258 _("Brief"), _("Only log directories as they are copied"),
2259 'B', o_action_brief.int_value);
2261 number_of_windows++;
2262 gtk_widget_show(abox);
2265 /* If leaf is NULL then the file is not renamed.
2266 * quiet can be -1 for default.
2268 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2270 GUIside *gui_side;
2271 GtkWidget *abox;
2273 if (quiet == -1)
2274 quiet = o_action_move.int_value;
2276 action_dest = dest;
2277 action_leaf = leaf;
2278 action_do_func = do_move;
2280 abox = abox_new(_("Move"), quiet);
2281 if(paths && paths->next)
2282 abox_set_percentage(ABOX(abox), 0);
2283 gui_side = start_action(abox, list_cb, paths,
2284 o_action_force.int_value,
2285 o_action_brief.int_value,
2286 o_action_recurse.int_value,
2287 o_action_newer.int_value);
2288 if (!gui_side)
2289 return;
2291 abox_add_flag(ABOX(abox),
2292 _("Newer"),
2293 _("Only over-write if source is newer than destination."),
2294 'W', o_action_newer.int_value);
2295 abox_add_flag(ABOX(abox),
2296 _("Brief"), _("Don't log each file as it is moved"),
2297 'B', o_action_brief.int_value);
2298 number_of_windows++;
2299 gtk_widget_show(abox);
2302 /* If leaf is NULL then the link will have the same name */
2303 void action_link(GList *paths, const char *dest, const char *leaf,
2304 gboolean relative)
2306 GtkWidget *abox;
2307 GUIside *gui_side;
2309 action_dest = dest;
2310 action_leaf = leaf;
2311 if (relative)
2312 action_do_func = do_link_relative;
2313 else
2314 action_do_func = do_link_absolute;
2316 abox = abox_new(_("Link"), o_action_link.int_value);
2317 if(paths && paths->next)
2318 abox_set_percentage(ABOX(abox), 0);
2319 gui_side = start_action(abox, list_cb, paths,
2320 o_action_force.int_value,
2321 o_action_brief.int_value,
2322 o_action_recurse.int_value,
2323 o_action_newer.int_value);
2324 if (!gui_side)
2325 return;
2327 number_of_windows++;
2328 gtk_widget_show(abox);
2331 /* Eject these paths */
2332 void action_eject(GList *paths)
2334 GUIside *gui_side;
2335 GtkWidget *abox;
2337 abox = abox_new(_("Eject"), TRUE);
2338 if(paths && paths->next)
2339 abox_set_percentage(ABOX(abox), 0);
2340 gui_side = start_action(abox, eject_cb, paths,
2341 o_action_force.int_value,
2342 o_action_brief.int_value,
2343 o_action_recurse.int_value,
2344 o_action_newer.int_value);
2345 if (!gui_side)
2346 return;
2348 number_of_windows++;
2349 gtk_widget_show(abox);
2352 void action_init(void)
2354 option_add_int(&o_action_copy, "action_copy", 1);
2355 option_add_int(&o_action_move, "action_move", 1);
2356 option_add_int(&o_action_link, "action_link", 1);
2357 option_add_int(&o_action_delete, "action_delete", 0);
2358 option_add_int(&o_action_mount, "action_mount", 1);
2359 option_add_int(&o_action_force, "action_force", FALSE);
2360 option_add_int(&o_action_brief, "action_brief", FALSE);
2361 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2362 option_add_int(&o_action_newer, "action_newer", FALSE);
2364 option_add_string(&o_action_mount_command,
2365 "action_mount_command", "mount");
2366 option_add_string(&o_action_umount_command,
2367 "action_umount_command", "umount");
2368 option_add_string(&o_action_eject_command,
2369 "action_eject_command", "eject");
2372 #define MAX_ASK 4
2374 /* Check to see if any of the selected items (or their children) are
2375 * on the pinboard or panel. If so, ask for confirmation.
2377 * TRUE if it's OK to lose them.
2379 static gboolean remove_pinned_ok(GList *paths)
2381 GList *ask = NULL, *next;
2382 GString *message;
2383 int i, ask_n = 0;
2384 gboolean retval;
2386 for (; paths; paths = paths->next)
2388 guchar *path = (guchar *) paths->data;
2390 if (icons_require(path))
2392 if (++ask_n > MAX_ASK)
2393 break;
2394 ask = g_list_append(ask, path);
2398 if (!ask)
2399 return TRUE;
2401 if (ask_n > MAX_ASK)
2403 message = g_string_new(_("Deleting items such as "));
2404 ask_n--;
2406 else if (ask_n == 1)
2407 message = g_string_new(_("Deleting the item "));
2408 else
2409 message = g_string_new(_("Deleting the items "));
2411 i = 0;
2412 for (next = ask; next; next = next->next)
2414 guchar *path = (guchar *) next->data;
2415 guchar *leaf;
2417 leaf = strrchr(path, '/');
2418 if (leaf)
2419 leaf++;
2420 else
2421 leaf = path;
2423 g_string_append_c(message, '`');
2424 g_string_append(message, leaf);
2425 g_string_append_c(message, '\'');
2426 i++;
2427 if (i == ask_n - 1 && i > 0)
2428 g_string_append(message, _(" and "));
2429 else if (i < ask_n)
2430 g_string_append(message, ", ");
2433 g_list_free(ask);
2435 if (ask_n == 1)
2436 message = g_string_append(message,
2437 _(" will affect some items on the pinboard "
2438 "or panel - really delete it?"));
2439 else
2441 if (ask_n > MAX_ASK)
2442 message = g_string_append_c(message, ',');
2443 message = g_string_append(message,
2444 _(" will affect some items on the pinboard "
2445 "or panel - really delete them?"));
2448 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2450 g_string_free(message, TRUE);
2452 return retval;
2455 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2457 FindCondition *cond;
2459 cond = find_compile(string);
2460 entry_set_error(widget, !cond);
2462 find_condition_free(cond);