r2313: 2002 -> 2003
[rox-filer.git] / ROX-Filer / src / action.c
blobd03d8c0504901eba01d91101c9a74198bdfe3810
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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(const 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 GtkWidget *help;
170 GtkWidget *text;
172 help = gtk_dialog_new_with_buttons(
173 _("Find expression reference"),
174 NULL, 0,
175 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
176 NULL);
177 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
179 text = gtk_label_new(NULL);
180 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
181 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
182 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
183 gtk_label_set_markup(GTK_LABEL(text), _(
184 "<u>Quick Start</u>\n"
185 "Just put the name of the file you're looking for in single quotes:\n"
186 "<b>'index.html'</b> (to find a file called 'index.html')\n"
187 "\n"
188 "<u>Examples</u>\n"
189 "<b>'*.htm', '*.html'</b> (finds HTML files)\n"
190 "<b>IsDir 'lib'</b> (finds directories called 'lib')\n"
191 "<b>IsReg 'core'</b> (finds a regular file called 'core')\n"
192 "<b>! (IsDir, IsReg)</b> (is neither a directory nor a regular file)\n"
193 "<b>mtime after 1 day ago and size > 1Mb</b> (big, and recently modified)\n"
194 "<b>'CVS' prune, isreg</b> (a regular file not in CVS)\n"
195 "<b>IsReg system(grep -q fred \"%\")</b> (contains the word 'fred')\n"
196 "\n"
197 "<u>Simple Tests</u>\n"
198 "<b>IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket, IsDoor</b> "
199 "(types)\n"
200 "<b>IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable</b> "
201 "(permissions)\n"
202 "<b>IsEmpty, IsMine</b>\n"
203 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
204 "contains a slash then the match is against the full path; otherwise it is\n"
205 "against the leafname only.\n"
206 "\n"
207 "<u>Comparisons</u>\n"
208 "<b>&lt;, &lt;=, =, !=, &gt;, &gt;=, After, Before</b> (compare two values)\n"
209 "<b>5 bytes, 1Kb, 2Mb, 3Gb</b> (file sizes)\n"
210 "<b>2 secs|mins|hours|days|weeks|years ago|hence</b> (times)\n"
211 "<b>atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks</b> "
212 "(values)\n"
213 "\n"
214 "<u>Specials</u>\n"
215 "<b>system(command)</b> (true if 'command' returns with a zero exit status;\n"
216 "a % in 'command' is replaced with the path of the current file)\n"
217 "<b>prune</b> (false, and prevents searching the contents of a directory)."));
219 g_signal_connect(help, "response",
220 G_CALLBACK(gtk_widget_destroy), NULL);
222 gtk_widget_show_all(help);
225 static void show_chmod_help(gpointer data)
227 GtkWidget *help;
228 GtkWidget *text;
230 help = gtk_dialog_new_with_buttons(
231 _("Find expression reference"),
232 NULL, 0,
233 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
234 NULL);
235 gtk_dialog_set_default_response(GTK_DIALOG(help), GTK_RESPONSE_CANCEL);
237 text = gtk_label_new(NULL);
238 gtk_misc_set_padding(GTK_MISC(text), 2, 2);
239 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(help)->vbox), text);
240 gtk_label_set_selectable(GTK_LABEL(text), TRUE);
241 gtk_label_set_markup(GTK_LABEL(text), _(
242 "Normally, you can just select a command from the menu (click \n"
243 "on the arrow beside the command box). Sometimes, you need more...\n"
244 "\n"
245 "The format of a command is: <b>CHANGE, CHANGE, ...</b>\n"
246 "Each <b>CHANGE</b> is: <b>WHO HOW PERMISSIONS</b>\n"
247 "<b>WHO</b> is some combination of <b>u</b>, <b>g</b> and <b>o</b> which "
248 "determines whether to\n"
249 "change the permissions for the User (owner), Group or Others.\n"
250 "<b>HOW</b> is <b>+</b>, <b>-</b> or <b>=</b> to add, remove or set "
251 "exactly the permissions.\n"
252 "<b>PERMISSIONS</b> is some combination of the letters <b>rwxXstugo</b>\n"
253 "\n"
254 "Bracketed text and spaces are ignored.\n"
255 "\n"
256 "<u>Examples</u>\n"
257 "<b>u+rw</b>: the file owner gains read and write permission\n"
258 "<b>g=u</b>: the group permissions are set to be the same as the user's\n"
259 "<b>o=u-w</b>: others get the same permissions as the owner, but without "
260 "write permission\n"
261 "<b>a+x</b>: <b>a</b>ll get execute/access permission - same as <b>ugo+x</b>\n"
262 "<b>a+X</b>: directories become accessable by everyone; files which were\n"
263 "executable by anyone become executable by everyone\n"
264 "<b>u+rw, go+r</b>: two commands at once!\n"
265 "<b>u+s</b>: set the SetUID bit - often has no effect on script files\n"
266 "<b>755</b>: set the permissions directly\n"
267 "\n"
268 "See the chmod(1) man page for full details."));
270 g_signal_connect(help, "response",
271 G_CALLBACK(gtk_widget_destroy), NULL);
273 gtk_widget_show_all(help);
276 static void process_message(GUIside *gui_side, const gchar *buffer)
278 ABox *abox = gui_side->abox;
280 if (*buffer == '?')
281 abox_ask(abox, buffer + 1);
282 else if (*buffer == 's')
283 dir_check_this(buffer + 1); /* Update this item */
284 else if (*buffer == '=')
285 abox_add_filename(abox, buffer + 1);
286 else if (*buffer == '#')
287 abox_clear_results(abox);
288 else if (*buffer == 'm' || *buffer == 'M')
290 /* Mount / major changes to this path */
291 if (*buffer == 'M')
293 mount_update(TRUE);
294 mount_user_mount(buffer + 1);
296 filer_check_mounted(buffer + 1);
298 else if (*buffer == '/')
299 abox_set_current_object(abox, buffer + 1);
300 else if (*buffer == 'o')
301 filer_opendir(buffer + 1, NULL, NULL);
302 else if (*buffer == '!')
304 gui_side->errors++;
305 abox_log(abox, buffer + 1, "error");
307 else
308 abox_log(abox, buffer + 1, NULL);
311 /* Called when the child sends us a message */
312 static void message_from_child(gpointer data,
313 gint source,
314 GdkInputCondition condition)
316 char buf[5];
317 GUIside *gui_side = (GUIside *) data;
318 ABox *abox = gui_side->abox;
319 GtkTextBuffer *text_buffer;
321 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(abox->log));
323 if (read_exact(source, buf, 4))
325 ssize_t message_len;
326 char *buffer;
328 buf[4] = '\0';
329 message_len = strtol(buf, NULL, 16);
330 buffer = g_malloc(message_len + 1);
331 if (message_len > 0 && read_exact(source, buffer, message_len))
333 buffer[message_len] = '\0';
334 process_message(gui_side, buffer);
335 g_free(buffer);
336 return;
338 g_printerr("Child died in the middle of a message.\n");
341 /* The child is dead */
342 gui_side->child = 0;
344 fclose(gui_side->to_child);
345 gui_side->to_child = NULL;
346 close(gui_side->from_child);
347 g_source_remove(gui_side->input_tag);
348 abox_cancel_ask(gui_side->abox);
350 if (gui_side->errors)
352 guchar *report;
354 if (gui_side->errors == 1)
355 report = g_strdup(_("There was one error.\n"));
356 else
357 report = g_strdup_printf(_("There were %d errors.\n"),
358 gui_side->errors);
360 gtk_text_buffer_insert_at_cursor(text_buffer, report, -1);
362 g_free(report);
364 else if (gui_side->show_info == FALSE)
365 gtk_widget_destroy(GTK_WIDGET(gui_side->abox));
368 /* Scans src_dir, calling cb(item, dest_path) for each item */
369 static void for_dir_contents(ForDirCB *cb,
370 const char *src_dir,
371 const char *dest_path)
373 DIR *d;
374 struct dirent *ent;
375 GList *list = NULL, *next;
377 d = mc_opendir(src_dir);
378 if (!d)
380 /* Message displayed is "ERROR reading 'path': message" */
381 printf_send("!%s '%s': %s\n", _("ERROR reading"),
382 src_dir, g_strerror(errno));
383 return;
386 send_dir(src_dir);
388 while ((ent = mc_readdir(d)))
390 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
391 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
392 continue;
393 list = g_list_prepend(list, g_strdup(make_path(src_dir,
394 ent->d_name)));
396 mc_closedir(d);
398 for (next = list; next; next = next->next)
400 cb((char *) next->data, dest_path);
402 g_free(next->data);
404 g_list_free(list);
407 /* Read this many bytes into the buffer. TRUE on success. */
408 static gboolean read_exact(int source, char *buffer, ssize_t len)
410 while (len > 0)
412 ssize_t got;
413 got = read(source, buffer, len);
414 if (got < 1)
415 return FALSE;
416 len -= got;
417 buffer += got;
419 return TRUE;
422 static void send_done(void)
424 printf_send(_("'\nDone\n"));
427 /* Notify the filer that this item has been updated */
428 static void send_check_path(const gchar *path)
430 printf_send("s%s", path);
433 /* Notify the filer that this whole subtree has changed (eg, been unmounted) */
434 static void send_mount_path(const gchar *path)
436 printf_send("m%s", path);
439 /* Send a message to the filer process. The first character indicates the
440 * type of the message.
442 static gboolean printf_send(const char *msg, ...)
444 va_list args;
445 gchar *tmp;
447 va_start(args, msg);
448 tmp = g_strdup_vprintf(msg, args);
449 va_end(args);
451 g_string_assign(message, tmp);
452 g_free(tmp);
454 return send();
457 /* Send 'message' to our parent process. TRUE on success. */
458 static gboolean send(void)
460 char len_buffer[5];
461 ssize_t len;
463 g_return_val_if_fail(message->len < 0xffff, FALSE);
465 sprintf(len_buffer, "%04x", message->len);
466 fwrite(len_buffer, 1, 4, to_parent);
467 len = fwrite(message->str, 1, message->len, to_parent);
468 fflush(to_parent);
469 return len == (ssize_t) message->len;
472 /* Set the directory indicator at the top of the window */
473 static gboolean send_dir(const char *dir)
475 return printf_send("/%s", dir);
478 static gboolean send_error(void)
480 return printf_send("!%s: %s\n", _("ERROR"), g_strerror(errno));
483 static void response(GtkDialog *dialog, gint response, GUIside *gui_side)
485 gchar code;
487 if (!gui_side->to_child)
488 return;
490 if (response == GTK_RESPONSE_YES)
491 code = 'Y';
492 else if (response == GTK_RESPONSE_NO)
493 code = 'N';
494 else
495 return;
497 fputc(code, gui_side->to_child);
498 fflush(gui_side->to_child);
501 static void flag_toggled(ABox *abox, gint flag, GUIside *gui_side)
503 if (!gui_side->to_child)
504 return;
506 fputc(flag, gui_side->to_child);
507 fflush(gui_side->to_child);
510 static void read_new_entry_text(void)
512 int len;
513 char c;
514 GString *new;
516 new = g_string_new(NULL);
518 for (;;)
520 len = read(from_parent, &c, 1);
521 if (len != 1)
523 fprintf(stderr, "read() error: %s\n",
524 g_strerror(errno));
525 _exit(1); /* Parent died? */
528 if (c == '\n')
529 break;
530 g_string_append_c(new, c);
533 g_free(new_entry_string);
534 new_entry_string = new->str;
535 g_string_free(new, FALSE);
538 static void process_flag(char flag)
540 switch (flag)
542 case 'Q':
543 quiet = !quiet;
544 break;
545 case 'F':
546 o_force = !o_force;
547 break;
548 case 'R':
549 o_recurse = !o_recurse;
550 break;
551 case 'B':
552 o_brief = !o_brief;
553 break;
554 case 'W':
555 o_newer = !o_newer;
556 break;
557 case 'E':
558 read_new_entry_text();
559 break;
560 default:
561 printf_send("!ERROR: Bad message '%c'\n", flag);
562 break;
566 /* If the parent has sent any flag toggles, read them */
567 static void check_flags(void)
569 fd_set set;
570 int got;
571 char retval;
572 struct timeval tv;
574 FD_ZERO(&set);
576 while (1)
578 FD_SET(from_parent, &set);
579 tv.tv_sec = 0;
580 tv.tv_usec = 0;
581 got = select(from_parent + 1, &set, NULL, NULL, &tv);
583 if (got == -1)
584 g_error("select() failed: %s\n", g_strerror(errno));
585 else if (!got)
586 return;
588 got = read(from_parent, &retval, 1);
589 if (got != 1)
590 g_error("read() error: %s\n", g_strerror(errno));
592 process_flag(retval);
596 /* Read until the user sends a reply. If ignore_quiet is TRUE then
597 * the user MUST click Yes or No, else treat quiet on as Yes.
598 * If the user needs prompting then does send().
600 static gboolean printf_reply(int fd, gboolean ignore_quiet,
601 const char *msg, ...)
603 ssize_t len;
604 char retval;
605 va_list args;
606 gchar *tmp;
608 if (quiet && !ignore_quiet)
609 return TRUE;
611 va_start(args, msg);
612 tmp = g_strdup_vprintf(msg, args);
613 va_end(args);
615 g_string_assign(message, tmp);
616 g_free(tmp);
618 send();
620 while (1)
622 len = read(fd, &retval, 1);
623 if (len != 1)
625 fprintf(stderr, "read() error: %s\n",
626 g_strerror(errno));
627 _exit(1); /* Parent died? */
630 switch (retval)
632 case 'Y':
633 printf_send("' %s\n", _("Yes"));
634 return TRUE;
635 case 'N':
636 printf_send("' %s\n", _("No"));
637 return FALSE;
638 default:
639 process_flag(retval);
640 break;
645 static void destroy_action_window(GtkWidget *widget, gpointer data)
647 GUIside *gui_side = (GUIside *) data;
649 if (gui_side->child)
651 kill(gui_side->child, SIGTERM);
652 fclose(gui_side->to_child);
653 close(gui_side->from_child);
654 g_source_remove(gui_side->input_tag);
657 g_free(gui_side);
659 one_less_window();
662 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
663 * (NULL on failure). The child calls func().
665 static GUIside *start_action(GtkWidget *abox, ActionChild *func, gpointer data,
666 int force, int brief, int recurse, int newer)
668 gboolean autoq;
669 int filedes[4]; /* 0 and 2 are for reading */
670 GUIside *gui_side;
671 pid_t child;
672 struct sigaction act;
674 if (pipe(filedes))
676 report_error("pipe: %s", g_strerror(errno));
677 gtk_widget_destroy(abox);
678 return NULL;
681 if (pipe(filedes + 2))
683 close(filedes[0]);
684 close(filedes[1]);
685 report_error("pipe: %s", g_strerror(errno));
686 gtk_widget_destroy(abox);
687 return NULL;
690 autoq = gtk_toggle_button_get_active(
691 GTK_TOGGLE_BUTTON(ABOX(abox)->quiet));
693 o_force = force;
694 o_brief = brief;
695 o_recurse = recurse;
696 o_newer = newer;
698 child = fork();
699 switch (child)
701 case -1:
702 report_error("fork: %s", g_strerror(errno));
703 gtk_widget_destroy(abox);
704 return NULL;
705 case 0:
706 /* We are the child */
708 quiet = autoq;
710 /* Reset the SIGCHLD handler */
711 act.sa_handler = SIG_DFL;
712 sigemptyset(&act.sa_mask);
713 act.sa_flags = 0;
714 sigaction(SIGCHLD, &act, NULL);
716 message = g_string_new(NULL);
717 close(filedes[0]);
718 close(filedes[3]);
719 to_parent = fdopen(filedes[1], "wb");
720 from_parent = filedes[2];
721 func(data);
722 send_dir("");
723 _exit(0);
726 /* We are the parent */
727 close(filedes[1]);
728 close(filedes[2]);
729 gui_side = g_malloc(sizeof(GUIside));
730 gui_side->from_child = filedes[0];
731 gui_side->to_child = fdopen(filedes[3], "wb");
732 gui_side->child = child;
733 gui_side->errors = 0;
734 gui_side->show_info = FALSE;
735 gui_side->default_string = NULL;
736 gui_side->entry_string_func = NULL;
738 gui_side->abox = ABOX(abox);
739 g_signal_connect(abox, "destroy",
740 G_CALLBACK(destroy_action_window), gui_side);
742 g_signal_connect(abox, "response", G_CALLBACK(response), gui_side);
743 g_signal_connect(abox, "flag_toggled",
744 G_CALLBACK(flag_toggled), gui_side);
746 gui_side->input_tag = gtk_input_add_full(gui_side->from_child,
747 GDK_INPUT_READ,
748 message_from_child,
749 NULL, gui_side, NULL);
751 return gui_side;
754 /* ACTIONS ON ONE ITEM */
756 /* These may call themselves recursively, or ask questions, etc */
758 /* Updates the global size_tally, file_counter and dir_counter */
759 static void do_usage(const char *src_path, const char *unused)
761 struct stat info;
763 check_flags();
765 if (mc_lstat(src_path, &info))
767 printf_send("'%s:\n", src_path);
768 send_error();
770 else if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
772 file_counter++;
773 size_tally += info.st_size;
775 else if (S_ISDIR(info.st_mode))
777 dir_counter++;
778 if (printf_reply(from_parent, FALSE,
779 _("?Count contents of %s?"), src_path))
781 char *safe_path;
782 safe_path = g_strdup(src_path);
783 for_dir_contents(do_usage, safe_path, safe_path);
784 g_free(safe_path);
787 else
788 file_counter++;
791 /* dest_path is the dir containing src_path */
792 static void do_delete(const char *src_path, const char *unused)
794 struct stat info;
795 gboolean write_prot;
796 char *safe_path;
798 check_flags();
800 if (mc_lstat(src_path, &info))
802 send_error();
803 return;
806 write_prot = S_ISLNK(info.st_mode) ? FALSE
807 : access(src_path, W_OK) != 0;
808 if (write_prot || !quiet)
810 if (!printf_reply(from_parent, write_prot && !o_force,
811 _("?Delete %s'%s'?"),
812 write_prot ? _("WRITE-PROTECTED ") : "",
813 src_path))
814 return;
816 else if (!o_brief)
817 printf_send(_("'Deleting '%s'\n"), src_path);
819 safe_path = g_strdup(src_path);
821 if (S_ISDIR(info.st_mode))
823 for_dir_contents(do_delete, safe_path, safe_path);
824 if (rmdir(safe_path))
826 g_free(safe_path);
827 send_error();
828 return;
830 printf_send(_("'Directory '%s' deleted\n"), safe_path);
831 send_mount_path(safe_path);
833 else if (unlink(src_path))
834 send_error();
835 else
837 send_check_path(safe_path);
838 if (strcmp(g_basename(safe_path), ".DirIcon") == 0)
840 gchar *dir;
841 dir = g_path_get_dirname(safe_path);
842 send_check_path(dir);
843 g_free(dir);
847 g_free(safe_path);
850 /* path is the item to check. If is is a directory then we may recurse
851 * (unless prune is used).
853 static void do_find(const char *path, const char *unused)
855 FindInfo info;
857 check_flags();
859 if (!quiet)
861 if (!printf_reply(from_parent, FALSE, _("?Check '%s'?"), path))
862 return;
865 for (;;)
867 if (new_entry_string)
869 find_condition_free(find_condition);
870 find_condition = find_compile(new_entry_string);
871 null_g_free(&new_entry_string);
874 if (find_condition)
875 break;
877 printf_send(_("!Invalid find condition - "
878 "change it and try again\n"));
879 if (!printf_reply(from_parent, TRUE,
880 _("?Check '%s'?"), path))
881 return;
884 if (mc_lstat(path, &info.stats))
886 send_error();
887 printf_send(_("'(while checking '%s')\n"), path);
888 return;
891 info.fullpath = path;
892 time(&info.now); /* XXX: Not for each check! */
894 info.leaf = g_basename(path);
895 info.prune = FALSE;
896 if (find_test_condition(find_condition, &info))
897 printf_send("=%s", path);
899 if (S_ISDIR(info.stats.st_mode) && !info.prune)
901 char *safe_path;
902 safe_path = g_strdup(path);
903 for_dir_contents(do_find, safe_path, safe_path);
904 g_free(safe_path);
908 /* Like mode_compile(), but ignores spaces and bracketed bits */
909 static struct mode_change *nice_mode_compile(const char *mode_string,
910 unsigned int masked_ops)
912 GString *new;
913 int brackets = 0;
914 struct mode_change *retval = NULL;
916 new = g_string_new(NULL);
918 for (; *mode_string; mode_string++)
920 if (*mode_string == '(')
921 brackets++;
922 if (*mode_string == ')')
924 brackets--;
925 if (brackets < 0)
926 break;
927 continue;
930 if (brackets == 0 && *mode_string != ' ')
931 g_string_append_c(new, *mode_string);
934 if (brackets == 0)
935 retval = mode_compile(new->str, masked_ops);
936 g_string_free(new, TRUE);
937 return retval;
940 static void do_chmod(const char *path, const char *unused)
942 struct stat info;
943 mode_t new_mode;
945 check_flags();
947 if (mc_lstat(path, &info))
949 send_error();
950 return;
952 if (S_ISLNK(info.st_mode))
953 return;
955 if (!quiet)
957 if (!printf_reply(from_parent, FALSE,
958 _("?Change permissions of '%s'?"), path))
959 return;
961 else if (!o_brief)
962 printf_send(_("'Changing permissions of '%s'\n"), path);
964 for (;;)
966 if (new_entry_string)
968 if (mode_change)
969 mode_free(mode_change);
970 mode_change = nice_mode_compile(new_entry_string,
971 MODE_MASK_ALL);
972 null_g_free(&new_entry_string);
975 if (mode_change)
976 break;
978 printf_send(
979 _("!Invalid mode command - change it and try again\n"));
980 if (!printf_reply(from_parent, TRUE,
981 _("?Change permissions of '%s'?"), path))
982 return;
985 if (mc_lstat(path, &info))
987 send_error();
988 return;
990 if (S_ISLNK(info.st_mode))
991 return;
993 new_mode = mode_adjust(info.st_mode, mode_change);
994 if (chmod(path, new_mode))
996 send_error();
997 return;
1000 send_check_path(path);
1002 if (S_ISDIR(info.st_mode))
1004 send_mount_path(path);
1006 if (o_recurse)
1008 guchar *safe_path;
1009 safe_path = g_strdup(path);
1010 for_dir_contents(do_chmod, safe_path, safe_path);
1011 g_free(safe_path);
1016 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1017 * is set then that is the new leafname, otherwise the leafname stays
1018 * the same.
1020 static const char *make_dest_path(const char *object, const char *dir)
1022 const char *leaf;
1024 if (action_leaf)
1025 leaf = action_leaf;
1026 else
1028 leaf = strrchr(object, '/');
1029 if (!leaf)
1030 leaf = object; /* Error? */
1031 else
1032 leaf++;
1035 return make_path(dir, leaf);
1038 /* If action_leaf is not NULL it specifies the new leaf name */
1039 static void do_copy2(const char *path, const char *dest)
1041 const char *dest_path;
1042 struct stat info;
1043 struct stat dest_info;
1045 check_flags();
1047 dest_path = make_dest_path(path, dest);
1049 if (mc_lstat(path, &info))
1051 send_error();
1052 return;
1055 if (mc_lstat(dest_path, &dest_info) == 0)
1057 int err;
1058 gboolean merge;
1060 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1062 if (!merge && o_newer && info.st_mtime > dest_info.st_mtime)
1064 /* Newer; keep going */
1066 else
1068 if (!printf_reply(from_parent, TRUE,
1069 _("?'%s' already exists - %s?"),
1070 dest_path,
1071 merge ? _("merge contents")
1072 : _("overwrite")))
1073 return;
1076 if (!merge)
1078 if (S_ISDIR(dest_info.st_mode))
1079 err = rmdir(dest_path);
1080 else
1081 err = unlink(dest_path);
1083 if (err)
1085 send_error();
1086 if (errno != ENOENT)
1087 return;
1088 printf_send(_("'Trying copy anyway...\n"));
1092 else if (!quiet)
1094 if (!printf_reply(from_parent, FALSE,
1095 _("?Copy %s as %s?"), path, dest_path))
1096 return;
1098 else
1099 printf_send(_("'Copying %s as %s\n"), path, dest_path);
1101 if (S_ISDIR(info.st_mode))
1103 mode_t mode = info.st_mode;
1104 char *safe_path, *safe_dest;
1105 struct stat dest_info;
1106 gboolean exists;
1108 safe_path = g_strdup(path);
1109 safe_dest = g_strdup(dest_path);
1111 exists = !mc_lstat(dest_path, &dest_info);
1113 if (exists && !S_ISDIR(dest_info.st_mode))
1114 printf_send(_("!ERROR: Destination already exists, "
1115 "but is not a directory\n"));
1116 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1117 send_error();
1118 else
1120 if (!exists)
1121 /* (just been created then) */
1122 send_check_path(dest_path);
1124 action_leaf = NULL;
1125 for_dir_contents(do_copy2, safe_path, safe_dest);
1126 /* Note: dest_path now invalid... */
1128 if (!exists)
1130 struct utimbuf utb;
1132 /* We may have created the directory with
1133 * more permissions than the source so that
1134 * we could write to it... change it back now.
1136 if (chmod(safe_dest, mode))
1138 /* Some filesystems don't support
1139 * SetGID and SetUID bits. Ignore
1140 * these errors.
1142 if (errno != EPERM)
1143 send_error();
1146 /* Also, try to preserve the timestamps */
1147 utb.actime = info.st_atime;
1148 utb.modtime = info.st_mtime;
1150 utime(safe_dest, &utb);
1154 g_free(safe_path);
1155 g_free(safe_dest);
1157 else if (S_ISLNK(info.st_mode))
1159 char *target;
1161 /* Not all versions of cp(1) can make symlinks,
1162 * so we special-case it.
1165 target = readlink_dup(path);
1166 if (target)
1168 if (symlink(target, dest_path))
1169 send_error();
1170 else
1171 send_check_path(dest_path);
1173 g_free(target);
1175 else
1176 send_error();
1178 else
1180 guchar *error;
1182 error = copy_file(path, dest_path);
1184 if (error)
1186 printf_send(_("!%s\nFailed to copy '%s'\n"),
1187 error, path);
1188 g_free(error);
1190 else
1191 send_check_path(dest_path);
1195 /* If action_leaf is not NULL it specifies the new leaf name */
1196 static void do_move2(const char *path, const char *dest)
1198 const char *dest_path;
1199 const char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1200 struct stat info2;
1201 gboolean is_dir;
1202 char *err;
1204 check_flags();
1206 dest_path = make_dest_path(path, dest);
1208 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1210 if (access(dest_path, F_OK) == 0)
1212 struct stat info;
1213 int err;
1215 if (mc_lstat(dest_path, &info))
1217 send_error();
1218 return;
1221 if (!is_dir && o_newer && info2.st_mtime > info.st_mtime)
1223 /* Newer; keep going */
1225 else if (!printf_reply(from_parent, TRUE,
1226 _("?'%s' already exists - overwrite?"),
1227 dest_path))
1228 return;
1230 if (S_ISDIR(info.st_mode))
1231 err = rmdir(dest_path);
1232 else
1233 err = unlink(dest_path);
1235 if (err)
1237 send_error();
1238 if (errno != ENOENT)
1239 return;
1240 printf_send(_("'Trying move anyway...\n"));
1243 else if (!quiet)
1245 if (!printf_reply(from_parent, FALSE,
1246 _("?Move %s as %s?"), path, dest_path))
1247 return;
1249 else
1250 printf_send(_("'Moving %s as %s\n"), path, dest_path);
1252 argv[2] = path;
1253 argv[3] = dest_path;
1255 err = fork_exec_wait(argv);
1256 if (err)
1258 printf_send(_("!%s\nFailed to move %s as %s\n"),
1259 err, path, dest_path);
1260 g_free(err);
1262 else
1264 send_check_path(dest_path);
1266 if (is_dir)
1267 send_mount_path(path);
1268 else
1269 send_check_path(path);
1273 /* Copy path to dest.
1274 * Check that path not copied into itself.
1276 static void do_copy(const char *path, const char *dest)
1278 if (is_sub_dir(make_dest_path(path, dest), path))
1279 printf_send(_("!ERROR: Can't copy object into itself\n"));
1280 else
1282 do_copy2(path, dest);
1283 send_check_path(dest);
1287 /* Move path to dest.
1288 * Check that path not moved into itself.
1290 static void do_move(const char *path, const char *dest)
1292 if (is_sub_dir(make_dest_path(path, dest), path))
1293 printf_send(
1294 _("!ERROR: Can't move/rename object into itself\n"));
1295 else
1297 do_move2(path, dest);
1298 send_check_path(dest);
1302 static void do_link(const char *path, const char *dest)
1304 const char *dest_path;
1306 check_flags();
1308 dest_path = make_dest_path(path, dest);
1310 if (quiet)
1311 printf_send(_("'Linking %s as %s\n"), path, dest_path);
1312 else if (!printf_reply(from_parent, FALSE,
1313 _("?Link %s as %s?"), path, dest_path))
1314 return;
1316 if (symlink(path, dest_path))
1317 send_error();
1318 else
1319 send_check_path(dest_path);
1322 /* Mount/umount this item (depending on 'mount') */
1323 static void do_mount(const guchar *path, gboolean mount)
1325 const char *argv[3] = {NULL, NULL, NULL};
1326 char *err;
1328 check_flags();
1330 argv[0] = mount ? "mount" : "umount";
1331 argv[1] = path;
1333 if (quiet)
1334 printf_send(mount ? _("'Mounting %s\n")
1335 : _("'Unmounting %s\n"),
1336 path);
1337 else if (!printf_reply(from_parent, FALSE,
1338 mount ? _("?Mount %s?")
1339 : _("?Unmount %s?"),
1340 path))
1341 return;
1343 err = fork_exec_wait(argv);
1344 if (err)
1346 printf_send(mount ?
1347 _("!%s\nMount failed\n") :
1348 _("!%s\nUnmount failed\n"), err);
1349 g_free(err);
1351 else
1353 printf_send("M%s", path);
1354 if (mount && mount_open_dir)
1355 printf_send("o%s", path);
1359 /* CHILD MAIN LOOPS */
1361 /* After forking, the child calls one of these functions */
1363 /* We use a double for total size in order to count beyond 4Gb */
1364 static void usage_cb(gpointer data)
1366 GList *paths = (GList *) data;
1367 double total_size = 0;
1369 dir_counter = file_counter = 0;
1371 for (; paths; paths = paths->next)
1373 guchar *path = (guchar *) paths->data;
1375 send_dir(path);
1377 size_tally = 0;
1379 do_usage(path, NULL);
1381 printf_send("'%s: %s\n",
1382 g_basename(path),
1383 format_double_size(size_tally));
1384 total_size += size_tally;
1387 g_string_printf(message, _("'\nTotal: %s ("),
1388 format_double_size(total_size));
1390 if (file_counter)
1391 g_string_append_printf(message,
1392 "%ld %s%s", file_counter,
1393 file_counter == 1 ? _("file") : _("files"),
1394 dir_counter ? ", " : ")\n");
1396 if (file_counter == 0 && dir_counter == 0)
1397 g_string_append(message, _("no directories)\n"));
1398 else if (dir_counter)
1399 g_string_append_printf(message,
1400 "%ld %s)\n", dir_counter,
1401 dir_counter == 1 ? _("directory")
1402 : _("directories"));
1404 send();
1407 #ifdef DO_MOUNT_POINTS
1408 static void mount_cb(gpointer data)
1410 GList *paths = (GList *) data;
1411 gboolean mount_points = FALSE;
1413 for (; paths; paths = paths->next)
1415 guchar *path = (guchar *) paths->data;
1417 if (mount_is_mounted(path, NULL, NULL))
1418 do_mount(path, FALSE); /* Unmount */
1419 else if (g_hash_table_lookup(fstab_mounts, path))
1420 do_mount(path, TRUE); /* Mount */
1421 else
1422 continue;
1424 mount_points = TRUE;
1427 if (mount_points)
1428 send_done();
1429 else
1430 printf_send(_("!No mount points selected!\n"));
1432 #endif
1434 /* (use g_dirname() instead?) */
1435 static guchar *dirname(guchar *path)
1437 guchar *slash;
1439 slash = strrchr(path, '/');
1440 g_return_val_if_fail(slash != NULL, g_strdup(path));
1442 if (slash != path)
1443 return g_strndup(path, slash - path);
1444 return g_strdup("/");
1447 static void delete_cb(gpointer data)
1449 GList *paths = (GList *) data;
1451 for (; paths; paths = paths->next)
1453 guchar *path = (guchar *) paths->data;
1454 guchar *dir;
1456 dir = dirname(path);
1457 send_dir(dir);
1459 do_delete(path, dir);
1461 g_free(dir);
1464 send_done();
1467 static void find_cb(gpointer data)
1469 GList *all_paths = (GList *) data;
1470 GList *paths;
1472 while (1)
1474 for (paths = all_paths; paths; paths = paths->next)
1476 guchar *path = (guchar *) paths->data;
1478 send_dir(path);
1480 do_find(path, NULL);
1483 if (!printf_reply(from_parent, TRUE,
1484 _("?Another search?")))
1485 break;
1486 printf_send("#");
1489 send_done();
1492 static void chmod_cb(gpointer data)
1494 GList *paths = (GList *) data;
1496 for (; paths; paths = paths->next)
1498 guchar *path = (guchar *) paths->data;
1499 struct stat info;
1501 send_dir(path);
1503 if (mc_stat(path, &info) != 0)
1504 send_error();
1505 else if (S_ISLNK(info.st_mode))
1506 printf_send(_("!'%s' is a symbolic link\n"),
1507 g_basename(path));
1508 else
1509 do_chmod(path, NULL);
1512 send_done();
1515 static void list_cb(gpointer data)
1517 GList *paths = (GList *) data;
1519 for (; paths; paths = paths->next)
1521 send_dir((char *) paths->data);
1523 action_do_func((char *) paths->data, action_dest);
1526 send_done();
1529 /* EXTERNAL INTERFACE */
1531 void action_find(GList *paths)
1533 GUIside *gui_side;
1534 GtkWidget *abox;
1536 if (!paths)
1538 report_error(_("You need to select some items "
1539 "to search through"));
1540 return;
1543 if (!last_find_string)
1544 last_find_string = g_strdup("'core'");
1546 new_entry_string = last_find_string;
1548 abox = abox_new(_("Find"), FALSE);
1549 gui_side = start_action(abox, find_cb, paths,
1550 o_action_force.int_value,
1551 o_action_brief.int_value,
1552 o_action_recurse.int_value,
1553 o_action_newer.int_value);
1554 if (!gui_side)
1555 return;
1557 abox_add_results(ABOX(abox));
1559 gui_side->default_string = &last_find_string;
1560 abox_add_entry(ABOX(abox), last_find_string,
1561 new_help_button(show_condition_help, NULL));
1562 g_signal_connect(ABOX(abox)->entry, "changed",
1563 G_CALLBACK(entry_changed), gui_side);
1564 set_find_string_colour(ABOX(abox)->entry, last_find_string);
1566 gui_side->show_info = TRUE;
1567 gui_side->entry_string_func = set_find_string_colour;
1569 number_of_windows++;
1570 gtk_widget_show_all(abox);
1573 /* Count disk space used by selected items */
1574 void action_usage(GList *paths)
1576 GUIside *gui_side;
1577 GtkWidget *abox;
1579 if (!paths)
1581 report_error(_("You need to select some items to count"));
1582 return;
1585 abox = abox_new(_("Disk Usage"), TRUE);
1587 gui_side = start_action(abox, usage_cb, paths,
1588 o_action_force.int_value,
1589 o_action_brief.int_value,
1590 o_action_recurse.int_value,
1591 o_action_newer.int_value);
1592 if (!gui_side)
1593 return;
1595 gui_side->show_info = TRUE;
1597 number_of_windows++;
1599 gtk_widget_show(abox);
1602 /* Mount/unmount listed items (paths).
1603 * Free the list after this function returns.
1604 * If open_dir is TRUE and the dir is successfully mounted, open it.
1605 * quiet can be -1 for default.
1607 void action_mount(GList *paths, gboolean open_dir, int quiet)
1609 #ifdef DO_MOUNT_POINTS
1610 GUIside *gui_side;
1611 GtkWidget *abox;
1613 if (quiet == -1)
1614 quiet = o_action_mount.int_value;
1616 mount_open_dir = open_dir;
1618 abox = abox_new(_("Mount / Unmount"), quiet);
1619 gui_side = start_action(abox, mount_cb, paths,
1620 o_action_force.int_value,
1621 o_action_brief.int_value,
1622 o_action_recurse.int_value,
1623 o_action_newer.int_value);
1624 if (!gui_side)
1625 return;
1627 number_of_windows++;
1628 gtk_widget_show(abox);
1629 #else
1630 report_error(
1631 _("ROX-Filer does not yet support mount points on your "
1632 "system. Sorry."));
1633 #endif /* DO_MOUNT_POINTS */
1636 /* Delete these paths */
1637 void action_delete(GList *paths)
1639 GUIside *gui_side;
1640 GtkWidget *abox;
1642 if (!remove_pinned_ok(paths))
1643 return;
1645 abox = abox_new(_("Delete"), o_action_delete.int_value);
1646 gui_side = start_action(abox, delete_cb, paths,
1647 o_action_force.int_value,
1648 o_action_brief.int_value,
1649 o_action_recurse.int_value,
1650 o_action_newer.int_value);
1651 if (!gui_side)
1652 return;
1654 abox_add_flag(ABOX(abox),
1655 _("Force"), _("Don't confirm deletion of non-writeable items"),
1656 'F', o_action_force.int_value);
1657 abox_add_flag(ABOX(abox),
1658 _("Brief"), _("Only log directories being deleted"),
1659 'B', o_action_brief.int_value);
1661 number_of_windows++;
1662 gtk_widget_show(abox);
1665 /* Change the permissions of the selected items */
1666 void action_chmod(GList *paths, gboolean force_recurse, const char *action)
1668 GtkWidget *abox;
1669 GUIside *gui_side;
1670 static GList *presets = NULL;
1671 gboolean recurse = force_recurse || o_action_recurse.int_value;
1673 if (!paths)
1675 report_error(_("You need to select the items "
1676 "whose permissions you want to change"));
1677 return;
1680 if (!presets)
1682 presets = g_list_append(presets, (gchar *)
1683 _("a+x (Make executable/searchable)"));
1684 presets = g_list_append(presets, (gchar *)
1685 _("a-x (Make non-executable/non-searchable)"));
1686 presets = g_list_append(presets, (gchar *)
1687 _("u+rw (Give owner read+write)"));
1688 presets = g_list_append(presets, (gchar *)
1689 _("go-rwx (Private - owner access only)"));
1690 presets = g_list_append(presets, (gchar *)
1691 _("go=u-w (Public access, not write)"));
1694 if (!last_chmod_string)
1695 last_chmod_string = g_strdup((guchar *) presets->data);
1697 if (action)
1698 new_entry_string = g_strdup(action);
1699 else
1700 new_entry_string = g_strdup(last_chmod_string);
1702 abox = abox_new(_("Permissions"), FALSE);
1703 gui_side = start_action(abox, chmod_cb, paths,
1704 o_action_force.int_value,
1705 o_action_brief.int_value,
1706 recurse,
1707 o_action_newer.int_value);
1709 if (!gui_side)
1710 goto out;
1712 abox_add_flag(ABOX(abox),
1713 _("Brief"), _("Don't list processed files"),
1714 'B', o_action_brief.int_value);
1715 abox_add_flag(ABOX(abox),
1716 _("Recurse"), _("Also change contents of subdirectories"),
1717 'R', recurse);
1719 gui_side->default_string = &last_chmod_string;
1720 abox_add_combo(ABOX(abox), presets, new_entry_string,
1721 new_help_button(show_chmod_help, NULL));
1723 g_signal_connect(ABOX(abox)->entry, "changed",
1724 G_CALLBACK(entry_changed), gui_side);
1725 #if 0
1726 g_signal_connect_swapped(gui_side->entry, "activate",
1727 G_CALLBACK(gtk_button_clicked),
1728 gui_side->yes);
1729 #endif
1731 number_of_windows++;
1732 gtk_widget_show(abox);
1734 out:
1735 null_g_free(&new_entry_string);
1738 /* If leaf is NULL then the copy has the same name as the original.
1739 * quiet can be -1 for default.
1741 void action_copy(GList *paths, const char *dest, const char *leaf, int quiet)
1743 GUIside *gui_side;
1744 GtkWidget *abox;
1746 if (quiet == -1)
1747 quiet = o_action_copy.int_value;
1749 action_dest = dest;
1750 action_leaf = leaf;
1751 action_do_func = do_copy;
1753 abox = abox_new(_("Copy"), quiet);
1754 gui_side = start_action(abox, list_cb, paths,
1755 o_action_force.int_value,
1756 o_action_brief.int_value,
1757 o_action_recurse.int_value,
1758 o_action_newer.int_value);
1759 if (!gui_side)
1760 return;
1762 abox_add_flag(ABOX(abox),
1763 _("Newer"),
1764 _("Only over-write if source is newer than destination."),
1765 'W', o_action_newer.int_value);
1767 number_of_windows++;
1768 gtk_widget_show(abox);
1771 /* If leaf is NULL then the file is not renamed.
1772 * quiet can be -1 for default.
1774 void action_move(GList *paths, const char *dest, const char *leaf, int quiet)
1776 GUIside *gui_side;
1777 GtkWidget *abox;
1779 if (quiet == -1)
1780 quiet = o_action_move.int_value;
1782 action_dest = dest;
1783 action_leaf = leaf;
1784 action_do_func = do_move;
1786 abox = abox_new(_("Move"), quiet);
1787 gui_side = start_action(abox, list_cb, paths,
1788 o_action_force.int_value,
1789 o_action_brief.int_value,
1790 o_action_recurse.int_value,
1791 o_action_newer.int_value);
1792 if (!gui_side)
1793 return;
1795 abox_add_flag(ABOX(abox),
1796 _("Newer"),
1797 _("Only over-write if source is newer than destination."),
1798 'W', o_action_newer.int_value);
1799 number_of_windows++;
1800 gtk_widget_show(abox);
1803 /* If leaf is NULL then the link will have the same name */
1804 /* XXX: No quiet option here? */
1805 void action_link(GList *paths, const char *dest, const char *leaf)
1807 GtkWidget *abox;
1808 GUIside *gui_side;
1810 action_dest = dest;
1811 action_leaf = leaf;
1812 action_do_func = do_link;
1814 abox = abox_new(_("Link"), o_action_link.int_value);
1815 gui_side = start_action(abox, list_cb, paths,
1816 o_action_force.int_value,
1817 o_action_brief.int_value,
1818 o_action_recurse.int_value,
1819 o_action_newer.int_value);
1820 if (!gui_side)
1821 return;
1823 number_of_windows++;
1824 gtk_widget_show(abox);
1827 void action_init(void)
1829 option_add_int(&o_action_copy, "action_copy", 1);
1830 option_add_int(&o_action_move, "action_move", 1);
1831 option_add_int(&o_action_link, "action_link", 1);
1832 option_add_int(&o_action_delete, "action_delete", 0);
1833 option_add_int(&o_action_mount, "action_mount", 1);
1834 option_add_int(&o_action_force, "action_force", FALSE);
1835 option_add_int(&o_action_brief, "action_brief", FALSE);
1836 option_add_int(&o_action_recurse, "action_recurse", FALSE);
1837 option_add_int(&o_action_newer, "action_newer", FALSE);
1840 #define MAX_ASK 4
1842 /* Check to see if any of the selected items (or their children) are
1843 * on the pinboard or panel. If so, ask for confirmation.
1845 * TRUE if it's OK to lose them.
1847 static gboolean remove_pinned_ok(GList *paths)
1849 GList *ask = NULL, *next;
1850 GString *message;
1851 int i, ask_n = 0;
1852 gboolean retval;
1854 for (; paths; paths = paths->next)
1856 guchar *path = (guchar *) paths->data;
1858 if (icons_require(path))
1860 if (++ask_n > MAX_ASK)
1861 break;
1862 ask = g_list_append(ask, path);
1866 if (!ask)
1867 return TRUE;
1869 if (ask_n > MAX_ASK)
1871 message = g_string_new(_("Deleting items such as "));
1872 ask_n--;
1874 else if (ask_n == 1)
1875 message = g_string_new(_("Deleting the item "));
1876 else
1877 message = g_string_new(_("Deleting the items "));
1879 i = 0;
1880 for (next = ask; next; next = next->next)
1882 guchar *path = (guchar *) next->data;
1883 guchar *leaf;
1885 leaf = strrchr(path, '/');
1886 if (leaf)
1887 leaf++;
1888 else
1889 leaf = path;
1891 g_string_append_c(message, '`');
1892 g_string_append(message, leaf);
1893 g_string_append_c(message, '\'');
1894 i++;
1895 if (i == ask_n - 1 && i > 0)
1896 g_string_append(message, _(" and "));
1897 else if (i < ask_n)
1898 g_string_append(message, ", ");
1901 g_list_free(ask);
1903 if (ask_n == 1)
1904 message = g_string_append(message,
1905 _(" will affect some items on the pinboard "
1906 "or panel - really delete it?"));
1907 else
1909 if (ask_n > MAX_ASK)
1910 message = g_string_append_c(message, ',');
1911 message = g_string_append(message,
1912 _(" will affect some items on the pinboard "
1913 "or panel - really delete them?"));
1916 retval = confirm(message->str, GTK_STOCK_DELETE, NULL);
1918 g_string_free(message, TRUE);
1920 return retval;
1923 void set_find_string_colour(GtkWidget *widget, const guchar *string)
1925 FindCondition *cond;
1927 cond = find_compile(string);
1928 entry_set_error(widget, !cond);
1930 find_condition_free(cond);