r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / action.c
blob5a29dd5ace74aef3dbf677580fd65277ed7abd1e
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>
36 #include <stdarg.h>
38 #include "global.h"
40 #include "action.h"
41 #include "abox.h"
42 #include "string.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "main.h"
48 #include "options.h"
49 #include "modechange.h"
50 #include "find.h"
51 #include "dir.h"
52 #include "icon.h"
53 #include "mount.h"
55 /* Parent->Child messages are one character each:
57 * Y/N Yes/No button clicked
58 * F Force deletion of non-writeable items
59 * Q Quiet toggled
60 * E Entry text changed
61 * W neWer toggled
64 typedef struct _GUIside GUIside;
65 typedef void ActionChild(gpointer data);
66 typedef void ForDirCB(const char *path, const char *dest_path);
68 struct _GUIside
70 ABox *abox; /* The action window widget */
72 int from_child; /* File descriptor */
73 FILE *to_child;
74 int input_tag; /* gdk_input_add() */
75 pid_t child; /* Process ID */
76 int errors; /* Number of errors so far */
77 gboolean show_info; /* For Disk Usage */
79 guchar **default_string; /* Changed when the entry changes */
80 void (*entry_string_func)(GtkWidget *widget,
81 const guchar *string);
84 /* These don't need to be in a structure because we fork() before
85 * using them again.
87 static gboolean mount_open_dir = FALSE;
88 static int from_parent = 0;
89 static FILE *to_parent = NULL;
90 static gboolean quiet = FALSE;
91 static GString *message = NULL;
92 static const char *action_dest = NULL;
93 static const char *action_leaf = NULL;
94 static void (*action_do_func)(const char *source, const char *dest);
95 static double size_tally; /* For Disk Usage */
96 static unsigned long dir_counter; /* For Disk Usage */
97 static unsigned long file_counter; /* For Disk Usage */
99 static struct mode_change *mode_change = NULL; /* For Permissions */
100 static FindCondition *find_condition = NULL; /* For Find */
102 /* Only used by child */
103 static gboolean o_force = FALSE;
104 static gboolean o_brief = FALSE;
105 static gboolean o_recurse = FALSE;
106 static gboolean o_newer = FALSE;
108 static Option o_action_copy, o_action_move, o_action_link;
109 static Option o_action_delete, o_action_mount;
110 static Option o_action_force, o_action_brief, o_action_recurse;
111 static Option o_action_newer;
113 /* Whenever the text in these boxes is changed we store a copy of the new
114 * string to be used as the default next time.
116 static guchar *last_chmod_string = NULL;
117 static guchar *last_find_string = NULL;
119 /* Set to one of the above before forking. This may change over a call to
120 * reply(). It is reset to NULL once the text is parsed.
122 static guchar *new_entry_string = NULL;
124 /* Static prototypes */
125 static void send_done(void);
126 static void send_check_path(const gchar *path);
127 static void send_mount_path(const gchar *path);
128 static gboolean printf_send(const char *msg, ...);
129 static gboolean send(void);
130 static gboolean send_error(void);
131 static gboolean send_dir(const char *dir);
132 static gboolean read_exact(int source, char *buffer, ssize_t len);
133 static void do_mount(guchar *path, gboolean mount);
134 static gboolean printf_reply(int fd, gboolean ignore_quiet,
135 const char *msg, ...);
136 static gboolean remove_pinned_ok(GList *paths);
138 /* SUPPORT */
141 /* This is called whenever the user edits the entry box (if any) - send the
142 * new string.
144 static void entry_changed(GtkEditable *entry, GUIside *gui_side)
146 guchar *text;
148 g_return_if_fail(gui_side->default_string != NULL);
150 text = gtk_editable_get_chars(entry, 0, -1);
152 if (gui_side->entry_string_func)
153 gui_side->entry_string_func(GTK_WIDGET(entry), text);
155 g_free(*(gui_side->default_string));
156 *(gui_side->default_string) = text; /* Gets text's ref */
158 if (!gui_side->to_child)
159 return;
161 fputc('E', gui_side->to_child);
162 fputs(text, gui_side->to_child);
163 fputc('\n', gui_side->to_child);
164 fflush(gui_side->to_child);
167 void show_condition_help(gpointer data)
169 static GtkWidget *help = NULL;
171 if (!help)
173 GtkWidget *text, *vbox, *button, *hbox, *frame;
175 help = gtk_window_new(GTK_WINDOW_TOPLEVEL);
176 gtk_window_set_type_hint(GTK_WINDOW(help),
177 GDK_WINDOW_TYPE_HINT_DIALOG);
178 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
179 gtk_window_set_title(GTK_WINDOW(help),
180 _("Find expression reference"));
182 vbox = gtk_vbox_new(FALSE, 0);
183 gtk_container_add(GTK_CONTAINER(help), vbox);
185 frame = gtk_frame_new(_("Quick Start"));
186 text = gtk_label_new(
187 _("Just put the name of the file you're looking for in single quotes:\n"
188 "'index.html' (to find a file called 'index.html')"));
189 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
190 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
191 gtk_container_add(GTK_CONTAINER(frame), text);
192 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
194 frame = gtk_frame_new(_("Examples"));
195 text = gtk_label_new(
196 _("'*.htm', '*.html' (finds HTML files)\n"
197 "IsDir 'lib' (finds directories called 'lib')\n"
198 "IsReg 'core' (finds a regular file called 'core')\n"
199 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
200 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
201 "'CVS' prune, isreg (a regular file not in CVS)\n"
202 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
203 gtk_widget_set_name(text, "fixed-style");
204 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
205 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
206 gtk_container_add(GTK_CONTAINER(frame), text);
207 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
209 frame = gtk_frame_new(_("Simple Tests"));
210 text = gtk_label_new(
211 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor "
212 "(types)\n"
213 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
214 "\n"
215 "IsEmpty, IsMine\n"
216 "\n"
217 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
218 "contains a slash then the match is against the full path; otherwise it is \n"
219 "against the leafname only."));
220 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
221 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
222 gtk_container_add(GTK_CONTAINER(frame), text);
223 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
225 frame = gtk_frame_new(_("Comparisons"));
226 text = gtk_label_new(
227 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
228 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
229 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
230 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
231 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
232 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
233 gtk_container_add(GTK_CONTAINER(frame), text);
234 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
236 frame = gtk_frame_new(_("Specials"));
237 text = gtk_label_new(
238 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
239 "in 'command' is replaced with the path of the current file)\n"
240 "prune (false, and prevents searching the contents of a directory).")
242 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
243 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
244 gtk_container_add(GTK_CONTAINER(frame), text);
245 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
247 hbox = gtk_hbox_new(FALSE, 20);
248 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
250 text = gtk_label_new(
251 _("See the ROX-Filer manual for full details."));
252 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
253 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
254 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
255 g_signal_connect_swapped(button, "clicked",
256 G_CALLBACK(gtk_widget_hide), help);
258 g_signal_connect_swapped(help, "delete_event",
259 G_CALLBACK(gtk_widget_hide), help);
262 if (GTK_WIDGET_VISIBLE(help))
263 gtk_widget_hide(help);
264 gtk_widget_show_all(help);
267 static void show_chmod_help(gpointer data)
269 static GtkWidget *help = NULL;
271 if (!help)
273 GtkWidget *text, *vbox, *button, *hbox, *sep;
275 help = gtk_window_new(GTK_WINDOW_TOPLEVEL);
276 gtk_window_set_type_hint(GTK_WINDOW(help),
277 GDK_WINDOW_TYPE_HINT_DIALOG);
278 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
279 gtk_window_set_title(GTK_WINDOW(help),
280 _("Permissions command reference"));
282 vbox = gtk_vbox_new(FALSE, 0);
283 gtk_container_add(GTK_CONTAINER(help), vbox);
285 text = gtk_label_new(
286 _("Normally, you can just select a command from the menu (click \n"
287 "on the arrow beside the command box). Sometimes, you need more...\n"
288 "\n"
289 "The format of a command is:\n"
290 "CHANGE, CHANGE, ...\n"
291 "Each CHANGE is:\n"
292 "WHO HOW PERMISSIONS\n"
293 "WHO is some combination of u, g and o which determines whether to\n"
294 "change the permissions for the User (owner), Group or Others.\n"
295 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
296 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
298 "Bracketed text and spaces are ignored.\n\n"
300 "Examples:\n"
301 "u+rw (the file owner gains read and write permission)\n"
302 "g=u (the group permissions are set to be the same as the user's)\n"
303 "o=u-w (others get the same permissions as the owner, but without "
304 "write permission)\n"
305 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
306 "a+X (directories become accessable by everyone; files which were\n"
307 "executable by anyone become executable by everyone)\n"
308 "u+rw, go+r (two commands at once!)\n"
309 "u+s (set the SetUID bit - often has no effect on script files)\n"
310 "755 (set the permissions directly)\n"
312 "\nSee the chmod(1) man page for full details."));
313 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
314 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
316 hbox = gtk_hbox_new(FALSE, 20);
317 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
319 sep = gtk_hseparator_new();
320 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
321 button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
322 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
323 g_signal_connect_swapped(button, "clicked",
324 G_CALLBACK(gtk_widget_hide), help);
326 g_signal_connect_swapped(help, "delete_event",
327 G_CALLBACK(gtk_widget_hide), help);
330 if (GTK_WIDGET_VISIBLE(help))
331 gtk_widget_hide(help);
332 gtk_widget_show_all(help);
335 static void process_message(GUIside *gui_side, const gchar *buffer)
337 ABox *abox = gui_side->abox;
339 if (*buffer == '?')
340 abox_ask(abox, buffer + 1);
341 else if (*buffer == 's')
342 dir_check_this(buffer + 1); /* Update this item */
343 else if (*buffer == '=')
344 abox_add_filename(abox, buffer + 1);
345 else if (*buffer == '#')
346 abox_clear_results(abox);
347 else if (*buffer == 'm' || *buffer == 'M')
349 /* Mount / major changes to this path */
350 if (*buffer == 'M')
351 mount_update(TRUE);
352 filer_check_mounted(buffer + 1);
354 else if (*buffer == '/')
355 abox_set_current_object(abox, buffer + 1);
356 else if (*buffer == 'o')
357 filer_opendir(buffer + 1, NULL, NULL);
358 else if (*buffer == '!')
360 gui_side->errors++;
361 abox_log(abox, buffer + 1, "error");
363 else
364 abox_log(abox, buffer + 1, NULL);
367 /* Called when the child sends us a message */
368 static void message_from_child(gpointer data,
369 gint source,
370 GdkInputCondition condition)
372 char buf[5];
373 GUIside *gui_side = (GUIside *) data;
374 ABox *abox = gui_side->abox;
375 GtkTextBuffer *text_buffer;
377 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
379 if (read_exact(source, buf, 4))
381 ssize_t message_len;
382 char *buffer;
384 buf[4] = '\0';
385 message_len = strtol(buf, NULL, 16);
386 buffer = g_malloc(message_len + 1);
387 if (message_len > 0 && read_exact(source, buffer, message_len))
389 buffer[message_len] = '\0';
390 process_message(gui_side, buffer);
391 g_free(buffer);
392 return;
394 g_printerr("Child died in the middle of a message.\n");
397 /* The child is dead */
398 gui_side->child = 0;
400 fclose(gui_side->to_child);
401 gui_side->to_child = NULL;
402 close(gui_side->from_child);
403 g_source_remove(gui_side->input_tag);
404 abox_cancel_ask(gui_side->abox);
406 if (gui_side->errors)
408 guchar *report;
410 if (gui_side->errors == 1)
411 report = g_strdup(_("There was one error.\n"));
412 else
413 report = g_strdup_printf(_("There were %d errors.\n"),
414 gui_side->errors);
416 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
418 g_free(report);
420 else if (gui_side->show_info == FALSE)
421 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
424 /* Scans src_dir, calling cb(item, dest_path) for each item */
425 static void for_dir_contents(ForDirCB *cb,
426 const char *src_dir,
427 const char *dest_path)
429 DIR *d;
430 struct dirent *ent;
431 GList *list = NULL, *next;
433 d = mc_opendir(src_dir);
434 if (!d)
436 /* Message displayed is "ERROR reading 'path': message" */
437 printf_send("!%s '%s': %s\n", _("ERROR reading"),
438 src_dir, g_strerror(errno));
439 return;
442 send_dir(src_dir);
444 while ((ent = mc_readdir(d)))
446 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
447 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
448 continue;
449 list = g_list_append(list, g_strdup(make_path(src_dir,
450 ent->d_name)->str));
452 mc_closedir(d);
454 if (!list)
455 return;
457 for (next = list; next; next = next->next)
459 cb((char *) next->data, dest_path);
461 g_free(next->data);
463 g_list_free(list);
466 /* Read this many bytes into the buffer. TRUE on success. */
467 static gboolean read_exact(int source, char *buffer, ssize_t len)
469 while (len > 0)
471 ssize_t got;
472 got = read(source, buffer, len);
473 if (got < 1)
474 return FALSE;
475 len -= got;
476 buffer += got;
478 return TRUE;
481 static void send_done(void)
483 printf_send(_("'\nDone\n"));
486 /* Notify the filer that this item has been updated */
487 static void send_check_path(const gchar *path)
489 printf_send("s%s", path);
492 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
493 static void send_mount_path(const gchar *path)
495 printf_send("m%s", path);
498 /* Send a message to the filer process. The first character indicates the
499 * type of the message.
501 static gboolean printf_send(const char *msg, ...)
503 va_list args;
504 gchar *tmp;
506 va_start(args, msg);
507 tmp = g_strdup_vprintf(msg, args);
508 va_end(args);
510 g_string_assign(message, tmp);
511 g_free(tmp);
513 return send();
516 /* Send 'message' to our parent process. TRUE on success. */
517 static gboolean send(void)
519 char len_buffer[5];
520 ssize_t len;
522 g_return_val_if_fail(message->len < 0xffff, FALSE);
524 sprintf(len_buffer, "%04x", message->len);
525 fwrite(len_buffer, 1, 4, to_parent);
526 len = fwrite(message->str, 1, message->len, to_parent);
527 fflush(to_parent);
528 return len == message->len;
531 /* Set the directory indicator at the top of the window */
532 static gboolean send_dir(const char *dir)
534 return printf_send("/%s", dir);
537 static gboolean send_error(void)
539 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
542 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
544 gchar code;
546 if (!gui_side->to_child)
547 return;
549 if (response == GTK_RESPONSE_YES)
550 code = 'Y';
551 else if (response == GTK_RESPONSE_NO)
552 code = 'N';
553 else
554 return;
556 fputc(code, gui_side->to_child);
557 fflush(gui_side->to_child);
560 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
562 if (!gui_side->to_child)
563 return;
565 fputc(flag, gui_side->to_child);
566 fflush(gui_side->to_child);
569 static void read_new_entry_text(void)
571 int len;
572 char c;
573 GString *new;
575 new = g_string_new(NULL);
577 for (;;)
579 len = read(from_parent, &c, 1);
580 if (len != 1)
582 fprintf(stderr, "read() error: %s\n",
583 g_strerror(errno));
584 _exit(1); /* Parent died? */
587 if (c == '\n')
588 break;
589 g_string_append_c(new, c);
592 g_free(new_entry_string);
593 new_entry_string = new->str;
594 g_string_free(new, FALSE);
597 static void process_flag(char flag)
599 switch (flag)
601 case 'Q':
602 quiet = !quiet;
603 break;
604 case 'F':
605 o_force = !o_force;
606 break;
607 case 'R':
608 o_recurse = !o_recurse;
609 break;
610 case 'B':
611 o_brief = !o_brief;
612 break;
613 case 'W':
614 o_newer = !o_newer;
615 break;
616 case 'E':
617 read_new_entry_text();
618 break;
619 default:
620 printf_send("!ERROR: Bad message '%c'\n", flag);
621 break;
625 /* If the parent has sent any flag toggles, read them */
626 static void check_flags(void)
628 fd_set set;
629 int got;
630 char retval;
631 struct timeval tv;
633 FD_ZERO(&set);
635 while (1)
637 FD_SET(from_parent, &set);
638 tv.tv_sec = 0;
639 tv.tv_usec = 0;
640 got = select(from_parent + 1, &set, NULL, NULL, &tv);
642 if (got == -1)
643 g_error("select() failed: %s\n", g_strerror(errno));
644 else if (!got)
645 return;
647 got = read(from_parent, &retval, 1);
648 if (got != 1)
649 g_error("read() error: %s\n", g_strerror(errno));
651 process_flag(retval);
655 /* Read until the user sends a reply. If ignore_quiet is TRUE then
656 * the user MUST click Yes or No, else treat quiet on as Yes.
657 * If the user needs prompting then does send().
659 static gboolean printf_reply(int fd, gboolean ignore_quiet,
660 const char *msg, ...)
662 ssize_t len;
663 char retval;
664 va_list args;
665 gchar *tmp;
667 if (quiet && !ignore_quiet)
668 return TRUE;
670 va_start(args, msg);
671 tmp = g_strdup_vprintf(msg, args);
672 va_end(args);
674 g_string_assign(message, tmp);
675 g_free(tmp);
677 send();
679 while (1)
681 len = read(fd, &retval, 1);
682 if (len != 1)
684 fprintf(stderr, "read() error: %s\n",
685 g_strerror(errno));
686 _exit(1); /* Parent died? */
689 switch (retval)
691 case 'Y':
692 printf_send("' %s\n", _("Yes"));
693 return TRUE;
694 case 'N':
695 printf_send("' %s\n", _("No"));
696 return FALSE;
697 default:
698 process_flag(retval);
699 break;
704 static void destroy_action_window(GtkWidget *widget, gpointer data)
706 GUIside *gui_side = (GUIside *) data;
708 if (gui_side->child)
710 kill(gui_side->child, SIGTERM);
711 fclose(gui_side->to_child);
712 close(gui_side->from_child);
713 g_source_remove(gui_side->input_tag);
716 g_free(gui_side);
718 one_less_window();
721 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
722 * (NULL on failure). The child calls func().
724 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
725 int force, int brief, int recurse, int newer)
727 gboolean autoq;
728 int filedes[4]; /* 0 and 2 are for reading */
729 GUIside *gui_side;
730 pid_t child;
731 struct sigaction act;
733 if (pipe(filedes))
735 report_error("pipe: %s", g_strerror(errno));
736 gtk_widget_destroy(abox);
737 return NULL;
740 if (pipe(filedes + 2))
742 close(filedes[0]);
743 close(filedes[1]);
744 report_error("pipe: %s", g_strerror(errno));
745 gtk_widget_destroy(abox);
746 return NULL;
749 autoq = gtk_toggle_button_get_active(
750 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
752 o_force = force;
753 o_brief = brief;
754 o_recurse = recurse;
755 o_newer = newer;
757 child = fork();
758 switch (child)
760 case -1:
761 report_error("fork: %s", g_strerror(errno));
762 gtk_widget_destroy(abox);
763 return NULL;
764 case 0:
765 /* We are the child */
767 quiet = autoq;
769 /* Reset the SIGCHLD handler */
770 act.sa_handler = SIG_DFL;
771 sigemptyset(&act.sa_mask);
772 act.sa_flags = 0;
773 sigaction(SIGCHLD, &act, NULL);
775 message = g_string_new(NULL);
776 close(filedes[0]);
777 close(filedes[3]);
778 to_parent = fdopen(filedes[1], "wb");
779 from_parent = filedes[2];
780 func(data);
781 send_dir("");
782 _exit(0);
785 /* We are the parent */
786 close(filedes[1]);
787 close(filedes[2]);
788 gui_side = g_malloc(sizeof(GUIside));
789 gui_side->from_child = filedes[0];
790 gui_side->to_child = fdopen(filedes[3], "wb");
791 gui_side->child = child;
792 gui_side->errors = 0;
793 gui_side->show_info = FALSE;
794 gui_side->default_string = NULL;
795 gui_side->entry_string_func = NULL;
797 gui_side->abox = ABOX(abox);
798 g_signal_connect(abox, "destroy",
799 G_CALLBACK(destroy_action_window), gui_side);
801 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
802 g_signal_connect(abox, "flag_toggled",
803 G_CALLBACK(flag_toggled), gui_side);
805 gui_side->input_tag = gtk_input_add_full(gui_side->from_child,
806 GDK_INPUT_READ,
807 message_from_child,
808 NULL, gui_side, NULL);
810 return gui_side;
813 /* ACTIONS ON ONE ITEM */
815 /* These may call themselves recursively, or ask questions, etc */
817 /* Updates the global size_tally, file_counter and dir_counter */
818 static void do_usage(const char *src_path, const char *unused)
820 struct stat info;
822 check_flags();
824 if (mc_lstat(src_path, &info))
826 printf_send("'%s:\n", src_path);
827 send_error();
829 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
831 file_counter++;
832 size_tally += info.st_size;
834 else if (S_ISDIR(info.st_mode))
836 dir_counter++;
837 if (printf_reply(from_parent, FALSE,
838 _("?Count contents of %s?"), src_path))
840 char *safe_path;
841 safe_path = g_strdup(src_path);
842 for_dir_contents(do_usage, safe_path, safe_path);
843 g_free(safe_path);
846 else
847 file_counter++;
850 /* dest_path is the dir containing src_path */
851 static void do_delete(const char *src_path, const char *unused)
853 struct stat info;
854 gboolean write_prot;
855 char *safe_path;
857 check_flags();
859 if (mc_lstat(src_path, &info))
861 send_error();
862 return;
865 write_prot = S_ISLNK(info.st_mode) ? FALSE
866 : access(src_path, W_OK) != 0;
867 if (write_prot || !quiet)
869 if (!printf_reply(from_parent, write_prot && !o_force,
870 _("?Delete %s'%s'?"),
871 write_prot ? _("WRITE-PROTECTED ") : "",
872 src_path))
873 return;
875 else if (!o_brief)
876 printf_send(_("'Deleting '%s'\n"), src_path);
878 safe_path = g_strdup(src_path);
880 if (S_ISDIR(info.st_mode))
882 for_dir_contents(do_delete, safe_path, safe_path);
883 if (rmdir(safe_path))
885 g_free(safe_path);
886 send_error();
887 return;
889 printf_send(_("'Directory '%s' deleted\n"), safe_path);
890 send_mount_path(safe_path);
892 else if (unlink(src_path))
893 send_error();
894 else
896 send_check_path(safe_path);
897 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
899 gchar *dir;
900 dir = g_dirname(safe_path);
901 send_check_path(dir);
902 g_free(dir);
906 g_free(safe_path);
909 /* path is the item to check. If is is a directory then we may recurse
910 * (unless prune is used).
912 static void do_find(const char *path, const char *unused)
914 FindInfo info;
916 check_flags();
918 if (!quiet)
920 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
921 return;
924 for (;;)
926 if (new_entry_string)
928 find_condition_free(find_condition);
929 find_condition = find_compile(new_entry_string);
930 null_g_free(&new_entry_string);
933 if (find_condition)
934 break;
936 printf_send(_("!Invalid find condition - "
937 "change it and try again\n"));
938 if (!printf_reply(from_parent, TRUE,
939 _("?Check '%s'?"), path))
940 return;
943 if (mc_lstat(path, &info.stats))
945 send_error();
946 printf_send(_("'(while checking '%s')\n"), path);
947 return;
950 info.fullpath = path;
951 time(&info.now); /* XXX: Not for each check! */
953 info.leaf = g_basename(path);
954 info.prune = FALSE;
955 if (find_test_condition(find_condition, &info))
956 printf_send("=%s", path);
958 if (S_ISDIR(info.stats.st_mode) && !info.prune)
960 char *safe_path;
961 safe_path = g_strdup(path);
962 for_dir_contents(do_find, safe_path, safe_path);
963 g_free(safe_path);
967 /* Like mode_compile(), but ignores spaces and bracketed bits */
968 static struct mode_change *nice_mode_compile(const char *mode_string,
969 unsigned int masked_ops)
971 GString *new;
972 int brackets = 0;
973 struct mode_change *retval = NULL;
975 new = g_string_new(NULL);
977 for (; *mode_string; mode_string++)
979 if (*mode_string == '(')
980 brackets++;
981 if (*mode_string == ')')
983 brackets--;
984 if (brackets < 0)
985 break;
986 continue;
989 if (brackets == 0 && *mode_string != ' ')
990 g_string_append_c(new, *mode_string);
993 if (brackets == 0)
994 retval = mode_compile(new->str, masked_ops);
995 g_string_free(new, TRUE);
996 return retval;
999 static void do_chmod(const char *path, const char *unused)
1001 struct stat info;
1002 mode_t new_mode;
1004 check_flags();
1006 if (mc_lstat(path, &info))
1008 send_error();
1009 return;
1011 if (S_ISLNK(info.st_mode))
1012 return;
1014 if (!quiet)
1016 if (!printf_reply(from_parent, FALSE,
1017 _("?Change permissions of '%s'?"), path))
1018 return;
1020 else if (!o_brief)
1021 printf_send(_("'Changing permissions of '%s'\n"), path);
1023 for (;;)
1025 if (new_entry_string)
1027 if (mode_change)
1028 mode_free(mode_change);
1029 mode_change = nice_mode_compile(new_entry_string,
1030 MODE_MASK_ALL);
1031 null_g_free(&new_entry_string);
1034 if (mode_change)
1035 break;
1037 printf_send(
1038 _("!Invalid mode command - change it and try again\n"));
1039 if (!printf_reply(from_parent, TRUE,
1040 _("?Change permissions of '%s'?"), path))
1041 return;
1044 if (mc_lstat(path, &info))
1046 send_error();
1047 return;
1049 if (S_ISLNK(info.st_mode))
1050 return;
1052 new_mode = mode_adjust(info.st_mode, mode_change);
1053 if (chmod(path, new_mode))
1055 send_error();
1056 return;
1059 send_check_path(path);
1061 if (S_ISDIR(info.st_mode))
1063 send_mount_path(path);
1065 if (o_recurse)
1067 guchar *safe_path;
1068 safe_path = g_strdup(path);
1069 for_dir_contents(do_chmod, safe_path, safe_path);
1070 g_free(safe_path);
1075 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1076 * is set then that is the new leafname, otherwise the leafname stays
1077 * the same.
1079 static char *make_dest_path(const char *object, const char *dir)
1081 const char *leaf;
1083 if (action_leaf)
1084 leaf = action_leaf;
1085 else
1087 leaf = strrchr(object, '/');
1088 if (!leaf)
1089 leaf = object; /* Error? */
1090 else
1091 leaf++;
1094 return make_path(dir, leaf)->str;
1097 /* If action_leaf is not NULL it specifies the new leaf name */
1098 static void do_copy2(const char *path, const char *dest)
1100 char *dest_path;
1101 struct stat info;
1102 struct stat dest_info;
1104 check_flags();
1106 dest_path = make_dest_path(path, dest);
1108 if (mc_lstat(path, &info))
1110 send_error();
1111 return;
1114 if (mc_lstat(dest_path, &dest_info) == 0)
1116 int err;
1117 gboolean merge;
1119 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1121 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1123 /* Newer; keep going */
1125 else
1127 if (!printf_reply(from_parent, TRUE,
1128 _("?'%s' already exists - %s?"),
1129 dest_path,
1130 merge ? _("merge contents")
1131 : _("overwrite")))
1132 return;
1135 if (!merge)
1137 if (S_ISDIR(dest_info.st_mode))
1138 err = rmdir(dest_path);
1139 else
1140 err = unlink(dest_path);
1142 if (err)
1144 send_error();
1145 if (errno != ENOENT)
1146 return;
1147 printf_send(_("'Trying copy anyway...\n"));
1151 else if (!quiet)
1153 if (!printf_reply(from_parent, FALSE,
1154 _("?Copy %s as %s?"), path, dest_path))
1155 return;
1157 else
1158 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1160 if (S_ISDIR(info.st_mode))
1162 mode_t mode = info.st_mode;
1163 char *safe_path, *safe_dest;
1164 struct stat dest_info;
1165 gboolean exists;
1167 safe_path = g_strdup(path);
1168 safe_dest = g_strdup(dest_path);
1170 exists = !mc_lstat(dest_path, &dest_info);
1172 if (exists && !S_ISDIR(dest_info.st_mode))
1173 printf_send(_("!ERROR: Destination already exists, "
1174 "but is not a directory\n"));
1175 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1176 send_error();
1177 else
1179 if (!exists)
1180 /* (just been created then) */
1181 send_check_path(dest_path);
1183 action_leaf = NULL;
1184 for_dir_contents(do_copy2, safe_path, safe_dest);
1185 /* Note: dest_path now invalid... */
1187 if (!exists)
1189 struct utimbuf utb;
1191 /* We may have created the directory with
1192 * more permissions than the source so that
1193 * we could write to it... change it back now.
1195 if (chmod(safe_dest, mode))
1197 /* Some filesystems don't support
1198 * SetGID and SetUID bits. Ignore
1199 * these errors.
1201 if (errno != EPERM)
1202 send_error();
1205 /* Also, try to preserve the timestamps */
1206 utb.actime = info.st_atime;
1207 utb.modtime = info.st_mtime;
1209 utime(safe_dest, &utb);
1213 g_free(safe_path);
1214 g_free(safe_dest);
1216 else if (S_ISLNK(info.st_mode))
1218 char *target;
1220 /* Not all versions of cp(1) can make symlinks,
1221 * so we special-case it.
1224 target = readlink_dup(path);
1225 if (target)
1227 if (symlink(target, dest_path))
1228 send_error();
1229 else
1230 send_check_path(dest_path);
1232 g_free(target);
1234 else
1235 send_error();
1237 else
1239 guchar *error;
1241 error = copy_file(path, dest_path);
1243 if (error)
1245 printf_send(_("!%s\nFailed to copy '%s'"), error, path);
1246 g_free(error);
1248 else
1249 send_check_path(dest_path);
1253 /* If action_leaf is not NULL it specifies the new leaf name */
1254 static void do_move2(const char *path, const char *dest)
1256 char *dest_path;
1257 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1258 struct stat info2;
1259 gboolean is_dir;
1260 char *err;
1262 check_flags();
1264 dest_path = make_dest_path(path, dest);
1266 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1268 if (access(dest_path, F_OK) == 0)
1270 struct stat info;
1271 int err;
1273 if (mc_lstat(dest_path, &info))
1275 send_error();
1276 return;
1279 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1281 /* Newer; keep going */
1283 else if (!printf_reply(from_parent, TRUE,
1284 _("?'%s' already exists - overwrite?"),
1285 dest_path))
1286 return;
1288 if (S_ISDIR(info.st_mode))
1289 err = rmdir(dest_path);
1290 else
1291 err = unlink(dest_path);
1293 if (err)
1295 send_error();
1296 if (errno != ENOENT)
1297 return;
1298 printf_send(_("'Trying move anyway...\n"));
1301 else if (!quiet)
1303 if (!printf_reply(from_parent, FALSE,
1304 _("?Move %s as %s?"), path, dest_path))
1305 return;
1307 else
1308 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1310 argv[2] = path;
1311 argv[3] = dest_path;
1313 err = fork_exec_wait(argv);
1314 if (err)
1316 printf_send(_("!%s\nFailed to move %s as %s\n"),
1317 err, path, dest_path);
1318 g_free(err);
1320 else
1322 send_check_path(dest_path);
1324 if (is_dir)
1325 send_mount_path(path);
1326 else
1327 send_check_path(path);
1331 /* Copy path to dest.
1332 * Check that path not copied into itself.
1334 static void do_copy(const char *path, const char *dest)
1336 if (is_sub_dir(make_dest_path(path, dest), path))
1337 printf_send(_("!ERROR: Can't copy object into itself\n"));
1338 else
1339 do_copy2(path, dest);
1342 /* Move path to dest.
1343 * Check that path not moved into itself.
1345 static void do_move(const char *path, const char *dest)
1347 if (is_sub_dir(make_dest_path(path, dest), path))
1348 printf_send(
1349 _("!ERROR: Can't move/rename object into itself\n"));
1350 else
1351 do_move2(path, dest);
1354 static void do_link(const char *path, const char *dest)
1356 char *dest_path;
1358 check_flags();
1360 dest_path = make_dest_path(path, dest);
1362 if (quiet)
1363 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1364 else if (!printf_reply(from_parent, FALSE,
1365 _("?Link %s as %s?"), path, dest_path))
1366 return;
1368 if (symlink(path, dest_path))
1369 send_error();
1370 else
1371 send_check_path(dest_path);
1374 /* Mount/umount this item (depending on 'mount') */
1375 static void do_mount(guchar *path, gboolean mount)
1377 const char *argv[3] = {NULL, NULL, NULL};
1378 char *err;
1380 check_flags();
1382 argv[0] = mount ? "mount" : "umount";
1383 argv[1] = path;
1385 if (quiet)
1386 printf_send(mount ? _("'Mounting %s\n")
1387 : _("'Unmounting %s\n"),
1388 path);
1389 else if (!printf_reply(from_parent, FALSE,
1390 mount ? _("?Mount %s?")
1391 : _("?Unmount %s?"),
1392 path))
1393 return;
1395 err = fork_exec_wait(argv);
1396 if (err)
1398 printf_send(mount ?
1399 _("!%s\nMount failed\n") :
1400 _("!%s\nUnmount failed\n"), err);
1401 g_free(err);
1403 else
1405 printf_send("M%s", path);
1406 if (mount && mount_open_dir)
1407 printf_send("o%s", path);
1411 /* CHILD MAIN LOOPS */
1413 /* After forking, the child calls one of these functions */
1415 /* We use a double for total size in order to count beyond 4Gb */
1416 static void usage_cb(gpointer data)
1418 GList *paths = (GList *) data;
1419 double total_size = 0;
1421 dir_counter = file_counter = 0;
1423 for (; paths; paths = paths->next)
1425 guchar *path = (guchar *) paths->data;
1427 send_dir(path);
1429 size_tally = 0;
1431 do_usage(path, NULL);
1433 printf_send("'%s: %s\n",
1434 g_basename(path),
1435 format_double_size(size_tally));
1436 total_size += size_tally;
1439 g_string_printf(message, _("'\nTotal: %s ("),
1440 format_double_size(total_size));
1442 if (file_counter)
1443 g_string_append_printf(message,
1444 "%ld %s%s", file_counter,
1445 file_counter == 1 ? _("file") : _("files"),
1446 dir_counter ? ", " : ")\n");
1448 if (file_counter == 0 && dir_counter == 0)
1449 g_string_append(message, _("no directories)\n"));
1450 else if (dir_counter)
1451 g_string_append_printf(message,
1452 "%ld %s)\n", dir_counter,
1453 dir_counter == 1 ? _("directory")
1454 : _("directories"));
1456 send();
1459 #ifdef DO_MOUNT_POINTS
1460 static void mount_cb(gpointer data)
1462 GList *paths = (GList *) data;
1463 gboolean mount_points = FALSE;
1465 for (; paths; paths = paths->next)
1467 guchar *path = (guchar *) paths->data;
1469 if (mount_is_mounted(path, NULL, NULL))
1470 do_mount(path, FALSE); /* Unmount */
1471 else if (g_hash_table_lookup(fstab_mounts, path))
1472 do_mount(path, TRUE); /* Mount */
1473 else
1474 continue;
1476 mount_points = TRUE;
1479 if (mount_points)
1480 send_done();
1481 else
1482 printf_send(_("!No mount points selected!\n"));
1484 #endif
1486 /* (use g_dirname() instead?) */
1487 static guchar *dirname(guchar *path)
1489 guchar *slash;
1491 slash = strrchr(path, '/');
1492 g_return_val_if_fail(slash != NULL, g_strdup(path));
1494 if (slash != path)
1495 return g_strndup(path, slash - path);
1496 return g_strdup("/");
1499 static void delete_cb(gpointer data)
1501 GList *paths = (GList *) data;
1503 for (; paths; paths = paths->next)
1505 guchar *path = (guchar *) paths->data;
1506 guchar *dir;
1508 dir = dirname(path);
1509 send_dir(dir);
1511 do_delete(path, dir);
1513 g_free(dir);
1516 send_done();
1519 static void find_cb(gpointer data)
1521 GList *all_paths = (GList *) data;
1522 GList *paths;
1524 while (1)
1526 for (paths = all_paths; paths; paths = paths->next)
1528 guchar *path = (guchar *) paths->data;
1530 send_dir(path);
1532 do_find(path, NULL);
1535 if (!printf_reply(from_parent, TRUE,
1536 _("?Another search?")))
1537 break;
1538 printf_send("#");
1541 send_done();
1544 static void chmod_cb(gpointer data)
1546 GList *paths = (GList *) data;
1548 for (; paths; paths = paths->next)
1550 guchar *path = (guchar *) paths->data;
1551 struct stat info;
1553 send_dir(path);
1555 if (mc_stat(path, &info) != 0)
1556 send_error();
1557 else if (S_ISLNK(info.st_mode))
1558 printf_send(_("!'%s' is a symbolic link\n"),
1559 g_basename(path));
1560 else
1561 do_chmod(path, NULL);
1564 send_done();
1567 static void list_cb(gpointer data)
1569 GList *paths = (GList *) data;
1571 for (; paths; paths = paths->next)
1573 send_dir((char *) paths->data);
1575 action_do_func((char *) paths->data, action_dest);
1578 send_done();
1581 /* EXTERNAL INTERFACE */
1583 void action_find(GList *paths)
1585 GUIside *gui_side;
1586 GtkWidget *abox;
1588 if (!paths)
1590 report_error(_("You need to select some items "
1591 "to search through"));
1592 return;
1595 if (!last_find_string)
1596 last_find_string = g_strdup("'core'");
1598 new_entry_string = last_find_string;
1600 abox = abox_new(_("Find"), FALSE);
1601 gui_side = start_action(abox, find_cb, paths,
1602 o_action_force.int_value,
1603 o_action_brief.int_value,
1604 o_action_recurse.int_value,
1605 o_action_newer.int_value);
1606 if (!gui_side)
1607 return;
1609 abox_add_results(ABOX(abox));
1611 gui_side->default_string = &last_find_string;
1612 abox_add_entry(ABOX(abox), last_find_string,
1613 new_help_button(show_condition_help, NULL));
1614 g_signal_connect(ABOX(abox)->entry, "changed",
1615 G_CALLBACK(entry_changed), gui_side);
1616 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1618 gui_side->show_info = TRUE;
1619 gui_side->entry_string_func = set_find_string_colour;
1621 number_of_windows++;
1622 gtk_widget_show_all(abox);
1625 /* Count disk space used by selected items */
1626 void action_usage(GList *paths)
1628 GUIside *gui_side;
1629 GtkWidget *abox;
1631 if (!paths)
1633 report_error(_("You need to select some items to count"));
1634 return;
1637 abox = abox_new(_("Disk Usage"), TRUE);
1639 gui_side = start_action(abox, usage_cb, paths,
1640 o_action_force.int_value,
1641 o_action_brief.int_value,
1642 o_action_recurse.int_value,
1643 o_action_newer.int_value);
1644 if (!gui_side)
1645 return;
1647 gui_side->show_info = TRUE;
1649 number_of_windows++;
1651 gtk_widget_show(abox);
1654 /* Mount/unmount listed items (paths).
1655 * Free the list after this function returns.
1656 * If open_dir is TRUE and the dir is successfully mounted, open it.
1657 * quiet can be -1 for default.
1659 void action_mount(GList *paths, gboolean open_dir, int quiet)
1661 #ifdef DO_MOUNT_POINTS
1662 GUIside *gui_side;
1663 GtkWidget *abox;
1665 if (quiet == -1)
1666 quiet = o_action_mount.int_value;
1668 mount_open_dir = open_dir;
1670 abox = abox_new(_("Mount / Unmount"), quiet);
1671 gui_side = start_action(abox, mount_cb, paths,
1672 o_action_force.int_value,
1673 o_action_brief.int_value,
1674 o_action_recurse.int_value,
1675 o_action_newer.int_value);
1676 if (!gui_side)
1677 return;
1679 number_of_windows++;
1680 gtk_widget_show(abox);
1681 #else
1682 report_error(
1683 _("ROX-Filer does not yet support mount points on your "
1684 "system. Sorry."));
1685 #endif /* DO_MOUNT_POINTS */
1688 /* Deletes all selected items in the window */
1689 void action_delete(GList *paths)
1691 GUIside *gui_side;
1692 GtkWidget *abox;
1694 if (!remove_pinned_ok(paths))
1695 return;
1697 abox = abox_new(_("Delete"), o_action_delete.int_value);
1698 gui_side = start_action(abox, delete_cb, paths,
1699 o_action_force.int_value,
1700 o_action_brief.int_value,
1701 o_action_recurse.int_value,
1702 o_action_newer.int_value);
1703 if (!gui_side)
1704 return;
1706 abox_add_flag(ABOX(abox),
1707 _("Force"), _("Don't confirm deletion of non-writeable items"),
1708 'F', o_action_force.int_value);
1709 abox_add_flag(ABOX(abox),
1710 _("Brief"), _("Only log directories being deleted"),
1711 'B', o_action_brief.int_value);
1713 number_of_windows++;
1714 gtk_widget_show(abox);
1717 /* Change the permissions of the selected items */
1718 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
1720 GtkWidget *abox;
1721 GUIside *gui_side;
1722 static GList *presets = NULL;
1723 gboolean recurse = force_recurse || o_action_recurse.int_value;
1725 if (!paths)
1727 report_error(_("You need to select the items "
1728 "whose permissions you want to change"));
1729 return;
1732 if (!presets)
1734 presets = g_list_append(presets, (gchar *)
1735 _("a+x (Make executable/searchable)"));
1736 presets = g_list_append(presets, (gchar *)
1737 _("a-x (Make non-executable/non-searchable)"));
1738 presets = g_list_append(presets, (gchar *)
1739 _("u+rw (Give owner read+write)"));
1740 presets = g_list_append(presets, (gchar *)
1741 _("go-rwx (Private - owner access only)"));
1742 presets = g_list_append(presets, (gchar *)
1743 _("go=u-w (Public access, not write)"));
1746 if (!last_chmod_string)
1747 last_chmod_string = g_strdup((guchar *) presets->data);
1749 if (action)
1750 new_entry_string = g_strdup(action);
1751 else
1752 new_entry_string = g_strdup(last_chmod_string);
1754 abox = abox_new(_("Permissions"), FALSE);
1755 gui_side = start_action(abox, chmod_cb, paths,
1756 o_action_force.int_value,
1757 o_action_brief.int_value,
1758 recurse,
1759 o_action_newer.int_value);
1761 if (!gui_side)
1762 goto out;
1764 abox_add_flag(ABOX(abox),
1765 _("Brief"), _("Don't list processed files"),
1766 'B', o_action_brief.int_value);
1767 abox_add_flag(ABOX(abox),
1768 _("Recurse"), _("Also change contents of subdirectories"),
1769 'R', recurse);
1771 gui_side->default_string = &last_chmod_string;
1772 abox_add_combo(ABOX(abox), presets, new_entry_string,
1773 new_help_button(show_chmod_help, NULL));
1775 g_signal_connect(ABOX(abox)->entry, "changed",
1776 G_CALLBACK(entry_changed), gui_side);
1777 #if 0
1778 g_signal_connect_swapped(gui_side->entry, "activate",
1779 G_CALLBACK(gtk_button_clicked),
1780 gui_side->yes);
1781 #endif
1783 number_of_windows++;
1784 gtk_widget_show(abox);
1786 out:
1787 null_g_free(&new_entry_string);
1790 /* If leaf is NULL then the copy has the same name as the original.
1791 * quiet can be -1 for default.
1793 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
1795 GUIside *gui_side;
1796 GtkWidget *abox;
1798 if (quiet == -1)
1799 quiet = o_action_copy.int_value;
1801 action_dest = dest;
1802 action_leaf = leaf;
1803 action_do_func = do_copy;
1805 abox = abox_new(_("Copy"), quiet);
1806 gui_side = start_action(abox, list_cb, paths,
1807 o_action_force.int_value,
1808 o_action_brief.int_value,
1809 o_action_recurse.int_value,
1810 o_action_newer.int_value);
1811 if (!gui_side)
1812 return;
1814 abox_add_flag(ABOX(abox),
1815 _("Newer"),
1816 _("Only over-write if source is newer than destination."),
1817 'W', o_action_newer.int_value);
1819 number_of_windows++;
1820 gtk_widget_show(abox);
1823 /* If leaf is NULL then the file is not renamed.
1824 * quiet can be -1 for default.
1826 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
1828 GUIside *gui_side;
1829 GtkWidget *abox;
1831 if (quiet == -1)
1832 quiet = o_action_move.int_value;
1834 action_dest = dest;
1835 action_leaf = leaf;
1836 action_do_func = do_move;
1838 abox = abox_new(_("Move"), quiet);
1839 gui_side = start_action(abox, list_cb, paths,
1840 o_action_force.int_value,
1841 o_action_brief.int_value,
1842 o_action_recurse.int_value,
1843 o_action_newer.int_value);
1844 if (!gui_side)
1845 return;
1847 abox_add_flag(ABOX(abox),
1848 _("Newer"),
1849 _("Only over-write if source is newer than destination."),
1850 'W', o_action_newer.int_value);
1851 number_of_windows++;
1852 gtk_widget_show(abox);
1855 /* If leaf is NULL then the link will have the same name */
1856 /* XXX: No quiet option here? */
1857 void action_link(GList *paths, const char *dest, const char *leaf)
1859 GtkWidget *abox;
1860 GUIside *gui_side;
1862 action_dest = dest;
1863 action_leaf = leaf;
1864 action_do_func = do_link;
1866 abox = abox_new(_("Link"), o_action_link.int_value);
1867 gui_side = start_action(abox, list_cb, paths,
1868 o_action_force.int_value,
1869 o_action_brief.int_value,
1870 o_action_recurse.int_value,
1871 o_action_newer.int_value);
1872 if (!gui_side)
1873 return;
1875 number_of_windows++;
1876 gtk_widget_show(abox);
1879 void action_init(void)
1881 option_add_int(&o_action_copy, "action_copy", 1);
1882 option_add_int(&o_action_move, "action_move", 1);
1883 option_add_int(&o_action_link, "action_link", 1);
1884 option_add_int(&o_action_delete, "action_delete", 0);
1885 option_add_int(&o_action_mount, "action_mount", 1);
1886 option_add_int(&o_action_force, "action_force", FALSE);
1887 option_add_int(&o_action_brief, "action_brief", FALSE);
1888 option_add_int(&o_action_recurse, "action_recurse", FALSE);
1889 option_add_int(&o_action_newer, "action_newer", FALSE);
1892 #define MAX_ASK 4
1894 /* Check to see if any of the selected items (or their children) are
1895 * on the pinboard or panel. If so, ask for confirmation.
1897 * TRUE if it's OK to lose them.
1899 static gboolean remove_pinned_ok(GList *paths)
1901 GList *ask = NULL, *next;
1902 GString *message;
1903 int i, ask_n = 0;
1904 gboolean retval;
1906 for (; paths; paths = paths->next)
1908 guchar *path = (guchar *) paths->data;
1910 if (icons_require(path))
1912 if (++ask_n > MAX_ASK)
1913 break;
1914 ask = g_list_append(ask, path);
1918 if (!ask)
1919 return TRUE;
1921 if (ask_n > MAX_ASK)
1923 message = g_string_new(_("Deleting items such as "));
1924 ask_n--;
1926 else if (ask_n == 1)
1927 message = g_string_new(_("Deleting the item "));
1928 else
1929 message = g_string_new(_("Deleting the items "));
1931 i = 0;
1932 for (next = ask; next; next = next->next)
1934 guchar *path = (guchar *) next->data;
1935 guchar *leaf;
1937 leaf = strrchr(path, '/');
1938 if (leaf)
1939 leaf++;
1940 else
1941 leaf = path;
1943 g_string_append_c(message, '`');
1944 g_string_append(message, leaf);
1945 g_string_append_c(message, '\'');
1946 i++;
1947 if (i == ask_n - 1 && i > 0)
1948 g_string_append(message, _(" and "));
1949 else if (i < ask_n)
1950 g_string_append(message, ", ");
1953 g_list_free(ask);
1955 if (ask_n == 1)
1956 message = g_string_append(message,
1957 _(" will affect some items on the pinboard "
1958 "or panel - really delete it?"));
1959 else
1961 if (ask_n > MAX_ASK)
1962 message = g_string_append_c(message, ',');
1963 message = g_string_append(message,
1964 _(" will affect some items on the pinboard "
1965 "or panel - really delete them?"));
1968 retval = get_choice(PROJECT, message->str,
1969 2, _("Cancel"), _("Delete")) == 1;
1971 g_string_free(message, TRUE);
1973 return retval;
1976 void set_find_string_colour(GtkWidget *widget, const guchar *string)
1978 FindCondition *cond;
1980 cond = find_compile(string);
1981 entry_set_error(widget, !cond);
1983 find_condition_free(cond);