r1379: Converted icon to png.
[rox-filer.git] / ROX-Filer / src / action.c
blob76d71887b16a39c4c2411e7fe6db3a13870fc3ff
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 pid_t 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 abox_cancel_ask(gui_side->abox);
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 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
509 gchar code;
511 if (!gui_side->to_child)
512 return;
514 if (response == GTK_RESPONSE_YES)
515 code = 'Y';
516 else if (response == GTK_RESPONSE_NO)
517 code = 'N';
518 else
519 return;
521 fputc(code, gui_side->to_child);
522 fflush(gui_side->to_child);
525 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
527 if (!gui_side->to_child)
528 return;
530 fputc(flag, gui_side->to_child);
531 fflush(gui_side->to_child);
534 static void read_new_entry_text(void)
536 int len;
537 char c;
538 GString *new;
540 new = g_string_new(NULL);
542 for (;;)
544 len = read(from_parent, &c, 1);
545 if (len != 1)
547 fprintf(stderr, "read() error: %s\n",
548 g_strerror(errno));
549 _exit(1); /* Parent died? */
552 if (c == '\n')
553 break;
554 g_string_append_c(new, c);
557 g_free(new_entry_string);
558 new_entry_string = new->str;
559 g_string_free(new, FALSE);
562 static void process_flag(char flag)
564 switch (flag)
566 case 'Q':
567 quiet = !quiet;
568 break;
569 case 'F':
570 o_force = !o_force;
571 break;
572 case 'R':
573 o_recurse = !o_recurse;
574 break;
575 case 'B':
576 o_brief = !o_brief;
577 break;
578 case 'W':
579 o_newer = !o_newer;
580 break;
581 case 'E':
582 read_new_entry_text();
583 break;
584 default:
585 g_string_sprintf(message,
586 "!ERROR: Bad message '%c'\n", flag);
587 send();
588 break;
592 /* If the parent has sent any flag toggles, read them */
593 static void check_flags(void)
595 fd_set set;
596 int got;
597 char retval;
598 struct timeval tv;
600 FD_ZERO(&set);
602 while (1)
604 FD_SET(from_parent, &set);
605 tv.tv_sec = 0;
606 tv.tv_usec = 0;
607 got = select(from_parent + 1, &set, NULL, NULL, &tv);
609 if (got == -1)
610 g_error("select() failed: %s\n", g_strerror(errno));
611 else if (!got)
612 return;
614 got = read(from_parent, &retval, 1);
615 if (got != 1)
616 g_error("read() error: %s\n", g_strerror(errno));
618 process_flag(retval);
622 /* Read until the user sends a reply. If ignore_quiet is TRUE then
623 * the user MUST click Yes or No, else treat quiet on as Yes.
624 * If the user needs prompting then does send().
626 static gboolean reply(int fd, gboolean ignore_quiet)
628 ssize_t len;
629 char retval;
631 if (quiet && !ignore_quiet)
632 return TRUE;
634 send();
636 while (1)
638 len = read(fd, &retval, 1);
639 if (len != 1)
641 fprintf(stderr, "read() error: %s\n",
642 g_strerror(errno));
643 _exit(1); /* Parent died? */
646 switch (retval)
648 case 'Y':
649 g_string_sprintf(message, "' %s\n", _("Yes"));
650 send();
651 return TRUE;
652 case 'N':
653 g_string_sprintf(message, "' %s\n", _("No"));
654 send();
655 return FALSE;
656 default:
657 process_flag(retval);
658 break;
663 static void destroy_action_window(GtkWidget *widget, gpointer data)
665 GUIside *gui_side = (GUIside *) data;
667 if (gui_side->child)
669 kill(gui_side->child, SIGTERM);
670 fclose(gui_side->to_child);
671 close(gui_side->from_child);
672 g_source_remove(gui_side->input_tag);
675 g_free(gui_side);
677 if (--number_of_windows < 1)
678 gtk_main_quit();
681 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
682 * (NULL on failure). The child calls func().
684 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
685 int force, int brief, int recurse, int newer)
687 gboolean autoq;
688 int filedes[4]; /* 0 and 2 are for reading */
689 GUIside *gui_side;
690 int child;
691 struct sigaction act;
693 if (pipe(filedes))
695 report_error("pipe: %s", g_strerror(errno));
696 gtk_widget_destroy(abox);
697 return NULL;
700 if (pipe(filedes + 2))
702 close(filedes[0]);
703 close(filedes[1]);
704 report_error("pipe: %s", g_strerror(errno));
705 gtk_widget_destroy(abox);
706 return NULL;
709 autoq = gtk_toggle_button_get_active(
710 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
712 o_force = force;
713 o_brief = brief;
714 o_recurse = recurse;
715 o_newer = newer;
717 child = fork();
718 switch (child)
720 case -1:
721 report_error("fork: %s", g_strerror(errno));
722 gtk_widget_destroy(abox);
723 return NULL;
724 case 0:
725 /* We are the child */
727 quiet = autoq;
729 /* Reset the SIGCHLD handler */
730 act.sa_handler = SIG_DFL;
731 sigemptyset(&act.sa_mask);
732 act.sa_flags = 0;
733 sigaction(SIGCHLD, &act, NULL);
735 message = g_string_new(NULL);
736 close(filedes[0]);
737 close(filedes[3]);
738 to_parent = fdopen(filedes[1], "wb");
739 from_parent = filedes[2];
740 func(data);
741 send_dir("");
742 _exit(0);
745 /* We are the parent */
746 close(filedes[1]);
747 close(filedes[2]);
748 gui_side = g_malloc(sizeof(GUIside));
749 gui_side->from_child = filedes[0];
750 gui_side->to_child = fdopen(filedes[3], "wb");
751 gui_side->child = child;
752 gui_side->errors = 0;
753 gui_side->show_info = FALSE;
754 gui_side->default_string = NULL;
755 gui_side->entry_string_func = NULL;
757 gui_side->abox = ABOX(abox);
758 g_signal_connect(abox, "destroy",
759 G_CALLBACK(destroy_action_window), gui_side);
761 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
762 g_signal_connect(abox, "flag_toggled",
763 G_CALLBACK(flag_toggled), gui_side);
765 gui_side->input_tag = gtk_input_add_full(gui_side->from_child,
766 GDK_INPUT_READ,
767 message_from_child,
768 NULL, gui_side, NULL);
770 return gui_side;
773 /* ACTIONS ON ONE ITEM */
775 /* These may call themselves recursively, or ask questions, etc */
777 /* Updates the global size_tally, file_counter and dir_counter */
778 static void do_usage(const char *src_path, const char *unused)
780 struct stat info;
782 check_flags();
784 if (mc_lstat(src_path, &info))
786 g_string_sprintf(message, "'%s:\n", src_path);
787 send();
788 send_error();
790 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
792 file_counter++;
793 size_tally += info.st_size;
795 else if (S_ISDIR(info.st_mode))
797 dir_counter++;
798 g_string_sprintf(message, _("?Count contents of %s?"),
799 src_path);
800 if (reply(from_parent, FALSE))
802 char *safe_path;
803 safe_path = g_strdup(src_path);
804 for_dir_contents(do_usage, safe_path, safe_path);
805 g_free(safe_path);
808 else
809 file_counter++;
812 /* dest_path is the dir containing src_path */
813 static void do_delete(const char *src_path, const char *unused)
815 struct stat info;
816 gboolean write_prot;
817 char *safe_path;
819 check_flags();
821 if (mc_lstat(src_path, &info))
823 send_error();
824 return;
827 write_prot = S_ISLNK(info.st_mode) ? FALSE
828 : access(src_path, W_OK) != 0;
829 if (write_prot || !quiet)
831 g_string_sprintf(message, _("?Delete %s'%s'?"),
832 write_prot ? _("WRITE-PROTECTED ") : "",
833 src_path);
834 if (!reply(from_parent, write_prot && !o_force))
835 return;
837 else if (!o_brief)
839 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
840 send();
843 safe_path = g_strdup(src_path);
845 if (S_ISDIR(info.st_mode))
847 for_dir_contents(do_delete, safe_path, safe_path);
848 if (rmdir(safe_path))
850 g_free(safe_path);
851 send_error();
852 return;
854 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
855 safe_path);
856 send();
857 g_string_sprintf(message, "m%s", safe_path);
858 send();
860 else if (unlink(src_path))
861 send_error();
862 else
864 g_string_sprintf(message, "s%s", safe_path);
865 send();
866 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
868 gchar *dir;
869 dir = g_dirname(safe_path);
870 g_string_sprintf(message, "s%s", dir);
871 g_free(dir);
872 send();
876 g_free(safe_path);
879 /* path is the item to check. If is is a directory then we may recurse
880 * (unless prune is used).
882 static void do_find(const char *path, const char *unused)
884 FindInfo info;
886 check_flags();
888 if (!quiet)
890 g_string_sprintf(message, _("?Check '%s'?"), path);
891 if (!reply(from_parent, FALSE))
892 return;
895 for (;;)
897 if (new_entry_string)
899 if (find_condition)
900 find_condition_free(find_condition);
901 find_condition = find_compile(new_entry_string);
902 g_free(new_entry_string);
903 new_entry_string = NULL;
906 if (find_condition)
907 break;
909 g_string_assign(message, _("!Invalid find condition - "
910 "change it and try again\n"));
911 send();
912 g_string_sprintf(message, _("?Check '%s'?"), path);
913 if (!reply(from_parent, TRUE))
914 return;
917 if (mc_lstat(path, &info.stats))
919 send_error();
920 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
921 send();
922 return;
925 info.fullpath = path;
926 time(&info.now); /* XXX: Not for each check! */
928 info.leaf = g_basename(path);
929 info.prune = FALSE;
930 if (find_test_condition(find_condition, &info))
932 g_string_sprintf(message, "=%s", path);
933 send();
936 if (S_ISDIR(info.stats.st_mode) && !info.prune)
938 char *safe_path;
939 safe_path = g_strdup(path);
940 for_dir_contents(do_find, safe_path, safe_path);
941 g_free(safe_path);
945 /* Like mode_compile(), but ignores spaces and bracketed bits */
946 struct mode_change *nice_mode_compile(const char *mode_string,
947 unsigned int masked_ops)
949 GString *new;
950 int brackets = 0;
951 struct mode_change *retval = NULL;
953 new = g_string_new(NULL);
955 for (; *mode_string; mode_string++)
957 if (*mode_string == '(')
958 brackets++;
959 if (*mode_string == ')')
961 brackets--;
962 if (brackets < 0)
963 break;
964 continue;
967 if (brackets == 0 && *mode_string != ' ')
968 g_string_append_c(new, *mode_string);
971 if (brackets == 0)
972 retval = mode_compile(new->str, masked_ops);
973 g_string_free(new, TRUE);
974 return retval;
977 static void do_chmod(const char *path, const char *unused)
979 struct stat info;
980 mode_t new_mode;
982 check_flags();
984 if (mc_lstat(path, &info))
986 send_error();
987 return;
989 if (S_ISLNK(info.st_mode))
990 return;
992 if (!quiet)
994 g_string_sprintf(message,
995 _("?Change permissions of '%s'?"), path);
996 if (!reply(from_parent, FALSE))
997 return;
999 else if (!o_brief)
1001 g_string_sprintf(message,
1002 _("'Changing permissions of '%s'\n"),
1003 path);
1004 send();
1007 for (;;)
1009 if (new_entry_string)
1011 if (mode_change)
1012 mode_free(mode_change);
1013 mode_change = nice_mode_compile(new_entry_string,
1014 MODE_MASK_ALL);
1015 g_free(new_entry_string);
1016 new_entry_string = NULL;
1019 if (mode_change)
1020 break;
1022 g_string_assign(message,
1023 _("!Invalid mode command - change it and try again\n"));
1024 send();
1025 g_string_sprintf(message,
1026 _("?Change permissions of '%s'?"), path);
1027 if (!reply(from_parent, TRUE))
1028 return;
1031 if (mc_lstat(path, &info))
1033 send_error();
1034 return;
1036 if (S_ISLNK(info.st_mode))
1037 return;
1039 new_mode = mode_adjust(info.st_mode, mode_change);
1040 if (chmod(path, new_mode))
1042 send_error();
1043 return;
1046 g_string_sprintf(message, "s%s", path);
1047 send();
1049 if (S_ISDIR(info.st_mode))
1051 g_string_sprintf(message, "m%s", path);
1052 send();
1054 if (o_recurse)
1056 guchar *safe_path;
1057 safe_path = g_strdup(path);
1058 for_dir_contents(do_chmod, safe_path, safe_path);
1059 g_free(safe_path);
1064 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1065 * is set then that is the new leafname, otherwise the leafname stays
1066 * the same.
1068 static char *make_dest_path(const char *object, const char *dir)
1070 const char *leaf;
1072 if (action_leaf)
1073 leaf = action_leaf;
1074 else
1076 leaf = strrchr(object, '/');
1077 if (!leaf)
1078 leaf = object; /* Error? */
1079 else
1080 leaf++;
1083 return make_path(dir, leaf)->str;
1086 /* If action_leaf is not NULL it specifies the new leaf name */
1087 static void do_copy2(const char *path, const char *dest)
1089 char *dest_path;
1090 struct stat info;
1091 struct stat dest_info;
1093 check_flags();
1095 dest_path = make_dest_path(path, dest);
1097 if (mc_lstat(path, &info))
1099 send_error();
1100 return;
1103 if (mc_lstat(dest_path, &dest_info) == 0)
1105 int err;
1106 gboolean merge;
1108 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1110 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1112 /* Newer; keep going */
1114 else
1116 g_string_sprintf(message,
1117 _("?'%s' already exists - %s?"), dest_path,
1118 merge ? _("merge contents") : _("overwrite"));
1120 if (!reply(from_parent, TRUE))
1121 return;
1124 if (!merge)
1126 if (S_ISDIR(dest_info.st_mode))
1127 err = rmdir(dest_path);
1128 else
1129 err = unlink(dest_path);
1131 if (err)
1133 send_error();
1134 if (errno != ENOENT)
1135 return;
1136 g_string_sprintf(message,
1137 _("'Trying copy anyway...\n"));
1138 send();
1142 else if (!quiet)
1144 g_string_sprintf(message,
1145 _("?Copy %s as %s?"), path, dest_path);
1146 if (!reply(from_parent, FALSE))
1147 return;
1149 else
1151 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1152 dest_path);
1153 send();
1156 if (S_ISDIR(info.st_mode))
1158 mode_t mode = info.st_mode;
1159 char *safe_path, *safe_dest;
1160 struct stat dest_info;
1161 gboolean exists;
1163 safe_path = g_strdup(path);
1164 safe_dest = g_strdup(dest_path);
1166 exists = !mc_lstat(dest_path, &dest_info);
1168 if (exists && !S_ISDIR(dest_info.st_mode))
1170 g_string_sprintf(message,
1171 _("!ERROR: Destination already exists, "
1172 "but is not a directory\n"));
1173 send();
1175 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1176 send_error();
1177 else
1179 if (!exists)
1181 /* (just been created then) */
1182 g_string_sprintf(message, "s%s", dest_path);
1183 send();
1186 action_leaf = NULL;
1187 for_dir_contents(do_copy2, safe_path, safe_dest);
1188 /* Note: dest_path now invalid... */
1190 if (!exists)
1192 struct utimbuf utb;
1194 /* We may have created the directory with
1195 * more permissions than the source so that
1196 * we could write to it... change it back now.
1198 if (chmod(safe_dest, mode))
1199 send_error();
1201 /* Also, try to preserve the timestamps */
1202 utb.actime = info.st_atime;
1203 utb.modtime = info.st_mtime;
1205 utime(safe_dest, &utb);
1209 g_free(safe_path);
1210 g_free(safe_dest);
1212 else if (S_ISLNK(info.st_mode))
1214 char *target;
1216 /* Not all versions of cp(1) can make symlinks,
1217 * so we special-case it.
1220 target = readlink_dup(path);
1221 if (target)
1223 if (symlink(target, dest_path))
1224 send_error();
1225 else
1227 g_string_sprintf(message, "s%s", dest_path);
1228 send();
1231 g_free(target);
1233 else
1234 send_error();
1236 else
1238 guchar *error;
1240 error = copy_file(path, dest_path);
1242 if (error)
1244 g_string_sprintf(message, _("!%s\nFailed to copy '%s'"),
1245 error, path);
1246 g_free(error);
1247 send();
1249 else
1251 g_string_sprintf(message, "s%s", dest_path);
1252 send();
1257 /* If action_leaf is not NULL it specifies the new leaf name */
1258 static void do_move2(const char *path, const char *dest)
1260 char *dest_path;
1261 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1262 struct stat info2;
1263 gboolean is_dir;
1264 char *err;
1266 check_flags();
1268 dest_path = make_dest_path(path, dest);
1270 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1272 if (access(dest_path, F_OK) == 0)
1274 struct stat info;
1275 int err;
1277 if (mc_lstat(dest_path, &info))
1279 send_error();
1280 return;
1283 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1285 /* Newer; keep going */
1287 else
1289 g_string_sprintf(message,
1290 _("?'%s' already exists - overwrite?"),
1291 dest_path);
1292 if (!reply(from_parent, TRUE))
1293 return;
1296 if (S_ISDIR(info.st_mode))
1297 err = rmdir(dest_path);
1298 else
1299 err = unlink(dest_path);
1301 if (err)
1303 send_error();
1304 if (errno != ENOENT)
1305 return;
1306 g_string_sprintf(message,
1307 _("'Trying move anyway...\n"));
1308 send();
1311 else if (!quiet)
1313 g_string_sprintf(message,
1314 _("?Move %s as %s?"), path, dest_path);
1315 if (!reply(from_parent, FALSE))
1316 return;
1318 else
1320 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1321 dest_path);
1322 send();
1325 argv[2] = path;
1326 argv[3] = dest_path;
1328 err = fork_exec_wait(argv);
1329 if (err)
1331 g_string_sprintf(message,
1332 _("!%s\nFailed to move %s as %s\n"),
1333 err, path, dest_path);
1334 send();
1336 g_free(err);
1338 else
1340 g_string_sprintf(message, "s%s", dest_path);
1341 send();
1343 if (is_dir)
1344 g_string_sprintf(message, "m%s", path);
1345 else
1346 g_string_sprintf(message, "s%s", path);
1348 send();
1352 /* Copy path to dest.
1353 * Check that path not copied into itself.
1355 static void do_copy(const char *path, const char *dest)
1357 if (is_sub_dir(make_dest_path(path, dest), path))
1359 g_string_sprintf(message,
1360 _("!ERROR: Can't copy object into itself\n"));
1361 send();
1363 else
1364 do_copy2(path, dest);
1367 /* Move path to dest.
1368 * Check that path not moved into itself.
1370 static void do_move(const char *path, const char *dest)
1372 if (is_sub_dir(make_dest_path(path, dest), path))
1374 g_string_sprintf(message,
1375 _("!ERROR: Can't move/rename object into itself\n"));
1376 send();
1378 else
1379 do_move2(path, dest);
1382 static void do_link(const char *path, const char *dest)
1384 char *dest_path;
1386 check_flags();
1388 dest_path = make_dest_path(path, dest);
1390 if (quiet)
1392 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1393 dest_path);
1394 send();
1396 else
1398 g_string_sprintf(message,
1399 _("?Link %s as %s?"), path, dest_path);
1400 if (!reply(from_parent, FALSE))
1401 return;
1404 if (symlink(path, dest_path))
1405 send_error();
1406 else
1408 g_string_sprintf(message, "s%s", dest_path);
1409 send();
1413 /* Mount/umount this item (depending on 'mount') */
1414 static void do_mount(guchar *path, gboolean mount)
1416 const char *argv[3] = {NULL, NULL, NULL};
1417 char *err;
1419 check_flags();
1421 argv[0] = mount ? "mount" : "umount";
1422 argv[1] = path;
1424 if (quiet)
1426 g_string_sprintf(message,
1427 mount ? _("'Mounting %s\n")
1428 : _("'Unmounting %s\n"),
1429 path);
1430 send();
1432 else
1434 g_string_sprintf(message,
1435 mount ? _("?Mount %s?")
1436 : _("?Unmount %s?"),
1437 path);
1438 if (!reply(from_parent, FALSE))
1439 return;
1442 err = fork_exec_wait(argv);
1443 if (err)
1445 g_string_sprintf(message, mount ?
1446 _("!%s\nMount failed\n") :
1447 _("!%s\nUnmount failed\n"), err);
1448 send();
1449 g_free(err);
1451 else
1453 g_string_sprintf(message, "M%s", path);
1454 send();
1455 if (mount && mount_open_dir)
1457 g_string_sprintf(message, "o%s", path);
1458 send();
1463 /* CHILD MAIN LOOPS */
1465 /* After forking, the child calls one of these functions */
1467 /* We use a double for total size in order to count beyond 4Gb */
1468 static void usage_cb(gpointer data)
1470 GList *paths = (GList *) data;
1471 double total_size = 0;
1472 gchar *tmp;
1474 dir_counter = file_counter = 0;
1476 for (; paths; paths = paths->next)
1478 guchar *path = (guchar *) paths->data;
1480 send_dir(path);
1482 size_tally = 0;
1484 do_usage(path, NULL);
1486 g_string_sprintf(message, "'%s: %s\n",
1487 g_basename(path),
1488 format_double_size(size_tally));
1489 send();
1490 total_size += size_tally;
1493 g_string_sprintf(message, _("'\nTotal: %s ("),
1494 format_double_size(total_size));
1496 if (file_counter)
1498 tmp = g_strdup_printf("%ld %s%s",
1499 file_counter,
1500 file_counter == 1 ? _("file") : _("files"),
1501 dir_counter ? ", " : ")\n");
1502 g_string_append(message, tmp);
1503 g_free(tmp);
1506 if (file_counter == 0 && dir_counter == 0)
1507 g_string_append(message, _("no directories)\n"));
1508 else if (dir_counter)
1510 tmp = g_strdup_printf("%ld %s)\n",
1511 dir_counter,
1512 dir_counter == 1 ? _("directory")
1513 : _("directories"));
1514 g_string_append(message, tmp);
1515 g_free(tmp);
1518 send();
1521 #ifdef DO_MOUNT_POINTS
1522 static void mount_cb(gpointer data)
1524 GList *paths = (GList *) data;
1525 gboolean mount_points = FALSE;
1527 for (; paths; paths = paths->next)
1529 guchar *path = (guchar *) paths->data;
1531 if (mount_is_mounted(path, NULL, NULL))
1532 do_mount(path, FALSE); /* Unmount */
1533 else if (g_hash_table_lookup(fstab_mounts, path))
1534 do_mount(path, TRUE); /* Mount */
1535 else
1536 continue;
1538 mount_points = TRUE;
1541 g_string_sprintf(message,
1542 mount_points ? _("'\nDone\n")
1543 : _("!No mount points selected!\n"));
1544 send();
1546 #endif
1548 /* (use g_dirname() instead?) */
1549 static guchar *dirname(guchar *path)
1551 guchar *slash;
1553 slash = strrchr(path, '/');
1554 g_return_val_if_fail(slash != NULL, g_strdup(path));
1556 if (slash != path)
1557 return g_strndup(path, slash - path);
1558 return g_strdup("/");
1561 static void delete_cb(gpointer data)
1563 GList *paths = (GList *) data;
1565 while (paths)
1567 guchar *path = (guchar *) paths->data;
1568 guchar *dir;
1570 dir = dirname(path);
1571 send_dir(dir);
1573 do_delete(path, dir);
1575 g_free(dir);
1576 paths = paths->next;
1579 g_string_sprintf(message, _("'\nDone\n"));
1580 send();
1583 static void find_cb(gpointer data)
1585 GList *all_paths = (GList *) data;
1586 GList *paths;
1588 while (1)
1590 for (paths = all_paths; paths; paths = paths->next)
1592 guchar *path = (guchar *) paths->data;
1594 send_dir(path);
1596 do_find(path, NULL);
1599 g_string_assign(message, _("?Another search?"));
1600 if (!reply(from_parent, TRUE))
1601 break;
1602 g_string_assign(message, "#");
1603 send();
1606 g_string_sprintf(message, _("'\nDone\n"));
1607 send();
1610 static void chmod_cb(gpointer data)
1612 GList *paths = (GList *) data;
1614 for (; paths; paths = paths->next)
1616 guchar *path = (guchar *) paths->data;
1617 struct stat info;
1619 send_dir(path);
1621 if (mc_stat(path, &info) != 0)
1622 send_error();
1623 else if (S_ISLNK(info.st_mode))
1625 g_string_sprintf(message,
1626 _("!'%s' is a symbolic link\n"),
1627 g_basename(path));
1628 send();
1630 else
1631 do_chmod(path, NULL);
1634 g_string_sprintf(message, _("'\nDone\n"));
1635 send();
1638 static void list_cb(gpointer data)
1640 GList *paths = (GList *) data;
1642 while (paths)
1644 send_dir((char *) paths->data);
1646 action_do_func((char *) paths->data, action_dest);
1648 paths = paths->next;
1651 g_string_sprintf(message, _("'\nDone\n"));
1652 send();
1655 /* EXTERNAL INTERFACE */
1657 void action_find(GList *paths)
1659 GUIside *gui_side;
1660 GtkWidget *abox;
1662 if (!paths)
1664 report_error(_("You need to select some items "
1665 "to search through"));
1666 return;
1669 if (!last_find_string)
1670 last_find_string = g_strdup("'core'");
1672 new_entry_string = last_find_string;
1674 abox = abox_new(_("Find"), FALSE);
1675 gui_side = start_action(abox, find_cb, paths,
1676 o_action_force.int_value,
1677 o_action_brief.int_value,
1678 o_action_recurse.int_value,
1679 o_action_newer.int_value);
1680 if (!gui_side)
1681 return;
1683 abox_add_results(ABOX(abox));
1685 gui_side->default_string = &last_find_string;
1686 abox_add_entry(ABOX(abox), last_find_string,
1687 new_help_button(show_condition_help, NULL));
1688 g_signal_connect(ABOX(abox)->entry, "changed",
1689 G_CALLBACK(entry_changed), gui_side);
1690 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1692 gui_side->show_info = TRUE;
1693 gui_side->entry_string_func = set_find_string_colour;
1695 number_of_windows++;
1696 gtk_widget_show_all(abox);
1699 /* Count disk space used by selected items */
1700 void action_usage(GList *paths)
1702 GUIside *gui_side;
1703 GtkWidget *abox;
1705 if (!paths)
1707 report_error(_("You need to select some items to count"));
1708 return;
1711 abox = abox_new(_("Disk Usage"), TRUE);
1713 gui_side = start_action(abox, usage_cb, paths,
1714 o_action_force.int_value,
1715 o_action_brief.int_value,
1716 o_action_recurse.int_value,
1717 o_action_newer.int_value);
1718 if (!gui_side)
1719 return;
1721 gui_side->show_info = TRUE;
1723 number_of_windows++;
1725 gtk_widget_show(abox);
1728 /* Mount/unmount listed items (paths).
1729 * Free the list after this function returns.
1730 * If open_dir is TRUE and the dir is successfully mounted, open it.
1731 * quiet can be -1 for default.
1733 void action_mount(GList *paths, gboolean open_dir, int quiet)
1735 #ifdef DO_MOUNT_POINTS
1736 GUIside *gui_side;
1737 GtkWidget *abox;
1739 if (quiet == -1)
1740 quiet = o_action_mount.int_value;
1742 mount_open_dir = open_dir;
1744 abox = abox_new(_("Mount / Unmount"), quiet);
1745 gui_side = start_action(abox, mount_cb, paths,
1746 o_action_force.int_value,
1747 o_action_brief.int_value,
1748 o_action_recurse.int_value,
1749 o_action_newer.int_value);
1750 if (!gui_side)
1751 return;
1753 number_of_windows++;
1754 gtk_widget_show(abox);
1755 #else
1756 report_error(
1757 _("ROX-Filer does not yet support mount points on your "
1758 "system. Sorry."));
1759 #endif /* DO_MOUNT_POINTS */
1762 /* Deletes all selected items in the window */
1763 void action_delete(GList *paths)
1765 GUIside *gui_side;
1766 GtkWidget *abox;
1768 if (!remove_pinned_ok(paths))
1769 return;
1771 abox = abox_new(_("Delete"), o_action_delete.int_value);
1772 gui_side = start_action(abox, delete_cb, paths,
1773 o_action_force.int_value,
1774 o_action_brief.int_value,
1775 o_action_recurse.int_value,
1776 o_action_newer.int_value);
1777 if (!gui_side)
1778 return;
1780 abox_add_flag(ABOX(abox),
1781 _("Force"), _("Don't confirm deletion of non-writeable items"),
1782 'F', o_action_force.int_value);
1783 abox_add_flag(ABOX(abox),
1784 _("Brief"), _("Only log directories being deleted"),
1785 'B', o_action_brief.int_value);
1787 number_of_windows++;
1788 gtk_widget_show(abox);
1791 /* Change the permissions of the selected items */
1792 void action_chmod(GList *paths)
1794 GtkWidget *abox;
1795 GUIside *gui_side;
1796 static GList *presets = NULL;
1798 if (!paths)
1800 report_error(_("You need to select the items "
1801 "whose permissions you want to change"));
1802 return;
1805 if (!presets)
1807 presets = g_list_append(presets, (gchar *)
1808 _("a+x (Make executable/searchable)"));
1809 presets = g_list_append(presets, (gchar *)
1810 _("a-x (Make non-executable/non-searchable)"));
1811 presets = g_list_append(presets, (gchar *)
1812 _("u+rw (Give owner read+write)"));
1813 presets = g_list_append(presets, (gchar *)
1814 _("go-rwx (Private - owner access only)"));
1815 presets = g_list_append(presets, (gchar *)
1816 _("go=u-w (Public access, not write)"));
1819 if (!last_chmod_string)
1820 last_chmod_string = g_strdup((guchar *) presets->data);
1821 new_entry_string = last_chmod_string;
1823 abox = abox_new(_("Permissions"), FALSE);
1824 gui_side = start_action(abox, chmod_cb, paths,
1825 o_action_force.int_value,
1826 o_action_brief.int_value,
1827 o_action_recurse.int_value,
1828 o_action_newer.int_value);
1829 if (!gui_side)
1830 return;
1832 abox_add_flag(ABOX(abox),
1833 _("Brief"), _("Don't list processed files"),
1834 'B', o_action_brief.int_value);
1835 abox_add_flag(ABOX(abox),
1836 _("Recurse"), _("Also change contents of subdirectories"),
1837 'R', o_action_recurse.int_value);
1839 gui_side->default_string = &last_chmod_string;
1840 abox_add_combo(ABOX(abox), presets, last_chmod_string,
1841 new_help_button(show_chmod_help, NULL));
1843 g_signal_connect(ABOX(abox)->entry, "changed",
1844 G_CALLBACK(entry_changed), gui_side);
1845 #if 0
1846 g_signal_connect_swapped(gui_side->entry, "activate",
1847 G_CALLBACK(gtk_button_clicked),
1848 gui_side->yes);
1849 #endif
1851 number_of_windows++;
1852 gtk_widget_show(abox);
1855 /* If leaf is NULL then the copy has the same name as the original.
1856 * quiet can be -1 for default.
1858 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
1860 GUIside *gui_side;
1861 GtkWidget *abox;
1863 if (quiet == -1)
1864 quiet = o_action_copy.int_value;
1866 action_dest = dest;
1867 action_leaf = leaf;
1868 action_do_func = do_copy;
1870 abox = abox_new(_("Copy"), quiet);
1871 gui_side = start_action(abox, list_cb, paths,
1872 o_action_force.int_value,
1873 o_action_brief.int_value,
1874 o_action_recurse.int_value,
1875 o_action_newer.int_value);
1876 if (!gui_side)
1877 return;
1879 abox_add_flag(ABOX(abox),
1880 _("Newer"),
1881 _("Only over-write if source is newer than destination."),
1882 'W', o_action_newer.int_value);
1884 number_of_windows++;
1885 gtk_widget_show(abox);
1888 /* If leaf is NULL then the file is not renamed.
1889 * quiet can be -1 for default.
1891 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
1893 GUIside *gui_side;
1894 GtkWidget *abox;
1896 if (quiet == -1)
1897 quiet = o_action_move.int_value;
1899 action_dest = dest;
1900 action_leaf = leaf;
1901 action_do_func = do_move;
1903 abox = abox_new(_("Move"), quiet);
1904 gui_side = start_action(abox, list_cb, paths,
1905 o_action_force.int_value,
1906 o_action_brief.int_value,
1907 o_action_recurse.int_value,
1908 o_action_newer.int_value);
1909 if (!gui_side)
1910 return;
1912 abox_add_flag(ABOX(abox),
1913 _("Newer"),
1914 _("Only over-write if source is newer than destination."),
1915 'W', o_action_newer.int_value);
1916 number_of_windows++;
1917 gtk_widget_show(abox);
1920 /* If leaf is NULL then the link will have the same name */
1921 /* XXX: No quiet option here? */
1922 void action_link(GList *paths, const char *dest, const char *leaf)
1924 GtkWidget *abox;
1925 GUIside *gui_side;
1927 action_dest = dest;
1928 action_leaf = leaf;
1929 action_do_func = do_link;
1931 abox = abox_new(_("Link"), o_action_link.int_value);
1932 gui_side = start_action(abox, list_cb, paths,
1933 o_action_force.int_value,
1934 o_action_brief.int_value,
1935 o_action_recurse.int_value,
1936 o_action_newer.int_value);
1937 if (!gui_side)
1938 return;
1940 number_of_windows++;
1941 gtk_widget_show(abox);
1944 void action_init(void)
1946 option_add_int(&o_action_copy, "action_copy", 1);
1947 option_add_int(&o_action_move, "action_move", 1);
1948 option_add_int(&o_action_link, "action_link", 1);
1949 option_add_int(&o_action_delete, "action_delete", 0);
1950 option_add_int(&o_action_mount, "action_mount", 1);
1951 option_add_int(&o_action_force, "action_force", FALSE);
1952 option_add_int(&o_action_brief, "action_brief", FALSE);
1953 option_add_int(&o_action_recurse, "action_recurse", FALSE);
1954 option_add_int(&o_action_newer, "action_newer", FALSE);
1957 #define MAX_ASK 4
1959 /* Check to see if any of the selected items (or their children) are
1960 * on the pinboard or panel. If so, ask for confirmation.
1962 * TRUE if it's OK to lose them.
1964 static gboolean remove_pinned_ok(GList *paths)
1966 GList *ask = NULL, *next;
1967 GString *message;
1968 int i, ask_n = 0;
1969 gboolean retval;
1971 while (paths)
1973 guchar *path = (guchar *) paths->data;
1975 if (icons_require(path))
1977 if (++ask_n > MAX_ASK)
1978 break;
1979 ask = g_list_append(ask, path);
1982 paths = paths->next;
1985 if (!ask)
1986 return TRUE;
1988 if (ask_n > MAX_ASK)
1990 message = g_string_new(_("Deleting items such as "));
1991 ask_n--;
1993 else if (ask_n == 1)
1994 message = g_string_new(_("Deleting the item "));
1995 else
1996 message = g_string_new(_("Deleting the items "));
1998 i = 0;
1999 for (next = ask; next; next = next->next)
2001 guchar *path = (guchar *) next->data;
2002 guchar *leaf;
2004 leaf = strrchr(path, '/');
2005 if (leaf)
2006 leaf++;
2007 else
2008 leaf = path;
2010 g_string_append_c(message, '`');
2011 g_string_append(message, leaf);
2012 g_string_append_c(message, '\'');
2013 i++;
2014 if (i == ask_n - 1 && i > 0)
2015 g_string_append(message, _(" and "));
2016 else if (i < ask_n)
2017 g_string_append(message, ", ");
2020 g_list_free(ask);
2022 if (ask_n == 1)
2023 message = g_string_append(message,
2024 _(" will affect some items on the pinboard "
2025 "or panel - really delete it?"));
2026 else
2028 if (ask_n > MAX_ASK)
2029 message = g_string_append_c(message, ',');
2030 message = g_string_append(message,
2031 _(" will affect some items on the pinboard "
2032 "or panel - really delete them?"));
2035 retval = get_choice(PROJECT, message->str,
2036 2, _("Cancel"), _("Delete")) == 1;
2038 g_string_free(message, TRUE);
2040 return retval;
2043 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2045 FindCondition *cond;
2047 cond = find_compile(string);
2048 entry_set_error(widget, !cond);
2050 if (cond)
2051 find_condition_free(cond);