r1353: Various speed improvements. Intelligent sort copes better with adjacent
[rox-filer.git] / ROX-Filer / src / action.c
blob41c873d81f5b729f5aca349542b7b4c5e6f8bcc7
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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>
37 #include "global.h"
39 #include "action.h"
40 #include "abox.h"
41 #include "string.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "filer.h"
45 #include "display.h"
46 #include "main.h"
47 #include "options.h"
48 #include "modechange.h"
49 #include "find.h"
50 #include "dir.h"
51 #include "icon.h"
52 #include "mount.h"
54 /* Parent->Child messages are one character each:
56 * Y/N Yes/No button clicked
57 * F Force deletion of non-writeable items
58 * Q Quiet toggled
59 * E Entry text changed
60 * W neWer toggled
63 typedef struct _GUIside GUIside;
64 typedef void ActionChild(gpointer data);
65 typedef void ForDirCB(const char *path, const char *dest_path);
67 struct _GUIside
69 ABox *abox; /* The action window widget */
71 int from_child; /* File descriptor */
72 FILE *to_child;
73 int input_tag; /* gdk_input_add() */
74 int child; /* Process ID */
75 int errors; /* Number of errors so far */
76 gboolean show_info; /* For Disk Usage */
78 guchar **default_string; /* Changed when the entry changes */
79 void (*entry_string_func)(GtkWidget *widget,
80 const guchar *string);
83 /* These don't need to be in a structure because we fork() before
84 * using them again.
86 static gboolean mount_open_dir = FALSE;
87 static int from_parent = 0;
88 static FILE *to_parent = NULL;
89 static gboolean quiet = FALSE;
90 static GString *message = NULL;
91 static const char *action_dest = NULL;
92 static const char *action_leaf = NULL;
93 static void (*action_do_func)(const char *source, const char *dest);
94 static double size_tally; /* For Disk Usage */
95 static unsigned long dir_counter; /* For Disk Usage */
96 static unsigned long file_counter; /* For Disk Usage */
98 static struct mode_change *mode_change = NULL; /* For Permissions */
99 static FindCondition *find_condition = NULL; /* For Find */
101 /* Only used by child */
102 static gboolean o_force = FALSE;
103 static gboolean o_brief = FALSE;
104 static gboolean o_recurse = FALSE;
105 static gboolean o_newer = FALSE;
107 static Option o_action_copy, o_action_move, o_action_link;
108 static Option o_action_delete, o_action_mount;
109 static Option o_action_force, o_action_brief, o_action_recurse;
110 static Option o_action_newer;
112 /* Whenever the text in these boxes is changed we store a copy of the new
113 * string to be used as the default next time.
115 static guchar *last_chmod_string = NULL;
116 static guchar *last_find_string = NULL;
118 /* Set to one of the above before forking. This may change over a call to
119 * reply(). It is reset to NULL once the text is parsed.
121 static guchar *new_entry_string = NULL;
123 /* Static prototypes */
124 static gboolean send();
125 static gboolean send_error();
126 static gboolean send_dir(const char *dir);
127 static gboolean read_exact(int source, char *buffer, ssize_t len);
128 static void do_mount(guchar *path, gboolean mount);
129 static gboolean reply(int fd, gboolean ignore_quiet);
130 static gboolean remove_pinned_ok(GList *paths);
132 /* SUPPORT */
135 /* This is called whenever the user edits the entry box (if any) - send the
136 * new string.
138 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
140 guchar *text;
142 g_return_if_fail(gui_side->default_string != NULL);
144 text = gtk_editable_get_chars(entry, 0, -1);
146 if (gui_side->entry_string_func)
147 gui_side->entry_string_func(GTK_WIDGET(entry), text);
149 g_free(*(gui_side->default_string));
150 *(gui_side->default_string) = text; /* Gets text's ref */
152 if (!gui_side->to_child)
153 return;
155 fputc('E', gui_side->to_child);
156 fputs(text, gui_side->to_child);
157 fputc('\n', gui_side->to_child);
158 fflush(gui_side->to_child);
161 void show_condition_help(gpointer data)
163 static GtkWidget *help = NULL;
165 if (!help)
167 GtkWidget *text, *vbox, *button, *hbox, *frame;
169 help = gtk_window_new(GTK_WINDOW_TOPLEVEL);
170 gtk_window_set_type_hint(GTK_WINDOW(help),
171 GDK_WINDOW_TYPE_HINT_DIALOG);
172 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
173 gtk_window_set_title(GTK_WINDOW(help),
174 _("Find expression reference"));
176 vbox = gtk_vbox_new(FALSE, 0);
177 gtk_container_add(GTK_CONTAINER(help), vbox);
179 frame = gtk_frame_new(_("Quick Start"));
180 text = gtk_label_new(
181 _("Just put the name of the file you're looking for in single quotes:\n"
182 "'index.html' (to find a file called 'index.html')"));
183 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
184 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
185 gtk_container_add(GTK_CONTAINER(frame), text);
186 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
188 frame = gtk_frame_new(_("Examples"));
189 text = gtk_label_new(
190 _("'*.htm', '*.html' (finds HTML files)\n"
191 "IsDir 'lib' (finds directories called 'lib')\n"
192 "IsReg 'core' (finds a regular file called 'core')\n"
193 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
194 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
195 "'CVS' prune, isreg (a regular file not in CVS)\n"
196 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
197 gtk_widget_set_name(text, "fixed-style");
198 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
199 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
200 gtk_container_add(GTK_CONTAINER(frame), text);
201 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
203 frame = gtk_frame_new(_("Simple Tests"));
204 text = gtk_label_new(
205 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
206 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
207 "\n"
208 "IsEmpty, IsMine\n"
209 "\n"
210 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
211 "contains a slash then the match is against the full path; otherwise it is \n"
212 "against the leafname only."));
213 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
214 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
215 gtk_container_add(GTK_CONTAINER(frame), text);
216 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
218 frame = gtk_frame_new(_("Comparisons"));
219 text = gtk_label_new(
220 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
221 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
222 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
223 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
224 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
225 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
226 gtk_container_add(GTK_CONTAINER(frame), text);
227 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
229 frame = gtk_frame_new(_("Specials"));
230 text = gtk_label_new(
231 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
232 "in 'command' is replaced with the path of the current file)\n"
233 "prune (false, and prevents searching the contents of a directory).")
235 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
236 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
237 gtk_container_add(GTK_CONTAINER(frame), text);
238 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
240 hbox = gtk_hbox_new(FALSE, 20);
241 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
243 text = gtk_label_new(
244 _("See the ROX-Filer manual for full details."));
245 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
246 button = gtk_button_new_with_label(_("Close"));
247 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
248 g_signal_connect_swapped(button, "clicked",
249 G_CALLBACK(gtk_widget_hide), help);
251 g_signal_connect_swapped(help, "delete_event",
252 G_CALLBACK(gtk_widget_hide), help);
255 if (GTK_WIDGET_VISIBLE(help))
256 gtk_widget_hide(help);
257 gtk_widget_show_all(help);
260 static void show_chmod_help(gpointer data)
262 static GtkWidget *help = NULL;
264 if (!help)
266 GtkWidget *text, *vbox, *button, *hbox, *sep;
268 help = gtk_window_new(GTK_WINDOW_TOPLEVEL);
269 gtk_window_set_type_hint(GTK_WINDOW(help),
270 GDK_WINDOW_TYPE_HINT_DIALOG);
271 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
272 gtk_window_set_title(GTK_WINDOW(help),
273 _("Permissions command reference"));
275 vbox = gtk_vbox_new(FALSE, 0);
276 gtk_container_add(GTK_CONTAINER(help), vbox);
278 text = gtk_label_new(
279 _("Normally, you can just select a command from the menu (click \n"
280 "on the arrow beside the command box). Sometimes, you need more...\n"
281 "\n"
282 "The format of a command is:\n"
283 "CHANGE, CHANGE, ...\n"
284 "Each CHANGE is:\n"
285 "WHO HOW PERMISSIONS\n"
286 "WHO is some combination of u, g and o which determines whether to\n"
287 "change the permissions for the User (owner), Group or Others.\n"
288 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
289 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
291 "Bracketed text and spaces are ignored.\n\n"
293 "Examples:\n"
294 "u+rw (the file owner gains read and write permission)\n"
295 "g=u (the group permissions are set to be the same as the user's)\n"
296 "o=u-w (others get the same permissions as the owner, but without "
297 "write permission)\n"
298 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
299 "a+X (directories become accessable by everyone; files which were\n"
300 "executable by anyone become executable by everyone)\n"
301 "u+rw, go+r (two commands at once!)\n"
302 "u+s (set the SetUID bit - often has no effect on script files)\n"
303 "755 (set the permissions directly)\n"
305 "\nSee the chmod(1) man page for full details."));
306 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
307 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
309 hbox = gtk_hbox_new(FALSE, 20);
310 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
312 sep = gtk_hseparator_new();
313 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
314 button = gtk_button_new_with_label(_("Close"));
315 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
316 g_signal_connect_swapped(button, "clicked",
317 G_CALLBACK(gtk_widget_hide), help);
319 g_signal_connect_swapped(help, "delete_event",
320 G_CALLBACK(gtk_widget_hide), help);
323 if (GTK_WIDGET_VISIBLE(help))
324 gtk_widget_hide(help);
325 gtk_widget_show_all(help);
328 static void process_message(GUIside *gui_side, const gchar *buffer)
330 ABox *abox = gui_side->abox;
332 if (*buffer == '?')
333 abox_ask(abox, buffer + 1);
334 else if (*buffer == 's')
335 dir_check_this(buffer + 1); /* Update this item */
336 else if (*buffer == '=')
337 abox_add_filename(abox, buffer + 1);
338 else if (*buffer == '#')
339 abox_clear_results(abox);
340 else if (*buffer == 'm' || *buffer == 'M')
342 /* Mount / major changes to this path */
343 if (*buffer == 'M')
344 mount_update(TRUE);
345 filer_check_mounted(buffer + 1);
347 else if (*buffer == '/')
348 abox_set_current_object(abox, buffer + 1);
349 else if (*buffer == 'o')
350 filer_opendir(buffer + 1, NULL);
351 else if (*buffer == '!')
353 gui_side->errors++;
354 abox_log(abox, buffer + 1, "error");
356 else
357 abox_log(abox, buffer + 1, NULL);
360 /* Called when the child sends us a message */
361 static void message_from_child(gpointer data,
362 gint source,
363 GdkInputCondition condition)
365 char buf[5];
366 GUIside *gui_side = (GUIside *) data;
367 ABox *abox = gui_side->abox;
368 GtkTextBuffer *text_buffer;
370 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
372 if (read_exact(source, buf, 4))
374 ssize_t message_len;
375 char *buffer;
377 buf[4] = '\0';
378 message_len = strtol(buf, NULL, 16);
379 buffer = g_malloc(message_len + 1);
380 if (message_len > 0 && read_exact(source, buffer, message_len))
382 buffer[message_len] = '\0';
383 process_message(gui_side, buffer);
384 g_free(buffer);
385 return;
387 g_printerr("Child died in the middle of a message.\n");
390 /* The child is dead */
391 gui_side->child = 0;
393 fclose(gui_side->to_child);
394 gui_side->to_child = NULL;
395 close(gui_side->from_child);
396 g_source_remove(gui_side->input_tag);
397 /* XXX: gtk_widget_set_sensitive(gui_side->quiet, FALSE); */
399 if (gui_side->errors)
401 guchar *report;
403 if (gui_side->errors == 1)
404 report = g_strdup(_("There was one error.\n"));
405 else
406 report = g_strdup_printf(_("There were %d errors.\n"),
407 gui_side->errors);
409 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
411 g_free(report);
413 else if (gui_side->show_info == FALSE)
414 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
417 /* Scans src_dir, calling cb(item, dest_path) for each item */
418 static void for_dir_contents(ForDirCB *cb,
419 const char *src_dir,
420 const char *dest_path)
422 DIR *d;
423 struct dirent *ent;
424 GList *list = NULL, *next;
426 d = mc_opendir(src_dir);
427 if (!d)
429 /* Message displayed is "ERROR reading 'path': message" */
430 g_string_sprintf(message, "!%s '%s': %s\n",
431 _("ERROR reading"),
432 src_dir, g_strerror(errno));
433 send();
434 return;
437 send_dir(src_dir);
439 while ((ent = mc_readdir(d)))
441 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
442 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
443 continue;
444 list = g_list_append(list, g_strdup(make_path(src_dir,
445 ent->d_name)->str));
447 mc_closedir(d);
449 if (!list)
450 return;
452 next = list;
454 while (next)
456 cb((char *) next->data, dest_path);
458 g_free(next->data);
459 next = next->next;
461 g_list_free(list);
464 /* Read this many bytes into the buffer. TRUE on success. */
465 static gboolean read_exact(int source, char *buffer, ssize_t len)
467 while (len > 0)
469 ssize_t got;
470 got = read(source, buffer, len);
471 if (got < 1)
472 return FALSE;
473 len -= got;
474 buffer += got;
476 return TRUE;
479 /* Send 'message' to our parent process. TRUE on success. */
480 static gboolean send(void)
482 char len_buffer[5];
483 ssize_t len;
485 g_return_val_if_fail(message->len < 0xffff, FALSE);
487 sprintf(len_buffer, "%04x", message->len);
488 fwrite(len_buffer, 1, 4, to_parent);
489 len = fwrite(message->str, 1, message->len, to_parent);
490 fflush(to_parent);
491 return len == message->len;
494 /* Set the directory indicator at the top of the window */
495 static gboolean send_dir(const char *dir)
497 g_string_sprintf(message, "/%s", dir);
498 return send();
501 static gboolean send_error(void)
503 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
504 return send();
507 /* Send 'Quiet' if possible, 'Yes' otherwise */
508 #if 0
509 static void find_return_pressed(GtkWidget *button, GUIside *gui_side)
511 if (GTK_WIDGET_SENSITIVE(gui_side->quiet))
512 gtk_button_clicked(GTK_BUTTON(gui_side->quiet));
513 else if (GTK_WIDGET_SENSITIVE(gui_side->yes))
514 gtk_button_clicked(GTK_BUTTON(gui_side->yes));
515 else
516 gdk_beep();
518 #endif
520 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
522 gchar code;
524 if (!gui_side->to_child)
525 return;
527 if (response == GTK_RESPONSE_YES)
528 code = 'Y';
529 else if (response == GTK_RESPONSE_NO)
530 code = 'N';
531 else
532 return;
534 fputc(code, gui_side->to_child);
535 fflush(gui_side->to_child);
538 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
540 if (!gui_side->to_child)
541 return;
543 fputc(flag, gui_side->to_child);
544 fflush(gui_side->to_child);
547 static void read_new_entry_text(void)
549 int len;
550 char c;
551 GString *new;
553 new = g_string_new(NULL);
555 for (;;)
557 len = read(from_parent, &c, 1);
558 if (len != 1)
560 fprintf(stderr, "read() error: %s\n",
561 g_strerror(errno));
562 _exit(1); /* Parent died? */
565 if (c == '\n')
566 break;
567 g_string_append_c(new, c);
570 g_free(new_entry_string);
571 new_entry_string = new->str;
572 g_string_free(new, FALSE);
575 static void process_flag(char flag)
577 switch (flag)
579 case 'Q':
580 quiet = !quiet;
581 break;
582 case 'F':
583 o_force = !o_force;
584 break;
585 case 'R':
586 o_recurse = !o_recurse;
587 break;
588 case 'B':
589 o_brief = !o_brief;
590 break;
591 case 'W':
592 o_newer = !o_newer;
593 break;
594 case 'E':
595 read_new_entry_text();
596 break;
597 default:
598 g_string_sprintf(message,
599 "!ERROR: Bad message '%c'\n", flag);
600 send();
601 break;
605 /* If the parent has sent any flag toggles, read them */
606 static void check_flags(void)
608 fd_set set;
609 int got;
610 char retval;
611 struct timeval tv;
613 FD_ZERO(&set);
615 while (1)
617 FD_SET(from_parent, &set);
618 tv.tv_sec = 0;
619 tv.tv_usec = 0;
620 got = select(from_parent + 1, &set, NULL, NULL, &tv);
622 if (got == -1)
623 g_error("select() failed: %s\n", g_strerror(errno));
624 else if (!got)
625 return;
627 got = read(from_parent, &retval, 1);
628 if (got != 1)
629 g_error("read() error: %s\n", g_strerror(errno));
631 process_flag(retval);
635 /* Read until the user sends a reply. If ignore_quiet is TRUE then
636 * the user MUST click Yes or No, else treat quiet on as Yes.
637 * If the user needs prompting then does send().
639 static gboolean reply(int fd, gboolean ignore_quiet)
641 ssize_t len;
642 char retval;
644 if (quiet && !ignore_quiet)
645 return TRUE;
647 send();
649 while (1)
651 len = read(fd, &retval, 1);
652 if (len != 1)
654 fprintf(stderr, "read() error: %s\n",
655 g_strerror(errno));
656 _exit(1); /* Parent died? */
659 switch (retval)
661 case 'Y':
662 g_string_sprintf(message, "' %s\n", _("Yes"));
663 send();
664 return TRUE;
665 case 'N':
666 g_string_sprintf(message, "' %s\n", _("No"));
667 send();
668 return FALSE;
669 default:
670 process_flag(retval);
671 break;
676 static void destroy_action_window(GtkWidget *widget, gpointer data)
678 GUIside *gui_side = (GUIside *) data;
680 if (gui_side->child)
682 kill(gui_side->child, SIGTERM);
683 fclose(gui_side->to_child);
684 close(gui_side->from_child);
685 g_source_remove(gui_side->input_tag);
688 g_free(gui_side);
690 if (--number_of_windows < 1)
691 gtk_main_quit();
694 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
695 * (NULL on failure). The child calls func().
697 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
698 int force, int brief, int recurse, int newer)
700 gboolean autoq;
701 int filedes[4]; /* 0 and 2 are for reading */
702 GUIside *gui_side;
703 int child;
704 struct sigaction act;
706 if (pipe(filedes))
708 report_error("pipe: %s", g_strerror(errno));
709 gtk_widget_destroy(abox);
710 return NULL;
713 if (pipe(filedes + 2))
715 close(filedes[0]);
716 close(filedes[1]);
717 report_error("pipe: %s", g_strerror(errno));
718 gtk_widget_destroy(abox);
719 return NULL;
722 autoq = gtk_toggle_button_get_active(
723 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
725 o_force = force;
726 o_brief = brief;
727 o_recurse = recurse;
728 o_newer = newer;
730 child = fork();
731 switch (child)
733 case -1:
734 report_error("fork: %s", g_strerror(errno));
735 gtk_widget_destroy(abox);
736 return NULL;
737 case 0:
738 /* We are the child */
740 quiet = autoq;
742 /* Reset the SIGCHLD handler */
743 act.sa_handler = SIG_DFL;
744 sigemptyset(&act.sa_mask);
745 act.sa_flags = 0;
746 sigaction(SIGCHLD, &act, NULL);
748 message = g_string_new(NULL);
749 close(filedes[0]);
750 close(filedes[3]);
751 to_parent = fdopen(filedes[1], "wb");
752 from_parent = filedes[2];
753 func(data);
754 send_dir("");
755 _exit(0);
758 /* We are the parent */
759 close(filedes[1]);
760 close(filedes[2]);
761 gui_side = g_malloc(sizeof(GUIside));
762 gui_side->from_child = filedes[0];
763 gui_side->to_child = fdopen(filedes[3], "wb");
764 gui_side->child = child;
765 gui_side->errors = 0;
766 gui_side->show_info = FALSE;
767 gui_side->default_string = NULL;
768 gui_side->entry_string_func = NULL;
770 gui_side->abox = ABOX(abox);
771 g_signal_connect(abox, "destroy",
772 G_CALLBACK(destroy_action_window), gui_side);
774 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
775 g_signal_connect(abox, "flag_toggled",
776 G_CALLBACK(flag_toggled), gui_side);
778 gui_side->input_tag = gtk_input_add_full(gui_side->from_child,
779 GDK_INPUT_READ,
780 message_from_child,
781 NULL, gui_side, NULL);
783 return gui_side;
786 /* ACTIONS ON ONE ITEM */
788 /* These may call themselves recursively, or ask questions, etc */
790 /* Updates the global size_tally, file_counter and dir_counter */
791 static void do_usage(const char *src_path, const char *unused)
793 struct stat info;
795 check_flags();
797 if (mc_lstat(src_path, &info))
799 g_string_sprintf(message, "'%s:\n", src_path);
800 send();
801 send_error();
803 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
805 file_counter++;
806 size_tally += info.st_size;
808 else if (S_ISDIR(info.st_mode))
810 dir_counter++;
811 g_string_sprintf(message, _("?Count contents of %s?"),
812 src_path);
813 if (reply(from_parent, FALSE))
815 char *safe_path;
816 safe_path = g_strdup(src_path);
817 for_dir_contents(do_usage, safe_path, safe_path);
818 g_free(safe_path);
821 else
822 file_counter++;
825 /* dest_path is the dir containing src_path */
826 static void do_delete(const char *src_path, const char *unused)
828 struct stat info;
829 gboolean write_prot;
830 char *safe_path;
832 check_flags();
834 if (mc_lstat(src_path, &info))
836 send_error();
837 return;
840 write_prot = S_ISLNK(info.st_mode) ? FALSE
841 : access(src_path, W_OK) != 0;
842 if (write_prot || !quiet)
844 g_string_sprintf(message, _("?Delete %s'%s'?"),
845 write_prot ? _("WRITE-PROTECTED ") : "",
846 src_path);
847 if (!reply(from_parent, write_prot && !o_force))
848 return;
850 else if (!o_brief)
852 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
853 send();
856 safe_path = g_strdup(src_path);
858 if (S_ISDIR(info.st_mode))
860 for_dir_contents(do_delete, safe_path, safe_path);
861 if (rmdir(safe_path))
863 g_free(safe_path);
864 send_error();
865 return;
867 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
868 safe_path);
869 send();
870 g_string_sprintf(message, "m%s", safe_path);
871 send();
873 else if (unlink(src_path))
874 send_error();
875 else
877 g_string_sprintf(message, "s%s", safe_path);
878 send();
881 g_free(safe_path);
884 /* path is the item to check. If is is a directory then we may recurse
885 * (unless prune is used).
887 static void do_find(const char *path, const char *unused)
889 FindInfo info;
890 char *slash;
892 check_flags();
894 if (!quiet)
896 g_string_sprintf(message, _("?Check '%s'?"), path);
897 if (!reply(from_parent, FALSE))
898 return;
901 for (;;)
903 if (new_entry_string)
905 if (find_condition)
906 find_condition_free(find_condition);
907 find_condition = find_compile(new_entry_string);
908 g_free(new_entry_string);
909 new_entry_string = NULL;
912 if (find_condition)
913 break;
915 g_string_assign(message, _("!Invalid find condition - "
916 "change it and try again\n"));
917 send();
918 g_string_sprintf(message, _("?Check '%s'?"), path);
919 if (!reply(from_parent, TRUE))
920 return;
923 if (mc_lstat(path, &info.stats))
925 send_error();
926 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
927 send();
928 return;
931 info.fullpath = path;
932 time(&info.now); /* XXX: Not for each check! */
934 slash = strrchr(path, '/');
935 info.leaf = slash ? slash + 1 : path;
936 info.prune = FALSE;
937 if (find_test_condition(find_condition, &info))
939 g_string_sprintf(message, "=%s", path);
940 send();
943 if (S_ISDIR(info.stats.st_mode) && !info.prune)
945 char *safe_path;
946 safe_path = g_strdup(path);
947 for_dir_contents(do_find, safe_path, safe_path);
948 g_free(safe_path);
952 /* Like mode_compile(), but ignores spaces and bracketed bits */
953 struct mode_change *nice_mode_compile(const char *mode_string,
954 unsigned int masked_ops)
956 GString *new;
957 int brackets = 0;
958 struct mode_change *retval = NULL;
960 new = g_string_new(NULL);
962 for (; *mode_string; mode_string++)
964 if (*mode_string == '(')
965 brackets++;
966 if (*mode_string == ')')
968 brackets--;
969 if (brackets < 0)
970 break;
971 continue;
974 if (brackets == 0 && *mode_string != ' ')
975 g_string_append_c(new, *mode_string);
978 if (brackets == 0)
979 retval = mode_compile(new->str, masked_ops);
980 g_string_free(new, TRUE);
981 return retval;
984 static void do_chmod(const char *path, const char *unused)
986 struct stat info;
987 mode_t new_mode;
989 check_flags();
991 if (mc_lstat(path, &info))
993 send_error();
994 return;
996 if (S_ISLNK(info.st_mode))
997 return;
999 if (!quiet)
1001 g_string_sprintf(message,
1002 _("?Change permissions of '%s'?"), path);
1003 if (!reply(from_parent, FALSE))
1004 return;
1006 else if (!o_brief)
1008 g_string_sprintf(message,
1009 _("'Changing permissions of '%s'\n"),
1010 path);
1011 send();
1014 for (;;)
1016 if (new_entry_string)
1018 if (mode_change)
1019 mode_free(mode_change);
1020 mode_change = nice_mode_compile(new_entry_string,
1021 MODE_MASK_ALL);
1022 g_free(new_entry_string);
1023 new_entry_string = NULL;
1026 if (mode_change)
1027 break;
1029 g_string_assign(message,
1030 _("!Invalid mode command - change it and try again\n"));
1031 send();
1032 g_string_sprintf(message,
1033 _("?Change permissions of '%s'?"), path);
1034 if (!reply(from_parent, TRUE))
1035 return;
1038 if (mc_lstat(path, &info))
1040 send_error();
1041 return;
1043 if (S_ISLNK(info.st_mode))
1044 return;
1046 new_mode = mode_adjust(info.st_mode, mode_change);
1047 if (chmod(path, new_mode))
1049 send_error();
1050 return;
1053 g_string_sprintf(message, "s%s", path);
1054 send();
1056 if (S_ISDIR(info.st_mode))
1058 g_string_sprintf(message, "m%s", path);
1059 send();
1061 if (o_recurse)
1063 guchar *safe_path;
1064 safe_path = g_strdup(path);
1065 for_dir_contents(do_chmod, safe_path, safe_path);
1066 g_free(safe_path);
1071 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1072 * is set then that is the new leafname, otherwise the leafname stays
1073 * the same.
1075 static char *make_dest_path(const char *object, const char *dir)
1077 const char *leaf;
1079 if (action_leaf)
1080 leaf = action_leaf;
1081 else
1083 leaf = strrchr(object, '/');
1084 if (!leaf)
1085 leaf = object; /* Error? */
1086 else
1087 leaf++;
1090 return make_path(dir, leaf)->str;
1093 /* If action_leaf is not NULL it specifies the new leaf name */
1094 static void do_copy2(const char *path, const char *dest)
1096 char *dest_path;
1097 struct stat info;
1098 struct stat dest_info;
1100 check_flags();
1102 dest_path = make_dest_path(path, dest);
1104 if (mc_lstat(path, &info))
1106 send_error();
1107 return;
1110 if (mc_lstat(dest_path, &dest_info) == 0)
1112 int err;
1113 gboolean merge;
1115 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1117 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1119 /* Newer; keep going */
1121 else
1123 g_string_sprintf(message,
1124 _("?'%s' already exists - %s?"), dest_path,
1125 merge ? _("merge contents") : _("overwrite"));
1127 if (!reply(from_parent, TRUE))
1128 return;
1131 if (!merge)
1133 if (S_ISDIR(dest_info.st_mode))
1134 err = rmdir(dest_path);
1135 else
1136 err = unlink(dest_path);
1138 if (err)
1140 send_error();
1141 if (errno != ENOENT)
1142 return;
1143 g_string_sprintf(message,
1144 _("'Trying copy anyway...\n"));
1145 send();
1149 else if (!quiet)
1151 g_string_sprintf(message,
1152 _("?Copy %s as %s?"), path, dest_path);
1153 if (!reply(from_parent, FALSE))
1154 return;
1156 else
1158 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1159 dest_path);
1160 send();
1163 if (S_ISDIR(info.st_mode))
1165 mode_t mode = info.st_mode;
1166 char *safe_path, *safe_dest;
1167 struct stat dest_info;
1168 gboolean exists;
1170 safe_path = g_strdup(path);
1171 safe_dest = g_strdup(dest_path);
1173 exists = !mc_lstat(dest_path, &dest_info);
1175 if (exists && !S_ISDIR(dest_info.st_mode))
1177 g_string_sprintf(message,
1178 _("!ERROR: Destination already exists, "
1179 "but is not a directory\n"));
1180 send();
1182 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1183 send_error();
1184 else
1186 if (!exists)
1188 /* (just been created then) */
1189 g_string_sprintf(message, "s%s", dest_path);
1190 send();
1193 action_leaf = NULL;
1194 for_dir_contents(do_copy2, safe_path, safe_dest);
1195 /* Note: dest_path now invalid... */
1197 if (!exists)
1199 struct utimbuf utb;
1201 /* We may have created the directory with
1202 * more permissions than the source so that
1203 * we could write to it... change it back now.
1205 if (chmod(safe_dest, mode))
1206 send_error();
1208 /* Also, try to preserve the timestamps */
1209 utb.actime = info.st_atime;
1210 utb.modtime = info.st_mtime;
1212 utime(safe_dest, &utb);
1216 g_free(safe_path);
1217 g_free(safe_dest);
1219 else if (S_ISLNK(info.st_mode))
1221 char *target;
1223 /* Not all versions of cp(1) can make symlinks,
1224 * so we special-case it.
1227 target = readlink_dup(path);
1228 if (target)
1230 if (symlink(target, dest_path))
1231 send_error();
1232 else
1234 g_string_sprintf(message, "s%s", dest_path);
1235 send();
1238 g_free(target);
1240 else
1241 send_error();
1243 else
1245 guchar *error;
1247 error = copy_file(path, dest_path);
1249 if (error)
1251 g_string_sprintf(message, _("!%s\nFailed to copy '%s'"),
1252 error, path);
1253 g_free(error);
1254 send();
1256 else
1258 g_string_sprintf(message, "s%s", dest_path);
1259 send();
1264 /* If action_leaf is not NULL it specifies the new leaf name */
1265 static void do_move2(const char *path, const char *dest)
1267 char *dest_path;
1268 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1269 struct stat info2;
1270 gboolean is_dir;
1271 char *err;
1273 check_flags();
1275 dest_path = make_dest_path(path, dest);
1277 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1279 if (access(dest_path, F_OK) == 0)
1281 struct stat info;
1282 int err;
1284 if (mc_lstat(dest_path, &info))
1286 send_error();
1287 return;
1290 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1292 /* Newer; keep going */
1294 else
1296 g_string_sprintf(message,
1297 _("?'%s' already exists - overwrite?"),
1298 dest_path);
1299 if (!reply(from_parent, TRUE))
1300 return;
1303 if (S_ISDIR(info.st_mode))
1304 err = rmdir(dest_path);
1305 else
1306 err = unlink(dest_path);
1308 if (err)
1310 send_error();
1311 if (errno != ENOENT)
1312 return;
1313 g_string_sprintf(message,
1314 _("'Trying move anyway...\n"));
1315 send();
1318 else if (!quiet)
1320 g_string_sprintf(message,
1321 _("?Move %s as %s?"), path, dest_path);
1322 if (!reply(from_parent, FALSE))
1323 return;
1325 else
1327 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1328 dest_path);
1329 send();
1332 argv[2] = path;
1333 argv[3] = dest_path;
1335 err = fork_exec_wait(argv);
1336 if (err)
1338 g_string_sprintf(message,
1339 _("!%s\nFailed to move %s as %s\n"),
1340 err, path, dest_path);
1341 send();
1343 g_free(err);
1345 else
1347 g_string_sprintf(message, "s%s", dest_path);
1348 send();
1350 if (is_dir)
1351 g_string_sprintf(message, "m%s", path);
1352 else
1353 g_string_sprintf(message, "s%s", path);
1355 send();
1359 /* Copy path to dest.
1360 * Check that path not copied into itself.
1362 static void do_copy(const char *path, const char *dest)
1364 if (is_sub_dir(make_dest_path(path, dest), path))
1366 g_string_sprintf(message,
1367 _("!ERROR: Can't copy object into itself\n"));
1368 send();
1370 else
1371 do_copy2(path, dest);
1374 /* Move path to dest.
1375 * Check that path not moved into itself.
1377 static void do_move(const char *path, const char *dest)
1379 if (is_sub_dir(make_dest_path(path, dest), path))
1381 g_string_sprintf(message,
1382 _("!ERROR: Can't move/rename object into itself\n"));
1383 send();
1385 else
1386 do_move2(path, dest);
1389 static void do_link(const char *path, const char *dest)
1391 char *dest_path;
1393 check_flags();
1395 dest_path = make_dest_path(path, dest);
1397 if (quiet)
1399 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1400 dest_path);
1401 send();
1403 else
1405 g_string_sprintf(message,
1406 _("?Link %s as %s?"), path, dest_path);
1407 if (!reply(from_parent, FALSE))
1408 return;
1411 if (symlink(path, dest_path))
1412 send_error();
1413 else
1415 g_string_sprintf(message, "s%s", dest_path);
1416 send();
1420 /* Mount/umount this item (depending on 'mount') */
1421 static void do_mount(guchar *path, gboolean mount)
1423 const char *argv[3] = {NULL, NULL, NULL};
1424 char *err;
1426 check_flags();
1428 argv[0] = mount ? "mount" : "umount";
1429 argv[1] = path;
1431 if (quiet)
1433 g_string_sprintf(message,
1434 mount ? _("'Mounting %s\n")
1435 : _("'Unmounting %s\n"),
1436 path);
1437 send();
1439 else
1441 g_string_sprintf(message,
1442 mount ? _("?Mount %s?")
1443 : _("?Unmount %s?"),
1444 path);
1445 if (!reply(from_parent, FALSE))
1446 return;
1449 err = fork_exec_wait(argv);
1450 if (err)
1452 g_string_sprintf(message, mount ?
1453 _("!%s\nMount failed\n") :
1454 _("!%s\nUnmount failed\n"), err);
1455 send();
1456 g_free(err);
1458 else
1460 g_string_sprintf(message, "M%s", path);
1461 send();
1462 if (mount && mount_open_dir)
1464 g_string_sprintf(message, "o%s", path);
1465 send();
1470 /* CHILD MAIN LOOPS */
1472 /* After forking, the child calls one of these functions */
1474 /* We use a double for total size in order to count beyond 4Gb */
1475 static void usage_cb(gpointer data)
1477 GList *paths = (GList *) data;
1478 double total_size = 0;
1479 gchar *tmp;
1481 dir_counter = file_counter = 0;
1483 for (; paths; paths = paths->next)
1485 guchar *path = (guchar *) paths->data;
1487 send_dir(path);
1489 size_tally = 0;
1491 do_usage(path, NULL);
1493 g_string_sprintf(message, "'%s: %s\n",
1494 g_basename(path),
1495 format_double_size(size_tally));
1496 send();
1497 total_size += size_tally;
1500 g_string_sprintf(message, _("'\nTotal: %s ("),
1501 format_double_size(total_size));
1503 if (file_counter)
1505 tmp = g_strdup_printf("%ld %s%s",
1506 file_counter,
1507 file_counter == 1 ? _("file") : _("files"),
1508 dir_counter ? ", " : ")\n");
1509 g_string_append(message, tmp);
1510 g_free(tmp);
1513 if (file_counter == 0 && dir_counter == 0)
1514 g_string_append(message, _("no directories)\n"));
1515 else if (dir_counter)
1517 tmp = g_strdup_printf("%ld %s)\n",
1518 dir_counter,
1519 dir_counter == 1 ? _("directory")
1520 : _("directories"));
1521 g_string_append(message, tmp);
1522 g_free(tmp);
1525 send();
1528 #ifdef DO_MOUNT_POINTS
1529 static void mount_cb(gpointer data)
1531 GList *paths = (GList *) data;
1532 gboolean mount_points = FALSE;
1534 for (; paths; paths = paths->next)
1536 guchar *path = (guchar *) paths->data;
1538 if (mount_is_mounted(path, NULL, NULL))
1539 do_mount(path, FALSE); /* Unmount */
1540 else if (g_hash_table_lookup(fstab_mounts, path))
1541 do_mount(path, TRUE); /* Mount */
1542 else
1543 continue;
1545 mount_points = TRUE;
1548 g_string_sprintf(message,
1549 mount_points ? _("'\nDone\n")
1550 : _("!No mount points selected!\n"));
1551 send();
1553 #endif
1555 /* (use g_dirname() instead?) */
1556 static guchar *dirname(guchar *path)
1558 guchar *slash;
1560 slash = strrchr(path, '/');
1561 g_return_val_if_fail(slash != NULL, g_strdup(path));
1563 if (slash != path)
1564 return g_strndup(path, slash - path);
1565 return g_strdup("/");
1568 static void delete_cb(gpointer data)
1570 GList *paths = (GList *) data;
1572 while (paths)
1574 guchar *path = (guchar *) paths->data;
1575 guchar *dir;
1577 dir = dirname(path);
1578 send_dir(dir);
1580 do_delete(path, dir);
1582 g_free(dir);
1583 paths = paths->next;
1586 g_string_sprintf(message, _("'\nDone\n"));
1587 send();
1590 static void find_cb(gpointer data)
1592 GList *all_paths = (GList *) data;
1593 GList *paths;
1595 while (1)
1597 for (paths = all_paths; paths; paths = paths->next)
1599 guchar *path = (guchar *) paths->data;
1601 send_dir(path);
1603 do_find(path, NULL);
1606 g_string_assign(message, _("?Another search?"));
1607 if (!reply(from_parent, TRUE))
1608 break;
1609 g_string_assign(message, "#");
1610 send();
1613 g_string_sprintf(message, _("'\nDone\n"));
1614 send();
1617 static void chmod_cb(gpointer data)
1619 GList *paths = (GList *) data;
1621 for (; paths; paths = paths->next)
1623 guchar *path = (guchar *) paths->data;
1624 struct stat info;
1626 send_dir(path);
1628 if (mc_stat(path, &info) != 0)
1629 send_error();
1630 else if (S_ISLNK(info.st_mode))
1632 g_string_sprintf(message,
1633 _("!'%s' is a symbolic link\n"),
1634 g_basename(path));
1635 send();
1637 else
1638 do_chmod(path, NULL);
1641 g_string_sprintf(message, _("'\nDone\n"));
1642 send();
1645 static void list_cb(gpointer data)
1647 GList *paths = (GList *) data;
1649 while (paths)
1651 send_dir((char *) paths->data);
1653 action_do_func((char *) paths->data, action_dest);
1655 paths = paths->next;
1658 g_string_sprintf(message, _("'\nDone\n"));
1659 send();
1662 /* EXTERNAL INTERFACE */
1664 void action_find(GList *paths)
1666 GUIside *gui_side;
1667 GtkWidget *abox;
1669 if (!paths)
1671 report_error(_("You need to select some items "
1672 "to search through"));
1673 return;
1676 if (!last_find_string)
1677 last_find_string = g_strdup("'core'");
1679 new_entry_string = last_find_string;
1681 abox = abox_new(_("Find"), FALSE);
1682 gui_side = start_action(abox, find_cb, paths,
1683 o_action_force.int_value,
1684 o_action_brief.int_value,
1685 o_action_recurse.int_value,
1686 o_action_newer.int_value);
1687 if (!gui_side)
1688 return;
1690 abox_add_results(ABOX(abox));
1692 gui_side->default_string = &last_find_string;
1693 abox_add_entry(ABOX(abox), last_find_string,
1694 new_help_button(show_condition_help, NULL));
1695 g_signal_connect(ABOX(abox)->entry, "changed",
1696 G_CALLBACK(entry_changed), gui_side);
1697 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1699 gui_side->show_info = TRUE;
1700 gui_side->entry_string_func = set_find_string_colour;
1702 number_of_windows++;
1703 gtk_widget_show_all(abox);
1706 /* Count disk space used by selected items */
1707 void action_usage(GList *paths)
1709 GUIside *gui_side;
1710 GtkWidget *abox;
1712 if (!paths)
1714 report_error(_("You need to select some items to count"));
1715 return;
1718 abox = abox_new(_("Disk Usage"), TRUE);
1720 gui_side = start_action(abox, usage_cb, paths,
1721 o_action_force.int_value,
1722 o_action_brief.int_value,
1723 o_action_recurse.int_value,
1724 o_action_newer.int_value);
1725 if (!gui_side)
1726 return;
1728 gui_side->show_info = TRUE;
1730 number_of_windows++;
1732 gtk_widget_show(abox);
1735 /* Mount/unmount listed items (paths).
1736 * Free the list after this function returns.
1737 * If open_dir is TRUE and the dir is successfully mounted, open it.
1738 * quiet can be -1 for default.
1740 void action_mount(GList *paths, gboolean open_dir, int quiet)
1742 #ifdef DO_MOUNT_POINTS
1743 GUIside *gui_side;
1744 GtkWidget *abox;
1746 if (quiet == -1)
1747 quiet = o_action_mount.int_value;
1749 mount_open_dir = open_dir;
1751 abox = abox_new(_("Mount / Unmount"), quiet);
1752 gui_side = start_action(abox, mount_cb, paths,
1753 o_action_force.int_value,
1754 o_action_brief.int_value,
1755 o_action_recurse.int_value,
1756 o_action_newer.int_value);
1757 if (!gui_side)
1758 return;
1760 number_of_windows++;
1761 gtk_widget_show(abox);
1762 #else
1763 report_error(
1764 _("ROX-Filer does not yet support mount points on your "
1765 "system. Sorry."));
1766 #endif /* DO_MOUNT_POINTS */
1769 /* Deletes all selected items in the window */
1770 void action_delete(GList *paths)
1772 GUIside *gui_side;
1773 GtkWidget *abox;
1775 if (!remove_pinned_ok(paths))
1776 return;
1778 abox = abox_new(_("Delete"), o_action_delete.int_value);
1779 gui_side = start_action(abox, delete_cb, paths,
1780 o_action_force.int_value,
1781 o_action_brief.int_value,
1782 o_action_recurse.int_value,
1783 o_action_newer.int_value);
1784 if (!gui_side)
1785 return;
1787 abox_add_flag(ABOX(abox),
1788 _("Force"), _("Don't confirm deletion of non-writeable items"),
1789 'F', o_action_force.int_value);
1790 abox_add_flag(ABOX(abox),
1791 _("Brief"), _("Only log directories being deleted"),
1792 'B', o_action_brief.int_value);
1794 number_of_windows++;
1795 gtk_widget_show(abox);
1798 /* Change the permissions of the selected items */
1799 void action_chmod(GList *paths)
1801 GtkWidget *abox;
1802 GUIside *gui_side;
1803 static GList *presets = NULL;
1805 if (!paths)
1807 report_error(_("You need to select the items "
1808 "whose permissions you want to change"));
1809 return;
1812 if (!presets)
1814 presets = g_list_append(presets, (gchar *)
1815 _("a+x (Make executable/searchable)"));
1816 presets = g_list_append(presets, (gchar *)
1817 _("a-x (Make non-executable/non-searchable)"));
1818 presets = g_list_append(presets, (gchar *)
1819 _("u+rw (Give owner read+write)"));
1820 presets = g_list_append(presets, (gchar *)
1821 _("go-rwx (Private - owner access only)"));
1822 presets = g_list_append(presets, (gchar *)
1823 _("go=u-w (Public access, not write)"));
1826 if (!last_chmod_string)
1827 last_chmod_string = g_strdup((guchar *) presets->data);
1828 new_entry_string = last_chmod_string;
1830 abox = abox_new(_("Permissions"), FALSE);
1831 gui_side = start_action(abox, chmod_cb, paths,
1832 o_action_force.int_value,
1833 o_action_brief.int_value,
1834 o_action_recurse.int_value,
1835 o_action_newer.int_value);
1836 if (!gui_side)
1837 return;
1839 abox_add_flag(ABOX(abox),
1840 _("Brief"), _("Don't list processed files"),
1841 'B', o_action_brief.int_value);
1842 abox_add_flag(ABOX(abox),
1843 _("Recurse"), _("Also change contents of subdirectories"),
1844 'R', o_action_recurse.int_value);
1846 gui_side->default_string = &last_chmod_string;
1847 abox_add_combo(ABOX(abox), presets, last_chmod_string,
1848 new_help_button(show_chmod_help, NULL));
1850 g_signal_connect(ABOX(abox)->entry, "changed",
1851 G_CALLBACK(entry_changed), gui_side);
1852 #if 0
1853 g_signal_connect_swapped(gui_side->entry, "activate",
1854 G_CALLBACK(gtk_button_clicked),
1855 gui_side->yes);
1856 #endif
1858 number_of_windows++;
1859 gtk_widget_show(abox);
1862 /* If leaf is NULL then the copy has the same name as the original.
1863 * quiet can be -1 for default.
1865 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
1867 GUIside *gui_side;
1868 GtkWidget *abox;
1870 if (quiet == -1)
1871 quiet = o_action_copy.int_value;
1873 action_dest = dest;
1874 action_leaf = leaf;
1875 action_do_func = do_copy;
1877 abox = abox_new(_("Copy"), quiet);
1878 gui_side = start_action(abox, list_cb, paths,
1879 o_action_force.int_value,
1880 o_action_brief.int_value,
1881 o_action_recurse.int_value,
1882 o_action_newer.int_value);
1883 if (!gui_side)
1884 return;
1886 abox_add_flag(ABOX(abox),
1887 _("Newer"),
1888 _("Only over-write if source is newer than destination."),
1889 'W', o_action_newer.int_value);
1891 number_of_windows++;
1892 gtk_widget_show(abox);
1895 /* If leaf is NULL then the file is not renamed.
1896 * quiet can be -1 for default.
1898 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
1900 GUIside *gui_side;
1901 GtkWidget *abox;
1903 if (quiet == -1)
1904 quiet = o_action_move.int_value;
1906 action_dest = dest;
1907 action_leaf = leaf;
1908 action_do_func = do_move;
1910 abox = abox_new(_("Move"), quiet);
1911 gui_side = start_action(abox, list_cb, paths,
1912 o_action_force.int_value,
1913 o_action_brief.int_value,
1914 o_action_recurse.int_value,
1915 o_action_newer.int_value);
1916 if (!gui_side)
1917 return;
1919 abox_add_flag(ABOX(abox),
1920 _("Newer"),
1921 _("Only over-write if source is newer than destination."),
1922 'W', o_action_newer.int_value);
1923 number_of_windows++;
1924 gtk_widget_show(abox);
1927 /* If leaf is NULL then the link will have the same name */
1928 /* XXX: No quiet option here? */
1929 void action_link(GList *paths, const char *dest, const char *leaf)
1931 GtkWidget *abox;
1932 GUIside *gui_side;
1934 action_dest = dest;
1935 action_leaf = leaf;
1936 action_do_func = do_link;
1938 abox = abox_new(_("Link"), o_action_link.int_value);
1939 gui_side = start_action(abox, list_cb, paths,
1940 o_action_force.int_value,
1941 o_action_brief.int_value,
1942 o_action_recurse.int_value,
1943 o_action_newer.int_value);
1944 if (!gui_side)
1945 return;
1947 number_of_windows++;
1948 gtk_widget_show(abox);
1951 void action_init(void)
1953 option_add_int(&o_action_copy, "action_copy", 1);
1954 option_add_int(&o_action_move, "action_move", 1);
1955 option_add_int(&o_action_link, "action_link", 1);
1956 option_add_int(&o_action_delete, "action_delete", 0);
1957 option_add_int(&o_action_mount, "action_mount", 1);
1958 option_add_int(&o_action_force, "action_force", FALSE);
1959 option_add_int(&o_action_brief, "action_brief", FALSE);
1960 option_add_int(&o_action_recurse, "action_recurse", FALSE);
1961 option_add_int(&o_action_newer, "action_newer", FALSE);
1964 #define MAX_ASK 4
1966 /* Check to see if any of the selected items (or their children) are
1967 * on the pinboard or panel. If so, ask for confirmation.
1969 * TRUE if it's OK to lose them.
1971 static gboolean remove_pinned_ok(GList *paths)
1973 GList *ask = NULL, *next;
1974 GString *message;
1975 int i, ask_n = 0;
1976 gboolean retval;
1978 while (paths)
1980 guchar *path = (guchar *) paths->data;
1982 if (icons_require(path))
1984 if (++ask_n > MAX_ASK)
1985 break;
1986 ask = g_list_append(ask, path);
1989 paths = paths->next;
1992 if (!ask)
1993 return TRUE;
1995 if (ask_n > MAX_ASK)
1997 message = g_string_new(_("Deleting items such as "));
1998 ask_n--;
2000 else if (ask_n == 1)
2001 message = g_string_new(_("Deleting the item "));
2002 else
2003 message = g_string_new(_("Deleting the items "));
2005 i = 0;
2006 for (next = ask; next; next = next->next)
2008 guchar *path = (guchar *) next->data;
2009 guchar *leaf;
2011 leaf = strrchr(path, '/');
2012 if (leaf)
2013 leaf++;
2014 else
2015 leaf = path;
2017 g_string_append_c(message, '`');
2018 g_string_append(message, leaf);
2019 g_string_append_c(message, '\'');
2020 i++;
2021 if (i == ask_n - 1 && i > 0)
2022 g_string_append(message, _(" and "));
2023 else if (i < ask_n)
2024 g_string_append(message, ", ");
2027 g_list_free(ask);
2029 if (ask_n == 1)
2030 message = g_string_append(message,
2031 _(" will affect some items on the pinboard "
2032 "or panel - really delete it?"));
2033 else
2035 if (ask_n > MAX_ASK)
2036 message = g_string_append_c(message, ',');
2037 message = g_string_append(message,
2038 _(" will affect some items on the pinboard "
2039 "or panel - really delete them?"));
2042 retval = get_choice(PROJECT, message->str,
2043 2, _("Cancel"), _("Delete")) == 1;
2045 g_string_free(message, TRUE);
2047 return retval;
2050 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2052 FindCondition *cond;
2054 cond = find_compile(string);
2055 entry_set_error(widget, !cond);
2057 if (cond)
2058 find_condition_free(cond);