r4034: Use application_x_shellscript for scripts.
[rox-filer/translations.git] / ROX-Filer / src / action.c
blobc3ad39fe1cee54263fb40f08f34841af2c0f11e3
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 /* Whenever the text in these boxes is changed we store a copy of the new
128 * string to be used as the default next time.
130 static guchar *last_chmod_string = NULL;
131 static guchar *last_find_string = NULL;
132 static guchar *last_settype_string = NULL;
134 /* Set to one of the above before forking. This may change over a call to
135 * reply(). It is reset to NULL once the text is parsed.
137 static guchar *new_entry_string = NULL;
139 /* Static prototypes */
140 static void send_done(void);
141 static void send_check_path(const gchar *path);
142 static void send_mount_path(const gchar *path);
143 static gboolean printf_send(const char *msg, ...);
144 static gboolean send_msg(void);
145 static gboolean send_error(void);
146 static gboolean send_dir(const char *dir);
147 static gboolean read_exact(int source, char *buffer, ssize_t len);
148 static void do_mount(const guchar *path, gboolean mount);
149 static gboolean printf_reply(int fd, gboolean ignore_quiet,
150 const char *msg, ...);
151 static gboolean remove_pinned_ok(GList *paths);
153 /* SUPPORT */
156 /* This is called whenever the user edits the entry box (if any) - send the
157 * new string.
159 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
161 guchar *text;
163 g_return_if_fail(gui_side->default_string != NULL);
165 text = gtk_editable_get_chars(entry, 0, -1);
167 if (gui_side->entry_string_func)
168 gui_side->entry_string_func(GTK_WIDGET(entry), text);
170 g_free(*(gui_side->default_string));
171 *(gui_side->default_string) = text; /* Gets text's ref */
173 if (!gui_side->to_child)
174 return;
176 fputc('E', gui_side->to_child);
177 fputs(text, gui_side->to_child);
178 fputc('\n', gui_side->to_child);
179 fflush(gui_side->to_child);
182 void show_condition_help(gpointer data)
184 GtkWidget *help;
185 GtkWidget *text;
187 help = gtk_dialog_new_with_buttons(
188 _("Find expression reference"),
189 NULL, 0,
190 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
191 NULL);
192 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
194 text = gtk_label_new(NULL);
195 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
196 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
197 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
198 gtk_label_set_markup(GTK_LABEL(text), _(
199 "<u>Quick Start</u>\n"
200 "Just put the name of the file you're looking for in single quotes:\n"
201 "<b>'index.html'</b> (to find a file called 'index.html')\n"
202 "\n"
203 "<u>Examples</u>\n"
204 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
205 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
206 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
207 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
208 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
209 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
210 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
211 "\n"
212 "<u>Simple Tests</u>\n"
213 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
214 "(types)\n"
215 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
216 "(permissions)\n"
217 "<b>IsEmpty, IsMine</b>\n"
218 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
219 "contains a slash then the match is against the full path; otherwise it is\n"
220 "against the leafname only.\n"
221 "\n"
222 "<u>Comparisons</u>\n"
223 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
224 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
225 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
226 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
227 "(values)\n"
228 "\n"
229 "<u>Specials</u>\n"
230 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
231 "a % in 'command' is replaced with the path of the current file)\n"
232 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
234 g_signal_connect(help, "response",
235 G_CALLBACK(gtk_widget_destroy), NULL);
237 gtk_widget_show_all(help);
240 static void show_chmod_help(gpointer data)
242 GtkWidget *help;
243 GtkWidget *text;
245 help = gtk_dialog_new_with_buttons(
246 _("Change permissions reference"),
247 NULL, 0,
248 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
249 NULL);
250 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
252 text = gtk_label_new(NULL);
253 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
254 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
255 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
256 gtk_label_set_markup(GTK_LABEL(text), _(
257 "Normally, you can just select a command from the menu (click \n"
258 "on the arrow beside the command box). Sometimes, you need more...\n"
259 "\n"
260 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
261 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
262 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
263 "determines whether to\n"
264 "change the permissions for the User (owner), Group or Others.\n"
265 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
266 "exactly the permissions.\n"
267 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
268 "\n"
269 "Bracketed text and spaces are ignored.\n"
270 "\n"
271 "<u>Examples</u>\n"
272 "<b>u+rw</b>: the file owner gains read and write permission\n"
273 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
274 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
275 "write permission\n"
276 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
277 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
278 "executable by anyone become executable by everyone\n"
279 "<b>u+rw, go+r</b>: two commands at once!\n"
280 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
281 "<b>755</b>: set the permissions directly\n"
282 "\n"
283 "See the chmod(1) man page for full details."));
285 g_signal_connect(help, "response",
286 G_CALLBACK(gtk_widget_destroy), NULL);
288 gtk_widget_show_all(help);
292 static void show_settype_help(gpointer data)
294 GtkWidget *help;
295 GtkWidget *text;
297 help = gtk_dialog_new_with_buttons(
298 _("Set type reference"),
299 NULL, 0,
300 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
301 NULL);
302 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
304 text = gtk_label_new(NULL);
305 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
306 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
307 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
308 gtk_label_set_markup(GTK_LABEL(text), _(
309 "Normally ROX-Filer determines the type of a regular file\n"
310 "by matching it's name against a pattern. To change the\n"
311 "type of the file you must rename it.\n"
312 "\n"
313 "Newer file systems can support something called 'Extended\n"
314 "Attributes' which can be used to store additional data with\n"
315 "each file as named parameters. ROX-Filer uses the\n"
316 "'user.mime_type' attribute to store file types.\n"
317 "\n"
318 "File types are only supported for regular files, not\n"
319 "directories, devices, pipes or sockets, and then only\n"
320 "on certain file systems and where the OS implements them.\n"));
322 text = gtk_label_new(_(ATTR_MAN_PAGE));
323 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
324 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
326 g_signal_connect(help, "response",
327 G_CALLBACK(gtk_widget_destroy), NULL);
329 gtk_widget_show_all(help);
332 static void process_message(GUIside *gui_side, const gchar *buffer)
334 ABox *abox = gui_side->abox;
336 if (*buffer == '?')
337 abox_ask(abox, buffer + 1);
338 else if (*buffer == 's')
339 dir_check_this(buffer + 1); /* Update this item */
340 else if (*buffer == '=')
341 abox_add_filename(abox, buffer + 1);
342 else if (*buffer == '#')
343 abox_clear_results(abox);
344 else if (*buffer == 'X')
346 filer_close_recursive(buffer + 1);
347 /* Let child know it's safe to continue... */
348 fputc('X', gui_side->to_child);
349 fflush(gui_side->to_child);
351 else if (*buffer == 'm' || *buffer == 'M')
353 /* Mount / major changes to this path */
354 if (*buffer == 'M')
356 mount_update(TRUE);
357 mount_user_mount(buffer + 1);
359 filer_check_mounted(buffer + 1);
361 else if (*buffer == '/')
362 abox_set_current_object(abox, buffer + 1);
363 else if (*buffer == 'o')
364 filer_opendir(buffer + 1, NULL, NULL);
365 else if (*buffer == '!')
367 gui_side->errors++;
368 abox_log(abox, buffer + 1, "error");
370 else if (*buffer == '<')
371 abox_set_file(abox, 0, buffer+1);
372 else if (*buffer == '>')
374 abox_set_file(abox, 1, buffer+1);
375 abox_show_compare(abox, TRUE);
377 else if (*buffer == '%')
379 abox_set_percentage(abox, atoi(buffer+1));
381 else
382 abox_log(abox, buffer + 1, NULL);
385 /* Called when the child sends us a message */
386 static void message_from_child(gpointer data,
387 gint source,
388 GdkInputCondition condition)
390 char buf[5];
391 GUIside *gui_side = (GUIside *) data;
392 ABox *abox = gui_side->abox;
393 GtkTextBuffer *text_buffer;
395 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
397 if (read_exact(source, buf, 4))
399 ssize_t message_len;
400 char *buffer;
402 buf[4] = '\0';
403 message_len = strtol(buf, NULL, 16);
404 buffer = g_malloc(message_len + 1);
405 if (message_len > 0 && read_exact(source, buffer, message_len))
407 buffer[message_len] = '\0';
408 process_message(gui_side, buffer);
409 g_free(buffer);
410 return;
412 g_printerr("Child died in the middle of a message.\n");
415 if (gui_side->abort_attempts)
416 abox_log(abox, _("\nProcess terminated.\n"), "error");
418 /* The child is dead */
419 gui_side->child = 0;
421 fclose(gui_side->to_child);
422 gui_side->to_child = NULL;
423 close(gui_side->from_child);
424 g_source_remove(gui_side->input_tag);
425 abox_cancel_ask(gui_side->abox);
427 if (gui_side->errors)
429 guchar *report;
431 if (gui_side->errors == 1)
432 report = g_strdup(_("There was one error.\n"));
433 else
434 report = g_strdup_printf(_("There were %d errors.\n"),
435 gui_side->errors);
437 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
439 g_free(report);
441 else if (gui_side->show_info == FALSE)
442 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
445 /* Scans src_dir, calling cb(item, dest_path) for each item */
446 static void for_dir_contents(ForDirCB *cb,
447 const char *src_dir,
448 const char *dest_path)
450 DIR *d;
451 struct dirent *ent;
452 GList *list = NULL, *next;
454 d = mc_opendir(src_dir);
455 if (!d)
457 /* Message displayed is "ERROR reading 'path': message" */
458 printf_send("!%s '%s': %s\n", _("ERROR reading"),
459 src_dir, g_strerror(errno));
460 return;
463 send_dir(src_dir);
465 while ((ent = mc_readdir(d)))
467 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
468 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
469 continue;
470 list = g_list_prepend(list, g_strdup(make_path(src_dir,
471 ent->d_name)));
473 mc_closedir(d);
475 for (next = list; next; next = next->next)
477 cb((char *) next->data, dest_path);
479 g_free(next->data);
481 g_list_free(list);
484 /* Read this many bytes into the buffer. TRUE on success. */
485 static gboolean read_exact(int source, char *buffer, ssize_t len)
487 while (len > 0)
489 ssize_t got;
490 got = read(source, buffer, len);
491 if (got < 1)
492 return FALSE;
493 len -= got;
494 buffer += got;
496 return TRUE;
499 static void send_done(void)
501 printf_send(_("'\nDone\n"));
504 /* Notify the filer that this item has been updated */
505 static void send_check_path(const gchar *path)
507 printf_send("s%s", path);
510 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
511 static void send_mount_path(const gchar *path)
513 printf_send("m%s", path);
516 /* Send a message to the filer process. The first character indicates the
517 * type of the message.
519 static gboolean printf_send(const char *msg, ...)
521 va_list args;
522 gchar *tmp;
524 va_start(args, msg);
525 tmp = g_strdup_vprintf(msg, args);
526 va_end(args);
528 g_string_assign(message, tmp);
529 g_free(tmp);
531 return send_msg();
534 /* Send 'message' to our parent process. TRUE on success. */
535 static gboolean send_msg(void)
537 char len_buffer[5];
538 ssize_t len;
540 g_return_val_if_fail(message->len < 0xffff, FALSE);
542 sprintf(len_buffer, "%04" G_GSIZE_MODIFIER "x", message->len);
543 fwrite(len_buffer, 1, 4, to_parent);
544 len = fwrite(message->str, 1, message->len, to_parent);
545 fflush(to_parent);
546 return len == (ssize_t) message->len;
549 /* Set the directory indicator at the top of the window */
550 static gboolean send_dir(const char *dir)
552 return printf_send("/%s", dir);
555 static gboolean send_error(void)
557 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
560 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
562 gchar code;
564 if (!gui_side->to_child)
565 return;
567 if (response == GTK_RESPONSE_YES)
568 code = 'Y';
569 else if (response == GTK_RESPONSE_NO)
570 code = 'N';
571 else
572 return;
574 fputc(code, gui_side->to_child);
575 fflush(gui_side->to_child);
576 abox_show_compare(gui_side->abox, FALSE);
579 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
581 if (!gui_side->to_child)
582 return;
584 fputc(flag, gui_side->to_child);
585 fflush(gui_side->to_child);
588 static void read_new_entry_text(void)
590 int len;
591 char c;
592 GString *new;
594 new = g_string_new(NULL);
596 for (;;)
598 len = read(from_parent, &c, 1);
599 if (len != 1)
601 fprintf(stderr, "read() error: %s\n",
602 g_strerror(errno));
603 _exit(1); /* Parent died? */
606 if (c == '\n')
607 break;
608 g_string_append_c(new, c);
611 g_free(new_entry_string);
612 new_entry_string = new->str;
613 g_string_free(new, FALSE);
616 static void process_flag(char flag)
618 switch (flag)
620 case 'Q':
621 quiet = !quiet;
622 break;
623 case 'F':
624 o_force = !o_force;
625 break;
626 case 'R':
627 o_recurse = !o_recurse;
628 break;
629 case 'B':
630 o_brief = !o_brief;
631 break;
632 case 'W':
633 o_newer = !o_newer;
634 break;
635 case 'E':
636 read_new_entry_text();
637 break;
638 default:
639 printf_send("!ERROR: Bad message '%c'\n", flag);
640 break;
644 /* If the parent has sent any flag toggles, read them */
645 static void check_flags(void)
647 fd_set set;
648 int got;
649 char retval;
650 struct timeval tv;
652 FD_ZERO(&set);
654 while (1)
656 FD_SET(from_parent, &set);
657 tv.tv_sec = 0;
658 tv.tv_usec = 0;
659 got = select(from_parent + 1, &set, NULL, NULL, &tv);
661 if (got == -1)
662 g_error("select() failed: %s\n", g_strerror(errno));
663 else if (!got)
664 return;
666 got = read(from_parent, &retval, 1);
667 if (got != 1)
668 g_error("read() error: %s\n", g_strerror(errno));
670 process_flag(retval);
674 /* Read until the user sends a reply. If ignore_quiet is TRUE then
675 * the user MUST click Yes or No, else treat quiet on as Yes.
676 * If the user needs prompting then does send_msg().
678 static gboolean printf_reply(int fd, gboolean ignore_quiet,
679 const char *msg, ...)
681 ssize_t len;
682 char retval;
683 va_list args;
684 gchar *tmp;
686 if (quiet && !ignore_quiet)
687 return TRUE;
689 va_start(args, msg);
690 tmp = g_strdup_vprintf(msg, args);
691 va_end(args);
693 g_string_assign(message, tmp);
694 g_free(tmp);
696 send_msg();
698 while (1)
700 len = read(fd, &retval, 1);
701 if (len != 1)
703 fprintf(stderr, "read() error: %s\n",
704 g_strerror(errno));
705 _exit(1); /* Parent died? */
708 switch (retval)
710 case 'Y':
711 printf_send("' %s\n", _("Yes"));
712 return TRUE;
713 case 'N':
714 printf_send("' %s\n", _("No"));
715 return FALSE;
716 default:
717 process_flag(retval);
718 break;
723 static void abort_operation(GtkWidget *widget, gpointer data)
725 GUIside *gui_side = (GUIside *) data;
727 if (gui_side->child)
729 if (gui_side->abort_attempts == 0)
731 abox_log(ABOX(widget),
732 _("\nAsking child process to terminate...\n"),
733 "error");
734 kill(-gui_side->child, SIGTERM);
736 else
738 abox_log(ABOX(widget),
739 _("\nTrying to KILL run-away process...\n"),
740 "error");
741 kill(-gui_side->child, SIGKILL);
742 kill(-gui_side->child, SIGCONT);
744 gui_side->abort_attempts++;
746 else
747 gtk_widget_destroy(widget);
750 static void destroy_action_window(GtkWidget *widget, gpointer data)
752 GUIside *gui_side = (GUIside *) data;
754 if (gui_side->child)
756 kill(-gui_side->child, SIGTERM);
757 fclose(gui_side->to_child);
758 close(gui_side->from_child);
759 g_source_remove(gui_side->input_tag);
762 g_free(gui_side);
764 one_less_window();
767 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
768 * (NULL on failure). The child calls func().
770 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
771 int force, int brief, int recurse, int newer)
773 gboolean autoq;
774 int filedes[4]; /* 0 and 2 are for reading */
775 GUIside *gui_side;
776 pid_t child;
777 struct sigaction act;
779 if (pipe(filedes))
781 report_error("pipe: %s", g_strerror(errno));
782 gtk_widget_destroy(abox);
783 return NULL;
786 if (pipe(filedes + 2))
788 close(filedes[0]);
789 close(filedes[1]);
790 report_error("pipe: %s", g_strerror(errno));
791 gtk_widget_destroy(abox);
792 return NULL;
795 autoq = gtk_toggle_button_get_active(
796 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
798 o_force = force;
799 o_brief = brief;
800 o_recurse = recurse;
801 o_newer = newer;
803 child = fork();
804 switch (child)
806 case -1:
807 report_error("fork: %s", g_strerror(errno));
808 gtk_widget_destroy(abox);
809 return NULL;
810 case 0:
811 /* We are the child */
813 /* Create a new process group */
814 setpgid(0, 0);
816 quiet = autoq;
818 dir_drop_all_dnotifies();
820 /* Reset the SIGCHLD handler */
821 act.sa_handler = SIG_DFL;
822 sigemptyset(&act.sa_mask);
823 act.sa_flags = 0;
824 sigaction(SIGCHLD, &act, NULL);
826 message = g_string_new(NULL);
827 close(filedes[0]);
828 close(filedes[3]);
829 to_parent = fdopen(filedes[1], "wb");
830 from_parent = filedes[2];
831 func(data);
832 send_dir("");
833 _exit(0);
836 /* We are the parent */
837 close(filedes[1]);
838 close(filedes[2]);
839 gui_side = g_new(GUIside, 1);
840 gui_side->from_child = filedes[0];
841 gui_side->to_child = fdopen(filedes[3], "wb");
842 gui_side->child = child;
843 gui_side->errors = 0;
844 gui_side->show_info = FALSE;
845 gui_side->default_string = NULL;
846 gui_side->entry_string_func = NULL;
847 gui_side->abort_attempts = 0;
849 gui_side->abox = ABOX(abox);
850 g_signal_connect(abox, "destroy",
851 G_CALLBACK(destroy_action_window), gui_side);
853 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
854 g_signal_connect(abox, "flag_toggled",
855 G_CALLBACK(flag_toggled), gui_side);
856 g_signal_connect(abox, "abort_operation",
857 G_CALLBACK(abort_operation), gui_side);
859 gui_side->input_tag = gdk_input_add_full(gui_side->from_child,
860 GDK_INPUT_READ,
861 message_from_child,
862 gui_side, NULL);
864 return gui_side;
867 /* ACTIONS ON ONE ITEM */
869 /* These may call themselves recursively, or ask questions, etc */
871 /* Updates the global size_tally, file_counter and dir_counter */
872 static void do_usage(const char *src_path, const char *unused)
874 struct stat info;
876 check_flags();
878 if (mc_lstat(src_path, &info))
880 printf_send("'%s:\n", src_path);
881 send_error();
883 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
885 file_counter++;
886 size_tally += info.st_size;
888 else if (S_ISDIR(info.st_mode))
890 dir_counter++;
891 if (printf_reply(from_parent, FALSE,
892 _("?Count contents of %s?"), src_path))
894 char *safe_path;
895 safe_path = g_strdup(src_path);
896 for_dir_contents(do_usage, safe_path, safe_path);
897 g_free(safe_path);
900 else
901 file_counter++;
904 /* dest_path is the dir containing src_path */
905 static void do_delete(const char *src_path, const char *unused)
907 struct stat info;
908 gboolean write_prot;
909 char *safe_path;
911 check_flags();
913 if (mc_lstat(src_path, &info))
915 send_error();
916 return;
919 write_prot = S_ISLNK(info.st_mode) ? FALSE
920 : access(src_path, W_OK) != 0;
921 if (write_prot || !quiet)
923 int res;
925 printf_send("<%s", src_path);
926 printf_send(">");
927 res=printf_reply(from_parent, write_prot && !o_force,
928 _("?Delete %s'%s'?"),
929 write_prot ? _("WRITE-PROTECTED ") : "",
930 src_path);
931 printf_send("<");
932 if (!res)
933 return;
935 else if (!o_brief)
936 printf_send(_("'Deleting '%s'\n"), src_path);
938 safe_path = g_strdup(src_path);
940 if (S_ISDIR(info.st_mode))
942 for_dir_contents(do_delete, safe_path, safe_path);
943 if (rmdir(safe_path))
945 g_free(safe_path);
946 send_error();
947 return;
949 printf_send(_("'Directory '%s' deleted\n"), safe_path);
950 send_mount_path(safe_path);
952 else if (unlink(src_path))
953 send_error();
954 else
956 send_check_path(safe_path);
957 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
959 gchar *dir;
960 dir = g_path_get_dirname(safe_path);
961 send_check_path(dir);
962 g_free(dir);
966 g_free(safe_path);
969 static void do_eject(const char *path)
971 const char *argv[3] = {NULL, NULL, NULL};
972 char *err;
974 check_flags();
976 if (!quiet)
978 int res;
979 printf_send("<%s", path);
980 printf_send(">");
981 res=printf_reply(from_parent, !o_force,
982 _("?Eject '%s'?"),
983 path);
984 printf_send("<");
985 if (!res)
986 return;
988 else if (!o_brief)
989 printf_send(_("'Eject '%s'\n"), path);
991 /* Need to close all sub-directories now, or we
992 * can't unmount if dnotify is used.
995 char c = '?';
996 printf_send("X%s", path);
997 /* Wait until it's safe... */
998 read(from_parent, &c, 1);
999 g_return_if_fail(c == 'X');
1002 argv[0] = "eject";
1003 argv[1] = path;
1004 argv[2] = NULL;
1005 err = fork_exec_wait(argv);
1006 if (err)
1008 printf_send(_("!%s\neject failed\n"), err);
1009 g_free(err);
1012 printf_send("M%s", path);
1016 /* path is the item to check. If is is a directory then we may recurse
1017 * (unless prune is used).
1019 static void do_find(const char *path, const char *unused)
1021 FindInfo info;
1023 check_flags();
1025 if (!quiet)
1027 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
1028 return;
1031 for (;;)
1033 if (new_entry_string)
1035 find_condition_free(find_condition);
1036 find_condition = find_compile(new_entry_string);
1037 null_g_free(&new_entry_string);
1040 if (find_condition)
1041 break;
1043 printf_send(_("!Invalid find condition - "
1044 "change it and try again\n"));
1045 if (!printf_reply(from_parent, TRUE,
1046 _("?Check '%s'?"), path))
1047 return;
1050 if (mc_lstat(path, &info.stats))
1052 send_error();
1053 printf_send(_("'(while checking '%s')\n"), path);
1054 return;
1057 info.fullpath = path;
1058 time(&info.now); /* XXX: Not for each check! */
1060 info.leaf = g_basename(path);
1061 info.prune = FALSE;
1062 if (find_test_condition(find_condition, &info))
1063 printf_send("=%s", path);
1065 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1067 char *safe_path;
1068 safe_path = g_strdup(path);
1069 for_dir_contents(do_find, safe_path, safe_path);
1070 g_free(safe_path);
1074 /* Like mode_compile(), but ignores spaces and bracketed bits */
1075 static struct mode_change *nice_mode_compile(const char *mode_string,
1076 unsigned int masked_ops)
1078 GString *new;
1079 int brackets = 0;
1080 struct mode_change *retval = NULL;
1082 new = g_string_new(NULL);
1084 for (; *mode_string; mode_string++)
1086 if (*mode_string == '(')
1087 brackets++;
1088 if (*mode_string == ')')
1090 brackets--;
1091 if (brackets < 0)
1092 break;
1093 continue;
1096 if (brackets == 0 && *mode_string != ' ')
1097 g_string_append_c(new, *mode_string);
1100 if (brackets == 0)
1101 retval = mode_compile(new->str, masked_ops);
1102 g_string_free(new, TRUE);
1103 return retval;
1106 static void do_chmod(const char *path, const char *unused)
1108 struct stat info;
1109 mode_t new_mode;
1111 check_flags();
1113 if (mc_lstat(path, &info))
1115 send_error();
1116 return;
1118 if (S_ISLNK(info.st_mode))
1119 return;
1121 if (!quiet)
1123 int res;
1124 printf_send("<%s", path);
1125 printf_send(">");
1126 res=printf_reply(from_parent, FALSE,
1127 _("?Change permissions of '%s'?"), path);
1128 printf_send("<");
1129 if (!res)
1130 return;
1132 else if (!o_brief)
1133 printf_send(_("'Changing permissions of '%s'\n"), path);
1135 for (;;)
1137 if (new_entry_string)
1139 if (mode_change)
1140 mode_free(mode_change);
1141 mode_change = nice_mode_compile(new_entry_string,
1142 MODE_MASK_ALL);
1143 null_g_free(&new_entry_string);
1146 if (mode_change)
1147 break;
1149 printf_send(
1150 _("!Invalid mode command - change it and try again\n"));
1151 if (!printf_reply(from_parent, TRUE,
1152 _("?Change permissions of '%s'?"), path))
1153 return;
1156 if (mc_lstat(path, &info))
1158 send_error();
1159 return;
1161 if (S_ISLNK(info.st_mode))
1162 return;
1164 new_mode = mode_adjust(info.st_mode, mode_change);
1165 if (chmod(path, new_mode))
1167 send_error();
1168 return;
1171 send_check_path(path);
1173 if (S_ISDIR(info.st_mode))
1175 send_mount_path(path);
1177 if (o_recurse)
1179 guchar *safe_path;
1180 safe_path = g_strdup(path);
1181 for_dir_contents(do_chmod, safe_path, safe_path);
1182 g_free(safe_path);
1187 static void do_settype(const char *path, const char *unused)
1189 struct stat info;
1191 check_flags();
1193 if (mc_lstat(path, &info))
1195 send_error();
1196 return;
1198 if (S_ISLNK(info.st_mode))
1199 return;
1201 if (!quiet)
1203 int res;
1204 printf_send("<%s", path);
1205 printf_send(">");
1206 res=printf_reply(from_parent, FALSE,
1207 _("?Change type of '%s'?"), path);
1208 printf_send("<");
1209 if (!res)
1210 return;
1213 for (;;)
1215 if (new_entry_string)
1217 type_change = mime_type_lookup(new_entry_string);
1218 null_g_free(&new_entry_string);
1221 if (type_change)
1222 break;
1224 printf_send(_("!Invalid type - "
1225 "change it and try again\n"));
1226 if (!printf_reply(from_parent, TRUE,
1227 _("?Change type of '%s'?"), path))
1228 return;
1231 if (mc_lstat(path, &info))
1233 send_error();
1234 return;
1236 if (S_ISLNK(info.st_mode))
1237 return;
1239 if (S_ISREG(info.st_mode))
1241 if (!o_brief)
1243 const char *comment;
1245 comment = mime_type_comment(type_change);
1246 printf_send(_("'Changing type of '%s' to '%s'\n"), path,
1247 comment);
1250 if (xtype_set(path, type_change))
1252 send_error();
1253 return;
1256 send_check_path(path);
1258 else if (S_ISDIR(info.st_mode))
1260 if (o_recurse)
1262 guchar *safe_path;
1263 safe_path = g_strdup(path);
1264 for_dir_contents(do_settype, safe_path, unused);
1265 g_free(safe_path);
1270 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1271 * is set then that is the new leafname, otherwise the leafname stays
1272 * the same.
1274 static const char *make_dest_path(const char *object, const char *dir)
1276 const char *leaf;
1278 if (action_leaf)
1279 leaf = action_leaf;
1280 else
1282 leaf = strrchr(object, '/');
1283 if (!leaf)
1284 leaf = object; /* Error? */
1285 else
1286 leaf++;
1289 return make_path(dir, leaf);
1292 /* If action_leaf is not NULL it specifies the new leaf name */
1293 static void do_copy2(const char *path, const char *dest)
1295 const char *dest_path;
1296 struct stat info;
1297 struct stat dest_info;
1299 check_flags();
1301 dest_path = make_dest_path(path, dest);
1303 if (mc_lstat(path, &info))
1305 send_error();
1306 return;
1309 if (mc_lstat(dest_path, &dest_info) == 0)
1311 int err;
1312 gboolean merge;
1314 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1316 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1318 /* Newer; keep going */
1320 else
1322 printf_send("<%s", path);
1323 printf_send(">%s", dest_path);
1324 if (!printf_reply(from_parent, TRUE,
1325 _("?'%s' already exists - %s?"),
1326 dest_path,
1327 merge ? _("merge contents")
1328 : _("overwrite")))
1329 return;
1332 if (!merge)
1334 if (S_ISDIR(dest_info.st_mode))
1335 err = rmdir(dest_path);
1336 else
1337 err = unlink(dest_path);
1339 if (err)
1341 send_error();
1342 if (errno != ENOENT)
1343 return;
1344 printf_send(_("'Trying copy anyway...\n"));
1348 else if (!quiet)
1350 printf_send("<%s", path);
1351 printf_send(">");
1352 if (!printf_reply(from_parent, FALSE,
1353 _("?Copy %s as %s?"), path, dest_path))
1354 return;
1356 else if (!o_brief || S_ISDIR(info.st_mode))
1357 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1359 if (S_ISDIR(info.st_mode))
1361 mode_t mode = info.st_mode;
1362 char *safe_path, *safe_dest;
1363 struct stat dest_info;
1364 gboolean exists;
1366 safe_path = g_strdup(path);
1367 safe_dest = g_strdup(dest_path);
1369 exists = !mc_lstat(dest_path, &dest_info);
1371 if (exists && !S_ISDIR(dest_info.st_mode))
1372 printf_send(_("!ERROR: Destination already exists, "
1373 "but is not a directory\n"));
1374 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1375 send_error();
1376 else
1378 if (!exists)
1379 /* (just been created then) */
1380 send_check_path(dest_path);
1382 action_leaf = NULL;
1383 for_dir_contents(do_copy2, safe_path, safe_dest);
1384 /* Note: dest_path now invalid... */
1386 if (!exists)
1388 struct utimbuf utb;
1390 /* We may have created the directory with
1391 * more permissions than the source so that
1392 * we could write to it... change it back now.
1394 if (chmod(safe_dest, mode))
1396 /* Some filesystems don't support
1397 * SetGID and SetUID bits. Ignore
1398 * these errors.
1400 if (errno != EPERM)
1401 send_error();
1404 /* Also, try to preserve the timestamps */
1405 utb.actime = info.st_atime;
1406 utb.modtime = info.st_mtime;
1408 utime(safe_dest, &utb);
1412 g_free(safe_path);
1413 g_free(safe_dest);
1415 else if (S_ISLNK(info.st_mode))
1417 char *target;
1419 /* Not all versions of cp(1) can make symlinks,
1420 * so we special-case it.
1423 target = readlink_dup(path);
1424 if (target)
1426 if (symlink(target, dest_path))
1427 send_error();
1428 else
1429 send_check_path(dest_path);
1431 g_free(target);
1433 else
1434 send_error();
1436 else
1438 guchar *error;
1440 error = copy_file(path, dest_path);
1442 if (error)
1444 printf_send(_("!%s\nFailed to copy '%s'\n"),
1445 error, path);
1446 g_free(error);
1448 else
1449 send_check_path(dest_path);
1453 /* If action_leaf is not NULL it specifies the new leaf name */
1454 static void do_move2(const char *path, const char *dest)
1456 const char *dest_path;
1457 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1458 struct stat info2;
1459 gboolean is_dir;
1460 char *err;
1462 check_flags();
1464 dest_path = make_dest_path(path, dest);
1466 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1468 if (access(dest_path, F_OK) == 0)
1470 struct stat info;
1471 int err;
1473 if (mc_lstat(dest_path, &info))
1475 send_error();
1476 return;
1479 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1481 /* Newer; keep going */
1483 else
1485 printf_send("<%s", path);
1486 printf_send(">%s", dest_path);
1487 if (!printf_reply(from_parent, TRUE,
1488 _("?'%s' already exists - overwrite?"),
1489 dest_path))
1490 return;
1493 if (S_ISDIR(info.st_mode))
1494 err = rmdir(dest_path);
1495 else
1496 err = unlink(dest_path);
1498 if (err)
1500 send_error();
1501 if (errno != ENOENT)
1502 return;
1503 printf_send(_("'Trying move anyway...\n"));
1506 else if (!quiet)
1508 printf_send("<%s", path);
1509 printf_send(">");
1510 if (!printf_reply(from_parent, FALSE,
1511 _("?Move %s as %s?"), path, dest_path))
1512 return;
1514 else if (!o_brief)
1515 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1517 argv[2] = path;
1518 argv[3] = dest_path;
1520 err = fork_exec_wait(argv);
1521 if (err)
1523 printf_send(_("!%s\nFailed to move %s as %s\n"),
1524 err, path, dest_path);
1525 g_free(err);
1527 else
1529 send_check_path(dest_path);
1531 if (is_dir)
1532 send_mount_path(path);
1533 else
1534 send_check_path(path);
1538 /* Copy path to dest.
1539 * Check that path not copied into itself.
1541 static void do_copy(const char *path, const char *dest)
1543 if (is_sub_dir(make_dest_path(path, dest), path))
1544 printf_send(_("!ERROR: Can't copy object into itself\n"));
1545 else
1547 do_copy2(path, dest);
1548 send_check_path(dest);
1552 /* Move path to dest.
1553 * Check that path not moved into itself.
1555 static void do_move(const char *path, const char *dest)
1557 if (is_sub_dir(make_dest_path(path, dest), path))
1558 printf_send(
1559 _("!ERROR: Can't move/rename object into itself\n"));
1560 else
1562 do_move2(path, dest);
1563 send_check_path(dest);
1567 /* Common code for do_link_relative() and do_link_absolute(). */
1568 static void do_link(const char *path, const char *dest_path)
1570 if (quiet)
1571 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1572 else {
1573 printf_send("<%s", path);
1574 printf_send(">");
1575 if (!printf_reply(from_parent, FALSE,
1576 _("?Link %s as %s?"), path, dest_path))
1577 return;
1580 if (symlink(path, dest_path))
1581 send_error();
1582 else
1583 send_check_path(dest_path);
1586 static void do_link_relative(const char *path, const char *dest)
1588 char *rel_path;
1589 const char *dest_path;
1591 dest_path = make_dest_path(path, dest);
1593 check_flags();
1595 rel_path = get_relative_path(dest_path, path);
1596 do_link(rel_path, dest_path);
1597 g_free(rel_path);
1600 static void do_link_absolute(const char *path, const char *dest)
1602 check_flags();
1603 do_link(path, make_dest_path(path, dest));
1606 /* Mount/umount this item (depending on 'mount') */
1607 static void do_mount(const guchar *path, gboolean mount)
1609 const char *argv[3] = {NULL, NULL, NULL};
1610 char *err;
1612 check_flags();
1614 argv[0] = mount ? "mount" : "umount";
1615 argv[1] = path;
1617 if (quiet)
1618 printf_send(mount ? _("'Mounting %s\n")
1619 : _("'Unmounting %s\n"),
1620 path);
1621 else if (!printf_reply(from_parent, FALSE,
1622 mount ? _("?Mount %s?")
1623 : _("?Unmount %s?"),
1624 path))
1625 return;
1627 if (!mount)
1629 char c = '?';
1630 /* Need to close all sub-directories now, or we
1631 * can't unmount if dnotify is used.
1633 printf_send("X%s", path);
1634 /* Wait until it's safe... */
1635 read(from_parent, &c, 1);
1636 g_return_if_fail(c == 'X');
1639 err = fork_exec_wait(argv);
1640 if (err)
1642 printf_send(mount ?
1643 _("!%s\nMount failed\n") :
1644 _("!%s\nUnmount failed\n"), err);
1645 g_free(err);
1647 /* Mount may have worked even on error, eg if we try to mount
1648 * a read-only disk read/write, it gets mounted read-only
1649 * with an error.
1651 if (mount && mount_is_mounted(path, NULL, NULL))
1652 printf_send(_("'(seems to be mounted now anyway)\n"));
1653 else
1654 return;
1657 printf_send("M%s", path);
1658 if (mount && mount_open_dir)
1659 printf_send("o%s", path);
1662 /* CHILD MAIN LOOPS */
1664 /* After forking, the child calls one of these functions */
1666 /* We use a double for total size in order to count beyond 4Gb */
1667 static void usage_cb(gpointer data)
1669 GList *paths = (GList *) data;
1670 double total_size = 0;
1671 int n, i, per;
1673 n=g_list_length(paths);
1674 dir_counter = file_counter = 0;
1676 for (i=0; paths; paths = paths->next, i++)
1678 guchar *path = (guchar *) paths->data;
1680 send_dir(path);
1682 size_tally = 0;
1684 if(n>1 && i>0)
1686 per=100*i/n;
1687 printf_send("%%%d", per);
1689 do_usage(path, NULL);
1691 printf_send("'%s: %s\n",
1692 g_basename(path),
1693 format_double_size(size_tally));
1694 total_size += size_tally;
1696 printf_send("%%-1");
1698 g_string_printf(message, _("'\nTotal: %s ("),
1699 format_double_size(total_size));
1701 if (file_counter)
1702 g_string_append_printf(message,
1703 "%ld %s%s", file_counter,
1704 file_counter == 1 ? _("file") : _("files"),
1705 dir_counter ? ", " : ")\n");
1707 if (file_counter == 0 && dir_counter == 0)
1708 g_string_append(message, _("no directories)\n"));
1709 else if (dir_counter)
1710 g_string_append_printf(message,
1711 "%ld %s)\n", dir_counter,
1712 dir_counter == 1 ? _("directory")
1713 : _("directories"));
1715 send_msg();
1718 #ifdef DO_MOUNT_POINTS
1719 static void mount_cb(gpointer data)
1721 GList *paths = (GList *) data;
1722 gboolean mount_points = FALSE;
1723 int n, i, per;
1725 n=g_list_length(paths);
1726 for (i=0; paths; paths = paths->next, i++)
1728 guchar *path = (guchar *) paths->data;
1729 guchar *target;
1731 target = pathdup(path);
1732 if (!target)
1733 target = path;
1735 if(n>1 && i>0)
1737 per=100*i/n;
1738 printf_send("%%%d", per);
1740 if (mount_is_mounted(target, NULL, NULL) ||
1741 g_hash_table_lookup(fstab_mounts, target))
1743 mount_points = TRUE;
1744 do_mount(target, mount_mount); /* Mount */
1747 if (target != path)
1748 g_free(target);
1751 if (mount_points)
1752 send_done();
1753 else
1754 printf_send(_("!No mount points selected!\n"));
1756 #endif
1758 /* (use g_dirname() instead?) */
1759 static guchar *dirname(guchar *path)
1761 guchar *slash;
1763 slash = strrchr(path, '/');
1764 g_return_val_if_fail(slash != NULL, g_strdup(path));
1766 if (slash != path)
1767 return g_strndup(path, slash - path);
1768 return g_strdup("/");
1771 static void delete_cb(gpointer data)
1773 GList *paths = (GList *) data;
1774 int n, i, per;
1776 n=g_list_length(paths);
1777 for (i=0; paths; paths = paths->next, i++)
1779 guchar *path = (guchar *) paths->data;
1780 guchar *dir;
1782 dir = dirname(path);
1783 send_dir(dir);
1785 if(n>1 && i>0)
1787 per=100*i/n;
1788 printf_send("%%%d", per);
1790 do_delete(path, dir);
1792 g_free(dir);
1795 send_done();
1798 static void eject_cb(gpointer data)
1800 GList *paths = (GList *) data;
1801 int n, i, per;
1803 n=g_list_length(paths);
1805 for (i=0; paths; paths = paths->next, i++)
1807 guchar *path = (guchar *) paths->data;
1809 if(n>1 && i>0)
1811 per=100*i/n;
1812 printf_send("%%%d", per);
1814 send_dir(path);
1816 do_eject(path);
1819 send_done();
1822 static void find_cb(gpointer data)
1824 GList *all_paths = (GList *) data;
1825 GList *paths;
1827 while (1)
1829 for (paths = all_paths; paths; paths = paths->next)
1831 guchar *path = (guchar *) paths->data;
1833 send_dir(path);
1835 do_find(path, NULL);
1838 if (!printf_reply(from_parent, TRUE,
1839 _("?Another search?")))
1840 break;
1841 printf_send("#");
1844 send_done();
1847 static void chmod_cb(gpointer data)
1849 GList *paths = (GList *) data;
1850 int n, i, per;
1852 n=g_list_length(paths);
1854 for (i=0; paths; paths = paths->next, i++)
1856 guchar *path = (guchar *) paths->data;
1857 struct stat info;
1859 if(n>1 && i>0)
1861 per=100*i/n;
1862 printf_send("%%%d", per);
1864 send_dir(path);
1866 if (mc_stat(path, &info) != 0)
1867 send_error();
1868 else if (S_ISLNK(info.st_mode))
1869 printf_send(_("!'%s' is a symbolic link\n"),
1870 g_basename(path));
1871 else
1872 do_chmod(path, NULL);
1875 send_done();
1878 static void settype_cb(gpointer data)
1880 GList *paths = (GList *) data;
1881 int n, i, per;
1883 n=g_list_length(paths);
1885 for (i=0; paths; paths = paths->next, i++)
1887 guchar *path = (guchar *) paths->data;
1888 struct stat info;
1890 if(n>1 && i>0)
1892 per=100*i/n;
1893 printf_send("%%%d", per);
1895 send_dir(path);
1897 if (mc_stat(path, &info) != 0)
1898 send_error();
1899 else if (S_ISLNK(info.st_mode))
1900 printf_send(_("!'%s' is a symbolic link\n"),
1901 g_basename(path));
1902 else
1903 do_settype(path, NULL);
1906 send_done();
1909 static void list_cb(gpointer data)
1911 GList *paths = (GList *) data;
1912 int n, i, per;
1914 n=g_list_length(paths);
1916 for (i=0; paths; paths = paths->next, i++)
1918 if(n>1 && i>0)
1920 per=100*i/n;
1921 printf_send("%%%d", per);
1923 send_dir((char *) paths->data);
1925 action_do_func((char *) paths->data, action_dest);
1928 send_done();
1931 /* EXTERNAL INTERFACE */
1933 void action_find(GList *paths)
1935 GUIside *gui_side;
1936 GtkWidget *abox;
1938 if (!paths)
1940 report_error(_("You need to select some items "
1941 "to search through"));
1942 return;
1945 if (!last_find_string)
1946 last_find_string = g_strdup("'core'");
1948 new_entry_string = last_find_string;
1950 abox = abox_new(_("Find"), FALSE);
1951 gui_side = start_action(abox, find_cb, paths,
1952 o_action_force.int_value,
1953 o_action_brief.int_value,
1954 o_action_recurse.int_value,
1955 o_action_newer.int_value);
1956 if (!gui_side)
1957 return;
1959 abox_add_results(ABOX(abox));
1961 gui_side->default_string = &last_find_string;
1962 abox_add_entry(ABOX(abox), last_find_string,
1963 new_help_button(show_condition_help, NULL));
1964 g_signal_connect(ABOX(abox)->entry, "changed",
1965 G_CALLBACK(entry_changed), gui_side);
1966 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1968 gui_side->show_info = TRUE;
1969 gui_side->entry_string_func = set_find_string_colour;
1971 number_of_windows++;
1972 gtk_widget_show(abox);
1975 /* Count disk space used by selected items */
1976 void action_usage(GList *paths)
1978 GUIside *gui_side;
1979 GtkWidget *abox;
1981 if (!paths)
1983 report_error(_("You need to select some items to count"));
1984 return;
1987 abox = abox_new(_("Disk Usage"), TRUE);
1989 gui_side = start_action(abox, usage_cb, paths,
1990 o_action_force.int_value,
1991 o_action_brief.int_value,
1992 o_action_recurse.int_value,
1993 o_action_newer.int_value);
1994 if (!gui_side)
1995 return;
1997 gui_side->show_info = TRUE;
1999 number_of_windows++;
2001 gtk_widget_show(abox);
2004 /* Mount/unmount listed items (paths).
2005 * Free the list after this function returns.
2006 * If open_dir is TRUE and the dir is successfully mounted, open it.
2007 * quiet can be -1 for default.
2009 void action_mount(GList *paths, gboolean open_dir, gboolean mount, int quiet)
2011 #ifdef DO_MOUNT_POINTS
2012 GUIside *gui_side;
2013 GtkWidget *abox;
2015 if (quiet == -1)
2016 quiet = o_action_mount.int_value;
2018 mount_open_dir = open_dir;
2019 mount_mount = mount;
2021 abox = abox_new(_("Mount / Unmount"), quiet);
2022 gui_side = start_action(abox, mount_cb, paths,
2023 o_action_force.int_value,
2024 o_action_brief.int_value,
2025 o_action_recurse.int_value,
2026 o_action_newer.int_value);
2027 if (!gui_side)
2028 return;
2030 number_of_windows++;
2031 gtk_widget_show(abox);
2032 #else
2033 report_error(
2034 _("ROX-Filer does not yet support mount points on your "
2035 "system. Sorry."));
2036 #endif /* DO_MOUNT_POINTS */
2039 /* Delete these paths */
2040 void action_delete(GList *paths)
2042 GUIside *gui_side;
2043 GtkWidget *abox;
2045 if (!remove_pinned_ok(paths))
2046 return;
2048 abox = abox_new(_("Delete"), o_action_delete.int_value);
2049 gui_side = start_action(abox, delete_cb, paths,
2050 o_action_force.int_value,
2051 o_action_brief.int_value,
2052 o_action_recurse.int_value,
2053 o_action_newer.int_value);
2054 if (!gui_side)
2055 return;
2057 abox_add_flag(ABOX(abox),
2058 _("Force"), _("Don't confirm deletion of non-writeable items"),
2059 'F', o_action_force.int_value);
2060 abox_add_flag(ABOX(abox),
2061 _("Brief"), _("Only log directories being deleted"),
2062 'B', o_action_brief.int_value);
2064 number_of_windows++;
2065 gtk_widget_show(abox);
2068 /* Change the permissions of the selected items */
2069 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
2071 GtkWidget *abox;
2072 GUIside *gui_side;
2073 static GList *presets = NULL;
2074 gboolean recurse = force_recurse || o_action_recurse.int_value;
2076 if (!paths)
2078 report_error(_("You need to select the items "
2079 "whose permissions you want to change"));
2080 return;
2083 if (!presets)
2085 presets = g_list_append(presets, (gchar *)
2086 _("a+x (Make executable/searchable)"));
2087 presets = g_list_append(presets, (gchar *)
2088 _("a-x (Make non-executable/non-searchable)"));
2089 presets = g_list_append(presets, (gchar *)
2090 _("u+rw (Give owner read+write)"));
2091 presets = g_list_append(presets, (gchar *)
2092 _("go-rwx (Private - owner access only)"));
2093 presets = g_list_append(presets, (gchar *)
2094 _("go=u-w (Public access, not write)"));
2097 if (!last_chmod_string)
2098 last_chmod_string = g_strdup((guchar *) presets->data);
2100 if (action)
2101 new_entry_string = g_strdup(action);
2102 else
2103 new_entry_string = g_strdup(last_chmod_string);
2105 abox = abox_new(_("Permissions"), FALSE);
2106 gui_side = start_action(abox, chmod_cb, paths,
2107 o_action_force.int_value,
2108 o_action_brief.int_value,
2109 recurse,
2110 o_action_newer.int_value);
2112 if (!gui_side)
2113 goto out;
2115 abox_add_flag(ABOX(abox),
2116 _("Brief"), _("Don't list processed files"),
2117 'B', o_action_brief.int_value);
2118 abox_add_flag(ABOX(abox),
2119 _("Recurse"), _("Also change contents of subdirectories"),
2120 'R', recurse);
2122 gui_side->default_string = &last_chmod_string;
2123 abox_add_combo(ABOX(abox), _("Command:"), presets, new_entry_string,
2124 new_help_button(show_chmod_help, NULL));
2126 g_signal_connect(ABOX(abox)->entry, "changed",
2127 G_CALLBACK(entry_changed), gui_side);
2128 #if 0
2129 g_signal_connect_swapped(gui_side->entry, "activate",
2130 G_CALLBACK(gtk_button_clicked),
2131 gui_side->yes);
2132 #endif
2134 number_of_windows++;
2135 gtk_widget_show(abox);
2137 out:
2138 null_g_free(&new_entry_string);
2141 /* Set the MIME type of the selected items */
2142 void action_settype(GList *paths, gboolean force_recurse, const char *oldtype)
2144 GtkWidget *abox;
2145 GUIside *gui_side;
2146 GList *presets = NULL;
2147 gboolean recurse = force_recurse || o_action_recurse.int_value;
2149 if (!paths)
2151 report_error(_("You need to select the items "
2152 "whose type you want to change"));
2153 return;
2156 if (!last_settype_string)
2157 last_settype_string = g_strdup("text/plain");
2159 if (oldtype)
2160 new_entry_string = g_strdup(oldtype);
2161 else
2162 new_entry_string = g_strdup(last_settype_string);
2164 abox = abox_new(_("Set type"), FALSE);
2165 gui_side = start_action(abox, settype_cb, paths,
2166 o_action_force.int_value,
2167 o_action_brief.int_value,
2168 recurse,
2169 o_action_newer.int_value);
2171 if (!gui_side)
2172 goto out;
2174 abox_add_flag(ABOX(abox),
2175 _("Brief"), _("Don't list processed files"),
2176 'B', o_action_brief.int_value);
2177 abox_add_flag(ABOX(abox),
2178 _("Recurse"), _("Change contents of subdirectories"),
2179 'R', recurse);
2181 gui_side->default_string = &last_settype_string;
2183 /* Note: get the list again each time -- it can change */
2184 presets = mime_type_name_list();
2185 abox_add_combo(ABOX(abox), _("Type:"), presets, new_entry_string,
2186 new_help_button(show_settype_help, NULL));
2187 g_list_free(presets);
2189 g_signal_connect(ABOX(abox)->entry, "changed",
2190 G_CALLBACK(entry_changed), gui_side);
2192 number_of_windows++;
2193 gtk_widget_show(abox);
2195 out:
2196 null_g_free(&new_entry_string);
2199 /* If leaf is NULL then the copy has the same name as the original.
2200 * quiet can be -1 for default.
2202 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
2204 GUIside *gui_side;
2205 GtkWidget *abox;
2207 if (quiet == -1)
2208 quiet = o_action_copy.int_value;
2210 action_dest = dest;
2211 action_leaf = leaf;
2212 action_do_func = do_copy;
2214 abox = abox_new(_("Copy"), quiet);
2215 gui_side = start_action(abox, list_cb, paths,
2216 o_action_force.int_value,
2217 o_action_brief.int_value,
2218 o_action_recurse.int_value,
2219 o_action_newer.int_value);
2220 if (!gui_side)
2221 return;
2223 abox_add_flag(ABOX(abox),
2224 _("Newer"),
2225 _("Only over-write if source is newer than destination."),
2226 'W', o_action_newer.int_value);
2227 abox_add_flag(ABOX(abox),
2228 _("Brief"), _("Only log directories as they are copied"),
2229 'B', o_action_brief.int_value);
2231 number_of_windows++;
2232 gtk_widget_show(abox);
2235 /* If leaf is NULL then the file is not renamed.
2236 * quiet can be -1 for default.
2238 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
2240 GUIside *gui_side;
2241 GtkWidget *abox;
2243 if (quiet == -1)
2244 quiet = o_action_move.int_value;
2246 action_dest = dest;
2247 action_leaf = leaf;
2248 action_do_func = do_move;
2250 abox = abox_new(_("Move"), quiet);
2251 gui_side = start_action(abox, list_cb, paths,
2252 o_action_force.int_value,
2253 o_action_brief.int_value,
2254 o_action_recurse.int_value,
2255 o_action_newer.int_value);
2256 if (!gui_side)
2257 return;
2259 abox_add_flag(ABOX(abox),
2260 _("Newer"),
2261 _("Only over-write if source is newer than destination."),
2262 'W', o_action_newer.int_value);
2263 abox_add_flag(ABOX(abox),
2264 _("Brief"), _("Don't log each file as it is moved"),
2265 'B', o_action_brief.int_value);
2266 number_of_windows++;
2267 gtk_widget_show(abox);
2270 /* If leaf is NULL then the link will have the same name */
2271 void action_link(GList *paths, const char *dest, const char *leaf,
2272 gboolean relative)
2274 GtkWidget *abox;
2275 GUIside *gui_side;
2277 action_dest = dest;
2278 action_leaf = leaf;
2279 if (relative)
2280 action_do_func = do_link_relative;
2281 else
2282 action_do_func = do_link_absolute;
2284 abox = abox_new(_("Link"), o_action_link.int_value);
2285 gui_side = start_action(abox, list_cb, paths,
2286 o_action_force.int_value,
2287 o_action_brief.int_value,
2288 o_action_recurse.int_value,
2289 o_action_newer.int_value);
2290 if (!gui_side)
2291 return;
2293 number_of_windows++;
2294 gtk_widget_show(abox);
2297 /* Eject these paths */
2298 void action_eject(GList *paths)
2300 GUIside *gui_side;
2301 GtkWidget *abox;
2303 abox = abox_new(_("Eject"), TRUE);
2304 gui_side = start_action(abox, eject_cb, paths,
2305 o_action_force.int_value,
2306 o_action_brief.int_value,
2307 o_action_recurse.int_value,
2308 o_action_newer.int_value);
2309 if (!gui_side)
2310 return;
2312 number_of_windows++;
2313 gtk_widget_show(abox);
2316 void action_init(void)
2318 option_add_int(&o_action_copy, "action_copy", 1);
2319 option_add_int(&o_action_move, "action_move", 1);
2320 option_add_int(&o_action_link, "action_link", 1);
2321 option_add_int(&o_action_delete, "action_delete", 0);
2322 option_add_int(&o_action_mount, "action_mount", 1);
2323 option_add_int(&o_action_force, "action_force", FALSE);
2324 option_add_int(&o_action_brief, "action_brief", FALSE);
2325 option_add_int(&o_action_recurse, "action_recurse", FALSE);
2326 option_add_int(&o_action_newer, "action_newer", FALSE);
2329 #define MAX_ASK 4
2331 /* Check to see if any of the selected items (or their children) are
2332 * on the pinboard or panel. If so, ask for confirmation.
2334 * TRUE if it's OK to lose them.
2336 static gboolean remove_pinned_ok(GList *paths)
2338 GList *ask = NULL, *next;
2339 GString *message;
2340 int i, ask_n = 0;
2341 gboolean retval;
2343 for (; paths; paths = paths->next)
2345 guchar *path = (guchar *) paths->data;
2347 if (icons_require(path))
2349 if (++ask_n > MAX_ASK)
2350 break;
2351 ask = g_list_append(ask, path);
2355 if (!ask)
2356 return TRUE;
2358 if (ask_n > MAX_ASK)
2360 message = g_string_new(_("Deleting items such as "));
2361 ask_n--;
2363 else if (ask_n == 1)
2364 message = g_string_new(_("Deleting the item "));
2365 else
2366 message = g_string_new(_("Deleting the items "));
2368 i = 0;
2369 for (next = ask; next; next = next->next)
2371 guchar *path = (guchar *) next->data;
2372 guchar *leaf;
2374 leaf = strrchr(path, '/');
2375 if (leaf)
2376 leaf++;
2377 else
2378 leaf = path;
2380 g_string_append_c(message, '`');
2381 g_string_append(message, leaf);
2382 g_string_append_c(message, '\'');
2383 i++;
2384 if (i == ask_n - 1 && i > 0)
2385 g_string_append(message, _(" and "));
2386 else if (i < ask_n)
2387 g_string_append(message, ", ");
2390 g_list_free(ask);
2392 if (ask_n == 1)
2393 message = g_string_append(message,
2394 _(" will affect some items on the pinboard "
2395 "or panel - really delete it?"));
2396 else
2398 if (ask_n > MAX_ASK)
2399 message = g_string_append_c(message, ',');
2400 message = g_string_append(message,
2401 _(" will affect some items on the pinboard "
2402 "or panel - really delete them?"));
2405 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
2407 g_string_free(message, TRUE);
2409 return retval;
2412 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2414 FindCondition *cond;
2416 cond = find_compile(string);
2417 entry_set_error(widget, !cond);
2419 find_condition_free(cond);