r1601: Turned on more compiler warnings, and fixed some minor issues it threw up.
[rox-filer.git] / ROX-Filer / src / action.c
blob95c960c5db958e2f5925862be541c09bc3278a8e
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(void);
125 static gboolean send_error(void);
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_from_stock(GTK_STOCK_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_from_stock(GTK_STOCK_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 one_less_window();
680 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
681 * (NULL on failure). The child calls func().
683 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
684 int force, int brief, int recurse, int newer)
686 gboolean autoq;
687 int filedes[4]; /* 0 and 2 are for reading */
688 GUIside *gui_side;
689 pid_t child;
690 struct sigaction act;
692 if (pipe(filedes))
694 report_error("pipe: %s", g_strerror(errno));
695 gtk_widget_destroy(abox);
696 return NULL;
699 if (pipe(filedes + 2))
701 close(filedes[0]);
702 close(filedes[1]);
703 report_error("pipe: %s", g_strerror(errno));
704 gtk_widget_destroy(abox);
705 return NULL;
708 autoq = gtk_toggle_button_get_active(
709 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
711 o_force = force;
712 o_brief = brief;
713 o_recurse = recurse;
714 o_newer = newer;
716 child = fork();
717 switch (child)
719 case -1:
720 report_error("fork: %s", g_strerror(errno));
721 gtk_widget_destroy(abox);
722 return NULL;
723 case 0:
724 /* We are the child */
726 quiet = autoq;
728 /* Reset the SIGCHLD handler */
729 act.sa_handler = SIG_DFL;
730 sigemptyset(&act.sa_mask);
731 act.sa_flags = 0;
732 sigaction(SIGCHLD, &act, NULL);
734 message = g_string_new(NULL);
735 close(filedes[0]);
736 close(filedes[3]);
737 to_parent = fdopen(filedes[1], "wb");
738 from_parent = filedes[2];
739 func(data);
740 send_dir("");
741 _exit(0);
744 /* We are the parent */
745 close(filedes[1]);
746 close(filedes[2]);
747 gui_side = g_malloc(sizeof(GUIside));
748 gui_side->from_child = filedes[0];
749 gui_side->to_child = fdopen(filedes[3], "wb");
750 gui_side->child = child;
751 gui_side->errors = 0;
752 gui_side->show_info = FALSE;
753 gui_side->default_string = NULL;
754 gui_side->entry_string_func = NULL;
756 gui_side->abox = ABOX(abox);
757 g_signal_connect(abox, "destroy",
758 G_CALLBACK(destroy_action_window), gui_side);
760 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
761 g_signal_connect(abox, "flag_toggled",
762 G_CALLBACK(flag_toggled), gui_side);
764 gui_side->input_tag = gtk_input_add_full(gui_side->from_child,
765 GDK_INPUT_READ,
766 message_from_child,
767 NULL, gui_side, NULL);
769 return gui_side;
772 /* ACTIONS ON ONE ITEM */
774 /* These may call themselves recursively, or ask questions, etc */
776 /* Updates the global size_tally, file_counter and dir_counter */
777 static void do_usage(const char *src_path, const char *unused)
779 struct stat info;
781 check_flags();
783 if (mc_lstat(src_path, &info))
785 g_string_sprintf(message, "'%s:\n", src_path);
786 send();
787 send_error();
789 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
791 file_counter++;
792 size_tally += info.st_size;
794 else if (S_ISDIR(info.st_mode))
796 dir_counter++;
797 g_string_sprintf(message, _("?Count contents of %s?"),
798 src_path);
799 if (reply(from_parent, FALSE))
801 char *safe_path;
802 safe_path = g_strdup(src_path);
803 for_dir_contents(do_usage, safe_path, safe_path);
804 g_free(safe_path);
807 else
808 file_counter++;
811 /* dest_path is the dir containing src_path */
812 static void do_delete(const char *src_path, const char *unused)
814 struct stat info;
815 gboolean write_prot;
816 char *safe_path;
818 check_flags();
820 if (mc_lstat(src_path, &info))
822 send_error();
823 return;
826 write_prot = S_ISLNK(info.st_mode) ? FALSE
827 : access(src_path, W_OK) != 0;
828 if (write_prot || !quiet)
830 g_string_sprintf(message, _("?Delete %s'%s'?"),
831 write_prot ? _("WRITE-PROTECTED ") : "",
832 src_path);
833 if (!reply(from_parent, write_prot && !o_force))
834 return;
836 else if (!o_brief)
838 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
839 send();
842 safe_path = g_strdup(src_path);
844 if (S_ISDIR(info.st_mode))
846 for_dir_contents(do_delete, safe_path, safe_path);
847 if (rmdir(safe_path))
849 g_free(safe_path);
850 send_error();
851 return;
853 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
854 safe_path);
855 send();
856 g_string_sprintf(message, "m%s", safe_path);
857 send();
859 else if (unlink(src_path))
860 send_error();
861 else
863 g_string_sprintf(message, "s%s", safe_path);
864 send();
865 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
867 gchar *dir;
868 dir = g_dirname(safe_path);
869 g_string_sprintf(message, "s%s", dir);
870 g_free(dir);
871 send();
875 g_free(safe_path);
878 /* path is the item to check. If is is a directory then we may recurse
879 * (unless prune is used).
881 static void do_find(const char *path, const char *unused)
883 FindInfo info;
885 check_flags();
887 if (!quiet)
889 g_string_sprintf(message, _("?Check '%s'?"), path);
890 if (!reply(from_parent, FALSE))
891 return;
894 for (;;)
896 if (new_entry_string)
898 if (find_condition)
899 find_condition_free(find_condition);
900 find_condition = find_compile(new_entry_string);
901 g_free(new_entry_string);
902 new_entry_string = NULL;
905 if (find_condition)
906 break;
908 g_string_assign(message, _("!Invalid find condition - "
909 "change it and try again\n"));
910 send();
911 g_string_sprintf(message, _("?Check '%s'?"), path);
912 if (!reply(from_parent, TRUE))
913 return;
916 if (mc_lstat(path, &info.stats))
918 send_error();
919 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
920 send();
921 return;
924 info.fullpath = path;
925 time(&info.now); /* XXX: Not for each check! */
927 info.leaf = g_basename(path);
928 info.prune = FALSE;
929 if (find_test_condition(find_condition, &info))
931 g_string_sprintf(message, "=%s", path);
932 send();
935 if (S_ISDIR(info.stats.st_mode) && !info.prune)
937 char *safe_path;
938 safe_path = g_strdup(path);
939 for_dir_contents(do_find, safe_path, safe_path);
940 g_free(safe_path);
944 /* Like mode_compile(), but ignores spaces and bracketed bits */
945 static struct mode_change *nice_mode_compile(const char *mode_string,
946 unsigned int masked_ops)
948 GString *new;
949 int brackets = 0;
950 struct mode_change *retval = NULL;
952 new = g_string_new(NULL);
954 for (; *mode_string; mode_string++)
956 if (*mode_string == '(')
957 brackets++;
958 if (*mode_string == ')')
960 brackets--;
961 if (brackets < 0)
962 break;
963 continue;
966 if (brackets == 0 && *mode_string != ' ')
967 g_string_append_c(new, *mode_string);
970 if (brackets == 0)
971 retval = mode_compile(new->str, masked_ops);
972 g_string_free(new, TRUE);
973 return retval;
976 static void do_chmod(const char *path, const char *unused)
978 struct stat info;
979 mode_t new_mode;
981 check_flags();
983 if (mc_lstat(path, &info))
985 send_error();
986 return;
988 if (S_ISLNK(info.st_mode))
989 return;
991 if (!quiet)
993 g_string_sprintf(message,
994 _("?Change permissions of '%s'?"), path);
995 if (!reply(from_parent, FALSE))
996 return;
998 else if (!o_brief)
1000 g_string_sprintf(message,
1001 _("'Changing permissions of '%s'\n"),
1002 path);
1003 send();
1006 for (;;)
1008 if (new_entry_string)
1010 if (mode_change)
1011 mode_free(mode_change);
1012 mode_change = nice_mode_compile(new_entry_string,
1013 MODE_MASK_ALL);
1014 g_free(new_entry_string);
1015 new_entry_string = NULL;
1018 if (mode_change)
1019 break;
1021 g_string_assign(message,
1022 _("!Invalid mode command - change it and try again\n"));
1023 send();
1024 g_string_sprintf(message,
1025 _("?Change permissions of '%s'?"), path);
1026 if (!reply(from_parent, TRUE))
1027 return;
1030 if (mc_lstat(path, &info))
1032 send_error();
1033 return;
1035 if (S_ISLNK(info.st_mode))
1036 return;
1038 new_mode = mode_adjust(info.st_mode, mode_change);
1039 if (chmod(path, new_mode))
1041 send_error();
1042 return;
1045 g_string_sprintf(message, "s%s", path);
1046 send();
1048 if (S_ISDIR(info.st_mode))
1050 g_string_sprintf(message, "m%s", path);
1051 send();
1053 if (o_recurse)
1055 guchar *safe_path;
1056 safe_path = g_strdup(path);
1057 for_dir_contents(do_chmod, safe_path, safe_path);
1058 g_free(safe_path);
1063 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1064 * is set then that is the new leafname, otherwise the leafname stays
1065 * the same.
1067 static char *make_dest_path(const char *object, const char *dir)
1069 const char *leaf;
1071 if (action_leaf)
1072 leaf = action_leaf;
1073 else
1075 leaf = strrchr(object, '/');
1076 if (!leaf)
1077 leaf = object; /* Error? */
1078 else
1079 leaf++;
1082 return make_path(dir, leaf)->str;
1085 /* If action_leaf is not NULL it specifies the new leaf name */
1086 static void do_copy2(const char *path, const char *dest)
1088 char *dest_path;
1089 struct stat info;
1090 struct stat dest_info;
1092 check_flags();
1094 dest_path = make_dest_path(path, dest);
1096 if (mc_lstat(path, &info))
1098 send_error();
1099 return;
1102 if (mc_lstat(dest_path, &dest_info) == 0)
1104 int err;
1105 gboolean merge;
1107 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1109 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1111 /* Newer; keep going */
1113 else
1115 g_string_sprintf(message,
1116 _("?'%s' already exists - %s?"), dest_path,
1117 merge ? _("merge contents") : _("overwrite"));
1119 if (!reply(from_parent, TRUE))
1120 return;
1123 if (!merge)
1125 if (S_ISDIR(dest_info.st_mode))
1126 err = rmdir(dest_path);
1127 else
1128 err = unlink(dest_path);
1130 if (err)
1132 send_error();
1133 if (errno != ENOENT)
1134 return;
1135 g_string_sprintf(message,
1136 _("'Trying copy anyway...\n"));
1137 send();
1141 else if (!quiet)
1143 g_string_sprintf(message,
1144 _("?Copy %s as %s?"), path, dest_path);
1145 if (!reply(from_parent, FALSE))
1146 return;
1148 else
1150 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1151 dest_path);
1152 send();
1155 if (S_ISDIR(info.st_mode))
1157 mode_t mode = info.st_mode;
1158 char *safe_path, *safe_dest;
1159 struct stat dest_info;
1160 gboolean exists;
1162 safe_path = g_strdup(path);
1163 safe_dest = g_strdup(dest_path);
1165 exists = !mc_lstat(dest_path, &dest_info);
1167 if (exists && !S_ISDIR(dest_info.st_mode))
1169 g_string_sprintf(message,
1170 _("!ERROR: Destination already exists, "
1171 "but is not a directory\n"));
1172 send();
1174 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1175 send_error();
1176 else
1178 if (!exists)
1180 /* (just been created then) */
1181 g_string_sprintf(message, "s%s", dest_path);
1182 send();
1185 action_leaf = NULL;
1186 for_dir_contents(do_copy2, safe_path, safe_dest);
1187 /* Note: dest_path now invalid... */
1189 if (!exists)
1191 struct utimbuf utb;
1193 /* We may have created the directory with
1194 * more permissions than the source so that
1195 * we could write to it... change it back now.
1197 if (chmod(safe_dest, mode))
1199 /* Some filesystems don't support
1200 * SetGID and SetUID bits. Ignore
1201 * these errors.
1203 if (errno != EPERM)
1204 send_error();
1207 /* Also, try to preserve the timestamps */
1208 utb.actime = info.st_atime;
1209 utb.modtime = info.st_mtime;
1211 utime(safe_dest, &utb);
1215 g_free(safe_path);
1216 g_free(safe_dest);
1218 else if (S_ISLNK(info.st_mode))
1220 char *target;
1222 /* Not all versions of cp(1) can make symlinks,
1223 * so we special-case it.
1226 target = readlink_dup(path);
1227 if (target)
1229 if (symlink(target, dest_path))
1230 send_error();
1231 else
1233 g_string_sprintf(message, "s%s", dest_path);
1234 send();
1237 g_free(target);
1239 else
1240 send_error();
1242 else
1244 guchar *error;
1246 error = copy_file(path, dest_path);
1248 if (error)
1250 g_string_sprintf(message, _("!%s\nFailed to copy '%s'"),
1251 error, path);
1252 g_free(error);
1253 send();
1255 else
1257 g_string_sprintf(message, "s%s", dest_path);
1258 send();
1263 /* If action_leaf is not NULL it specifies the new leaf name */
1264 static void do_move2(const char *path, const char *dest)
1266 char *dest_path;
1267 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1268 struct stat info2;
1269 gboolean is_dir;
1270 char *err;
1272 check_flags();
1274 dest_path = make_dest_path(path, dest);
1276 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1278 if (access(dest_path, F_OK) == 0)
1280 struct stat info;
1281 int err;
1283 if (mc_lstat(dest_path, &info))
1285 send_error();
1286 return;
1289 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1291 /* Newer; keep going */
1293 else
1295 g_string_sprintf(message,
1296 _("?'%s' already exists - overwrite?"),
1297 dest_path);
1298 if (!reply(from_parent, TRUE))
1299 return;
1302 if (S_ISDIR(info.st_mode))
1303 err = rmdir(dest_path);
1304 else
1305 err = unlink(dest_path);
1307 if (err)
1309 send_error();
1310 if (errno != ENOENT)
1311 return;
1312 g_string_sprintf(message,
1313 _("'Trying move anyway...\n"));
1314 send();
1317 else if (!quiet)
1319 g_string_sprintf(message,
1320 _("?Move %s as %s?"), path, dest_path);
1321 if (!reply(from_parent, FALSE))
1322 return;
1324 else
1326 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1327 dest_path);
1328 send();
1331 argv[2] = path;
1332 argv[3] = dest_path;
1334 err = fork_exec_wait(argv);
1335 if (err)
1337 g_string_sprintf(message,
1338 _("!%s\nFailed to move %s as %s\n"),
1339 err, path, dest_path);
1340 send();
1342 g_free(err);
1344 else
1346 g_string_sprintf(message, "s%s", dest_path);
1347 send();
1349 if (is_dir)
1350 g_string_sprintf(message, "m%s", path);
1351 else
1352 g_string_sprintf(message, "s%s", path);
1354 send();
1358 /* Copy path to dest.
1359 * Check that path not copied into itself.
1361 static void do_copy(const char *path, const char *dest)
1363 if (is_sub_dir(make_dest_path(path, dest), path))
1365 g_string_sprintf(message,
1366 _("!ERROR: Can't copy object into itself\n"));
1367 send();
1369 else
1370 do_copy2(path, dest);
1373 /* Move path to dest.
1374 * Check that path not moved into itself.
1376 static void do_move(const char *path, const char *dest)
1378 if (is_sub_dir(make_dest_path(path, dest), path))
1380 g_string_sprintf(message,
1381 _("!ERROR: Can't move/rename object into itself\n"));
1382 send();
1384 else
1385 do_move2(path, dest);
1388 static void do_link(const char *path, const char *dest)
1390 char *dest_path;
1392 check_flags();
1394 dest_path = make_dest_path(path, dest);
1396 if (quiet)
1398 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1399 dest_path);
1400 send();
1402 else
1404 g_string_sprintf(message,
1405 _("?Link %s as %s?"), path, dest_path);
1406 if (!reply(from_parent, FALSE))
1407 return;
1410 if (symlink(path, dest_path))
1411 send_error();
1412 else
1414 g_string_sprintf(message, "s%s", dest_path);
1415 send();
1419 /* Mount/umount this item (depending on 'mount') */
1420 static void do_mount(guchar *path, gboolean mount)
1422 const char *argv[3] = {NULL, NULL, NULL};
1423 char *err;
1425 check_flags();
1427 argv[0] = mount ? "mount" : "umount";
1428 argv[1] = path;
1430 if (quiet)
1432 g_string_sprintf(message,
1433 mount ? _("'Mounting %s\n")
1434 : _("'Unmounting %s\n"),
1435 path);
1436 send();
1438 else
1440 g_string_sprintf(message,
1441 mount ? _("?Mount %s?")
1442 : _("?Unmount %s?"),
1443 path);
1444 if (!reply(from_parent, FALSE))
1445 return;
1448 err = fork_exec_wait(argv);
1449 if (err)
1451 g_string_sprintf(message, mount ?
1452 _("!%s\nMount failed\n") :
1453 _("!%s\nUnmount failed\n"), err);
1454 send();
1455 g_free(err);
1457 else
1459 g_string_sprintf(message, "M%s", path);
1460 send();
1461 if (mount && mount_open_dir)
1463 g_string_sprintf(message, "o%s", path);
1464 send();
1469 /* CHILD MAIN LOOPS */
1471 /* After forking, the child calls one of these functions */
1473 /* We use a double for total size in order to count beyond 4Gb */
1474 static void usage_cb(gpointer data)
1476 GList *paths = (GList *) data;
1477 double total_size = 0;
1478 gchar *tmp;
1480 dir_counter = file_counter = 0;
1482 for (; paths; paths = paths->next)
1484 guchar *path = (guchar *) paths->data;
1486 send_dir(path);
1488 size_tally = 0;
1490 do_usage(path, NULL);
1492 g_string_sprintf(message, "'%s: %s\n",
1493 g_basename(path),
1494 format_double_size(size_tally));
1495 send();
1496 total_size += size_tally;
1499 g_string_sprintf(message, _("'\nTotal: %s ("),
1500 format_double_size(total_size));
1502 if (file_counter)
1504 tmp = g_strdup_printf("%ld %s%s",
1505 file_counter,
1506 file_counter == 1 ? _("file") : _("files"),
1507 dir_counter ? ", " : ")\n");
1508 g_string_append(message, tmp);
1509 g_free(tmp);
1512 if (file_counter == 0 && dir_counter == 0)
1513 g_string_append(message, _("no directories)\n"));
1514 else if (dir_counter)
1516 tmp = g_strdup_printf("%ld %s)\n",
1517 dir_counter,
1518 dir_counter == 1 ? _("directory")
1519 : _("directories"));
1520 g_string_append(message, tmp);
1521 g_free(tmp);
1524 send();
1527 #ifdef DO_MOUNT_POINTS
1528 static void mount_cb(gpointer data)
1530 GList *paths = (GList *) data;
1531 gboolean mount_points = FALSE;
1533 for (; paths; paths = paths->next)
1535 guchar *path = (guchar *) paths->data;
1537 if (mount_is_mounted(path, NULL, NULL))
1538 do_mount(path, FALSE); /* Unmount */
1539 else if (g_hash_table_lookup(fstab_mounts, path))
1540 do_mount(path, TRUE); /* Mount */
1541 else
1542 continue;
1544 mount_points = TRUE;
1547 g_string_sprintf(message,
1548 mount_points ? _("'\nDone\n")
1549 : _("!No mount points selected!\n"));
1550 send();
1552 #endif
1554 /* (use g_dirname() instead?) */
1555 static guchar *dirname(guchar *path)
1557 guchar *slash;
1559 slash = strrchr(path, '/');
1560 g_return_val_if_fail(slash != NULL, g_strdup(path));
1562 if (slash != path)
1563 return g_strndup(path, slash - path);
1564 return g_strdup("/");
1567 static void delete_cb(gpointer data)
1569 GList *paths = (GList *) data;
1571 while (paths)
1573 guchar *path = (guchar *) paths->data;
1574 guchar *dir;
1576 dir = dirname(path);
1577 send_dir(dir);
1579 do_delete(path, dir);
1581 g_free(dir);
1582 paths = paths->next;
1585 g_string_sprintf(message, _("'\nDone\n"));
1586 send();
1589 static void find_cb(gpointer data)
1591 GList *all_paths = (GList *) data;
1592 GList *paths;
1594 while (1)
1596 for (paths = all_paths; paths; paths = paths->next)
1598 guchar *path = (guchar *) paths->data;
1600 send_dir(path);
1602 do_find(path, NULL);
1605 g_string_assign(message, _("?Another search?"));
1606 if (!reply(from_parent, TRUE))
1607 break;
1608 g_string_assign(message, "#");
1609 send();
1612 g_string_sprintf(message, _("'\nDone\n"));
1613 send();
1616 static void chmod_cb(gpointer data)
1618 GList *paths = (GList *) data;
1620 for (; paths; paths = paths->next)
1622 guchar *path = (guchar *) paths->data;
1623 struct stat info;
1625 send_dir(path);
1627 if (mc_stat(path, &info) != 0)
1628 send_error();
1629 else if (S_ISLNK(info.st_mode))
1631 g_string_sprintf(message,
1632 _("!'%s' is a symbolic link\n"),
1633 g_basename(path));
1634 send();
1636 else
1637 do_chmod(path, NULL);
1640 g_string_sprintf(message, _("'\nDone\n"));
1641 send();
1644 static void list_cb(gpointer data)
1646 GList *paths = (GList *) data;
1648 while (paths)
1650 send_dir((char *) paths->data);
1652 action_do_func((char *) paths->data, action_dest);
1654 paths = paths->next;
1657 g_string_sprintf(message, _("'\nDone\n"));
1658 send();
1661 /* EXTERNAL INTERFACE */
1663 void action_find(GList *paths)
1665 GUIside *gui_side;
1666 GtkWidget *abox;
1668 if (!paths)
1670 report_error(_("You need to select some items "
1671 "to search through"));
1672 return;
1675 if (!last_find_string)
1676 last_find_string = g_strdup("'core'");
1678 new_entry_string = last_find_string;
1680 abox = abox_new(_("Find"), FALSE);
1681 gui_side = start_action(abox, find_cb, paths,
1682 o_action_force.int_value,
1683 o_action_brief.int_value,
1684 o_action_recurse.int_value,
1685 o_action_newer.int_value);
1686 if (!gui_side)
1687 return;
1689 abox_add_results(ABOX(abox));
1691 gui_side->default_string = &last_find_string;
1692 abox_add_entry(ABOX(abox), last_find_string,
1693 new_help_button(show_condition_help, NULL));
1694 g_signal_connect(ABOX(abox)->entry, "changed",
1695 G_CALLBACK(entry_changed), gui_side);
1696 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1698 gui_side->show_info = TRUE;
1699 gui_side->entry_string_func = set_find_string_colour;
1701 number_of_windows++;
1702 gtk_widget_show_all(abox);
1705 /* Count disk space used by selected items */
1706 void action_usage(GList *paths)
1708 GUIside *gui_side;
1709 GtkWidget *abox;
1711 if (!paths)
1713 report_error(_("You need to select some items to count"));
1714 return;
1717 abox = abox_new(_("Disk Usage"), TRUE);
1719 gui_side = start_action(abox, usage_cb, paths,
1720 o_action_force.int_value,
1721 o_action_brief.int_value,
1722 o_action_recurse.int_value,
1723 o_action_newer.int_value);
1724 if (!gui_side)
1725 return;
1727 gui_side->show_info = TRUE;
1729 number_of_windows++;
1731 gtk_widget_show(abox);
1734 /* Mount/unmount listed items (paths).
1735 * Free the list after this function returns.
1736 * If open_dir is TRUE and the dir is successfully mounted, open it.
1737 * quiet can be -1 for default.
1739 void action_mount(GList *paths, gboolean open_dir, int quiet)
1741 #ifdef DO_MOUNT_POINTS
1742 GUIside *gui_side;
1743 GtkWidget *abox;
1745 if (quiet == -1)
1746 quiet = o_action_mount.int_value;
1748 mount_open_dir = open_dir;
1750 abox = abox_new(_("Mount / Unmount"), quiet);
1751 gui_side = start_action(abox, mount_cb, paths,
1752 o_action_force.int_value,
1753 o_action_brief.int_value,
1754 o_action_recurse.int_value,
1755 o_action_newer.int_value);
1756 if (!gui_side)
1757 return;
1759 number_of_windows++;
1760 gtk_widget_show(abox);
1761 #else
1762 report_error(
1763 _("ROX-Filer does not yet support mount points on your "
1764 "system. Sorry."));
1765 #endif /* DO_MOUNT_POINTS */
1768 /* Deletes all selected items in the window */
1769 void action_delete(GList *paths)
1771 GUIside *gui_side;
1772 GtkWidget *abox;
1774 if (!remove_pinned_ok(paths))
1775 return;
1777 abox = abox_new(_("Delete"), o_action_delete.int_value);
1778 gui_side = start_action(abox, delete_cb, paths,
1779 o_action_force.int_value,
1780 o_action_brief.int_value,
1781 o_action_recurse.int_value,
1782 o_action_newer.int_value);
1783 if (!gui_side)
1784 return;
1786 abox_add_flag(ABOX(abox),
1787 _("Force"), _("Don't confirm deletion of non-writeable items"),
1788 'F', o_action_force.int_value);
1789 abox_add_flag(ABOX(abox),
1790 _("Brief"), _("Only log directories being deleted"),
1791 'B', o_action_brief.int_value);
1793 number_of_windows++;
1794 gtk_widget_show(abox);
1797 /* Change the permissions of the selected items */
1798 void action_chmod(GList *paths)
1800 GtkWidget *abox;
1801 GUIside *gui_side;
1802 static GList *presets = NULL;
1804 if (!paths)
1806 report_error(_("You need to select the items "
1807 "whose permissions you want to change"));
1808 return;
1811 if (!presets)
1813 presets = g_list_append(presets, (gchar *)
1814 _("a+x (Make executable/searchable)"));
1815 presets = g_list_append(presets, (gchar *)
1816 _("a-x (Make non-executable/non-searchable)"));
1817 presets = g_list_append(presets, (gchar *)
1818 _("u+rw (Give owner read+write)"));
1819 presets = g_list_append(presets, (gchar *)
1820 _("go-rwx (Private - owner access only)"));
1821 presets = g_list_append(presets, (gchar *)
1822 _("go=u-w (Public access, not write)"));
1825 if (!last_chmod_string)
1826 last_chmod_string = g_strdup((guchar *) presets->data);
1827 new_entry_string = last_chmod_string;
1829 abox = abox_new(_("Permissions"), FALSE);
1830 gui_side = start_action(abox, chmod_cb, paths,
1831 o_action_force.int_value,
1832 o_action_brief.int_value,
1833 o_action_recurse.int_value,
1834 o_action_newer.int_value);
1835 if (!gui_side)
1836 return;
1838 abox_add_flag(ABOX(abox),
1839 _("Brief"), _("Don't list processed files"),
1840 'B', o_action_brief.int_value);
1841 abox_add_flag(ABOX(abox),
1842 _("Recurse"), _("Also change contents of subdirectories"),
1843 'R', o_action_recurse.int_value);
1845 gui_side->default_string = &last_chmod_string;
1846 abox_add_combo(ABOX(abox), presets, last_chmod_string,
1847 new_help_button(show_chmod_help, NULL));
1849 g_signal_connect(ABOX(abox)->entry, "changed",
1850 G_CALLBACK(entry_changed), gui_side);
1851 #if 0
1852 g_signal_connect_swapped(gui_side->entry, "activate",
1853 G_CALLBACK(gtk_button_clicked),
1854 gui_side->yes);
1855 #endif
1857 number_of_windows++;
1858 gtk_widget_show(abox);
1861 /* If leaf is NULL then the copy has the same name as the original.
1862 * quiet can be -1 for default.
1864 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
1866 GUIside *gui_side;
1867 GtkWidget *abox;
1869 if (quiet == -1)
1870 quiet = o_action_copy.int_value;
1872 action_dest = dest;
1873 action_leaf = leaf;
1874 action_do_func = do_copy;
1876 abox = abox_new(_("Copy"), quiet);
1877 gui_side = start_action(abox, list_cb, paths,
1878 o_action_force.int_value,
1879 o_action_brief.int_value,
1880 o_action_recurse.int_value,
1881 o_action_newer.int_value);
1882 if (!gui_side)
1883 return;
1885 abox_add_flag(ABOX(abox),
1886 _("Newer"),
1887 _("Only over-write if source is newer than destination."),
1888 'W', o_action_newer.int_value);
1890 number_of_windows++;
1891 gtk_widget_show(abox);
1894 /* If leaf is NULL then the file is not renamed.
1895 * quiet can be -1 for default.
1897 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
1899 GUIside *gui_side;
1900 GtkWidget *abox;
1902 if (quiet == -1)
1903 quiet = o_action_move.int_value;
1905 action_dest = dest;
1906 action_leaf = leaf;
1907 action_do_func = do_move;
1909 abox = abox_new(_("Move"), quiet);
1910 gui_side = start_action(abox, list_cb, paths,
1911 o_action_force.int_value,
1912 o_action_brief.int_value,
1913 o_action_recurse.int_value,
1914 o_action_newer.int_value);
1915 if (!gui_side)
1916 return;
1918 abox_add_flag(ABOX(abox),
1919 _("Newer"),
1920 _("Only over-write if source is newer than destination."),
1921 'W', o_action_newer.int_value);
1922 number_of_windows++;
1923 gtk_widget_show(abox);
1926 /* If leaf is NULL then the link will have the same name */
1927 /* XXX: No quiet option here? */
1928 void action_link(GList *paths, const char *dest, const char *leaf)
1930 GtkWidget *abox;
1931 GUIside *gui_side;
1933 action_dest = dest;
1934 action_leaf = leaf;
1935 action_do_func = do_link;
1937 abox = abox_new(_("Link"), o_action_link.int_value);
1938 gui_side = start_action(abox, list_cb, paths,
1939 o_action_force.int_value,
1940 o_action_brief.int_value,
1941 o_action_recurse.int_value,
1942 o_action_newer.int_value);
1943 if (!gui_side)
1944 return;
1946 number_of_windows++;
1947 gtk_widget_show(abox);
1950 void action_init(void)
1952 option_add_int(&o_action_copy, "action_copy", 1);
1953 option_add_int(&o_action_move, "action_move", 1);
1954 option_add_int(&o_action_link, "action_link", 1);
1955 option_add_int(&o_action_delete, "action_delete", 0);
1956 option_add_int(&o_action_mount, "action_mount", 1);
1957 option_add_int(&o_action_force, "action_force", FALSE);
1958 option_add_int(&o_action_brief, "action_brief", FALSE);
1959 option_add_int(&o_action_recurse, "action_recurse", FALSE);
1960 option_add_int(&o_action_newer, "action_newer", FALSE);
1963 #define MAX_ASK 4
1965 /* Check to see if any of the selected items (or their children) are
1966 * on the pinboard or panel. If so, ask for confirmation.
1968 * TRUE if it's OK to lose them.
1970 static gboolean remove_pinned_ok(GList *paths)
1972 GList *ask = NULL, *next;
1973 GString *message;
1974 int i, ask_n = 0;
1975 gboolean retval;
1977 while (paths)
1979 guchar *path = (guchar *) paths->data;
1981 if (icons_require(path))
1983 if (++ask_n > MAX_ASK)
1984 break;
1985 ask = g_list_append(ask, path);
1988 paths = paths->next;
1991 if (!ask)
1992 return TRUE;
1994 if (ask_n > MAX_ASK)
1996 message = g_string_new(_("Deleting items such as "));
1997 ask_n--;
1999 else if (ask_n == 1)
2000 message = g_string_new(_("Deleting the item "));
2001 else
2002 message = g_string_new(_("Deleting the items "));
2004 i = 0;
2005 for (next = ask; next; next = next->next)
2007 guchar *path = (guchar *) next->data;
2008 guchar *leaf;
2010 leaf = strrchr(path, '/');
2011 if (leaf)
2012 leaf++;
2013 else
2014 leaf = path;
2016 g_string_append_c(message, '`');
2017 g_string_append(message, leaf);
2018 g_string_append_c(message, '\'');
2019 i++;
2020 if (i == ask_n - 1 && i > 0)
2021 g_string_append(message, _(" and "));
2022 else if (i < ask_n)
2023 g_string_append(message, ", ");
2026 g_list_free(ask);
2028 if (ask_n == 1)
2029 message = g_string_append(message,
2030 _(" will affect some items on the pinboard "
2031 "or panel - really delete it?"));
2032 else
2034 if (ask_n > MAX_ASK)
2035 message = g_string_append_c(message, ',');
2036 message = g_string_append(message,
2037 _(" will affect some items on the pinboard "
2038 "or panel - really delete them?"));
2041 retval = get_choice(PROJECT, message->str,
2042 2, _("Cancel"), _("Delete")) == 1;
2044 g_string_free(message, TRUE);
2046 return retval;
2049 void set_find_string_colour(GtkWidget *widget, const guchar *string)
2051 FindCondition *cond;
2053 cond = find_compile(string);
2054 entry_set_error(widget, !cond);
2056 if (cond)
2057 find_condition_free(cond);