r585: AppInfo file may contain a <Summary> element - the text inside will be
[rox-filer.git] / ROX-Filer / src / action.c
blob52f9ee84f2cc42865f9bfaaef0e47d54f26a215d
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* action.c - code for handling the filer action windows.
23 * These routines generally fork() and talk to us via pipes.
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <sys/time.h>
35 #include <utime.h>
37 #include "global.h"
39 #include "action.h"
40 #include "string.h"
41 #include "support.h"
42 #include "gui_support.h"
43 #include "filer.h"
44 #include "main.h"
45 #include "options.h"
46 #include "modechange.h"
47 #include "find.h"
48 #include "dir.h"
49 #include "icon.h"
51 /* Parent->Child messages are one character each:
53 * Q/Y/N Quiet/Yes/No button clicked
54 * F Force deletion of non-writeable items
57 #define SENSITIVE_YESNO(gui_side, state) \
58 do { \
59 gtk_widget_set_sensitive((gui_side)->yes, state); \
60 gtk_widget_set_sensitive((gui_side)->no, state); \
61 if ((gui_side)->entry) \
62 gtk_widget_set_sensitive((gui_side)->entry, state);\
63 } while (0)
65 typedef struct _GUIside GUIside;
66 typedef void ActionChild(gpointer data);
67 typedef gboolean ForDirCB(char *path, char *dest_path);
69 struct _GUIside
71 int from_child; /* File descriptor */
72 FILE *to_child;
73 int input_tag; /* gdk_input_add() */
74 GtkWidget *vbox, *log, *window, *dir, *log_hbox;
75 GtkWidget *quiet, *yes, *no;
76 int child; /* Process ID */
77 int errors;
78 gboolean show_info; /* For Disk Usage */
80 GtkWidget *entry; /* May be NULL */
81 guchar **default_string; /* Changed when the entry changes */
82 void (*entry_string_func)(GtkWidget *widget, guchar *string);
84 char *next_dir; /* NULL => no timer active */
85 gint next_timer;
87 /* Used by Find */
88 FilerWindow *preview;
89 GtkWidget *results;
92 /* These don't need to be in a structure because we fork() before
93 * using them again.
95 static gboolean mount_open_dir = FALSE;
96 static int from_parent = 0;
97 static FILE *to_parent = NULL;
98 static gboolean quiet = FALSE;
99 static GString *message = NULL;
100 static char *action_dest = NULL;
101 static char *action_leaf = NULL;
102 static gboolean (*action_do_func)(char *source, char *dest);
103 static size_t size_tally; /* For Disk Usage */
105 static struct mode_change *mode_change = NULL; /* For Permissions */
106 static FindCondition *find_condition = NULL; /* For Find */
108 static gboolean o_force = FALSE;
109 static gboolean o_brief = FALSE;
110 static gboolean o_recurse = FALSE;
112 /* Whenever the text in these boxes is changed we store a copy of the new
113 * string to be used as the default next time.
115 static guchar *last_chmod_string = NULL;
116 static guchar *last_find_string = NULL;
118 /* Set to one of the above before forking. This may change over a call to
119 * reply(). It is reset to NULL once the text is parsed.
121 static guchar *new_entry_string = NULL;
123 /* Static prototypes */
124 static gboolean send();
125 static gboolean send_error();
126 static gboolean send_dir(char *dir);
127 static gboolean read_exact(int source, char *buffer, ssize_t len);
128 static void do_mount(guchar *path, gboolean mount);
129 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
130 static gboolean reply(int fd, gboolean ignore_quiet);
131 static gboolean remove_pinned_ok(GList *paths);
133 /* SUPPORT */
135 static void preview_closed(GtkWidget *window, GUIside *gui_side)
137 gui_side->preview = NULL;
140 static void select_row_callback(GtkWidget *widget,
141 gint row, gint column,
142 GdkEventButton *event,
143 GUIside *gui_side)
145 char *leaf, *dir;
147 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
148 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
150 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
152 if (gui_side->preview)
154 if (strcmp(gui_side->preview->path, dir) == 0)
155 display_set_autoselect(gui_side->preview, leaf);
156 else
157 filer_change_to(gui_side->preview, dir, leaf);
158 return;
161 gui_side->preview = filer_opendir(dir);
162 if (gui_side->preview)
164 display_set_autoselect(gui_side->preview, leaf);
165 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window),
166 "destroy",
167 GTK_SIGNAL_FUNC(preview_closed), gui_side);
172 /* This is called whenever the user edits the entry box (if any) - send the
173 * new string.
175 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
177 guchar *text;
179 g_return_if_fail(gui_side->default_string != NULL);
181 text = gtk_entry_get_text(entry);
183 if (gui_side->entry_string_func)
184 gui_side->entry_string_func(GTK_WIDGET(gui_side->entry), text);
186 g_free(*(gui_side->default_string));
187 *(gui_side->default_string) = g_strdup(text);
189 if (!gui_side->to_child)
190 return;
192 fputc('E', gui_side->to_child);
193 fputs(text, gui_side->to_child);
194 fputc('\n', gui_side->to_child);
195 fflush(gui_side->to_child);
198 void show_condition_help(gpointer data)
200 static GtkWidget *help = NULL;
202 if (!help)
204 GtkWidget *text, *vbox, *button, *hbox, *frame;
206 help = gtk_window_new(GTK_WINDOW_DIALOG);
207 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
208 gtk_window_set_title(GTK_WINDOW(help),
209 _("Find expression reference"));
211 vbox = gtk_vbox_new(FALSE, 0);
212 gtk_container_add(GTK_CONTAINER(help), vbox);
214 frame = gtk_frame_new(_("Quick Start"));
215 text = gtk_label_new(
216 _("Just put the name of the file you're looking for in single quotes:\n"
217 "'index.html' (to find a file called 'index.html')"));
218 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
219 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
220 gtk_container_add(GTK_CONTAINER(frame), text);
221 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
223 frame = gtk_frame_new(_("Examples"));
224 text = gtk_label_new(
225 _("'*.htm', '*.html' (finds HTML files)\n"
226 "IsDir 'lib' (finds directories called 'lib')\n"
227 "IsReg 'core' (finds a regular file called 'core')\n"
228 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
229 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
230 "'CVS' prune, isreg (a regular file not in CVS)\n"
231 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
232 gtk_widget_set_style(text, fixed_style);
233 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
234 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
235 gtk_container_add(GTK_CONTAINER(frame), text);
236 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
238 frame = gtk_frame_new(_("Simple Tests"));
239 text = gtk_label_new(
240 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
241 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
242 "\n"
243 "IsEmpty, IsMine\n"
244 "\n"
245 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
246 "contains a slash then the match is against the full path; otherwise it is \n"
247 "against the leafname only."));
248 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
249 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
250 gtk_container_add(GTK_CONTAINER(frame), text);
251 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
253 frame = gtk_frame_new(_("Comparisons"));
254 text = gtk_label_new(
255 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
256 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
257 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
258 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
259 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
260 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
261 gtk_container_add(GTK_CONTAINER(frame), text);
262 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
264 frame = gtk_frame_new(_("Specials"));
265 text = gtk_label_new(
266 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
267 "in 'command' is replaced with the path of the current file)\n"
268 "prune (false, and prevents searching the contents of a directory).")
270 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
271 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
272 gtk_container_add(GTK_CONTAINER(frame), text);
273 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
275 hbox = gtk_hbox_new(FALSE, 20);
276 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
278 text = gtk_label_new(
279 _("See the ROX-Filer manual for full details."));
280 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
281 button = gtk_button_new_with_label(_("Close"));
282 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
283 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
284 gtk_widget_hide, GTK_OBJECT(help));
286 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
287 gtk_widget_hide, GTK_OBJECT(help));
290 if (GTK_WIDGET_VISIBLE(help))
291 gtk_widget_hide(help);
292 gtk_widget_show_all(help);
295 static void show_chmod_help(gpointer data)
297 static GtkWidget *help = NULL;
299 if (!help)
301 GtkWidget *text, *vbox, *button, *hbox, *sep;
303 help = gtk_window_new(GTK_WINDOW_DIALOG);
304 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
305 gtk_window_set_title(GTK_WINDOW(help),
306 _("Permissions command reference"));
308 vbox = gtk_vbox_new(FALSE, 0);
309 gtk_container_add(GTK_CONTAINER(help), vbox);
311 text = gtk_label_new(
312 _("Normally, you can just select a command from the menu (click \n"
313 "on the arrow beside the command box). Sometimes, you need more...\n"
314 "\n"
315 "The format of a command is:\n"
316 "CHANGE, CHANGE, ...\n"
317 "Each CHANGE is:\n"
318 "WHO HOW PERMISSIONS\n"
319 "WHO is some combination of u, g and o which determines whether to\n"
320 "change the permissions for the User (owner), Group or Others.\n"
321 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
322 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
324 "Bracketed text and spaces are ignored.\n\n"
326 "Examples:\n"
327 "u+rw (the file owner gains read and write permission)\n"
328 "g=u (the group permissions are set to be the same as the user's)\n"
329 "o=u-w (others get the same permissions as the owner, but without "
330 "write permission)\n"
331 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
332 "a+X (directories become accessable by everyone; files which were\n"
333 "executable by anyone become executable by everyone)\n"
334 "u+rw, go+r (two commands at once!)\n"
335 "u+s (set the SetUID bit - often has no effect on script files)\n"
336 "755 (set the permissions directly)\n"
338 "\nSee the chmod(1) man page for full details."));
339 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
340 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
342 hbox = gtk_hbox_new(FALSE, 20);
343 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
345 sep = gtk_hseparator_new();
346 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
347 button = gtk_button_new_with_label(_("Close"));
348 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
349 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
350 gtk_widget_hide, GTK_OBJECT(help));
352 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
353 gtk_widget_hide, GTK_OBJECT(help));
356 if (GTK_WIDGET_VISIBLE(help))
357 gtk_widget_hide(help);
358 gtk_widget_show_all(help);
361 static gboolean display_dir(gpointer data)
363 GUIside *gui_side = (GUIside *) data;
365 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
366 g_free(gui_side->next_dir);
367 gui_side->next_dir = NULL;
369 return FALSE;
372 static void add_to_results(GUIside *gui_side, gchar *path)
374 gchar *row[] = {"Leaf", "Dir"};
375 gchar *slash;
376 int len;
378 slash = strrchr(path, '/');
379 g_return_if_fail(slash != NULL);
381 len = slash - path;
382 row[1] = g_strndup(path, MAX(len, 1));
383 row[0] = slash + 1;
385 gtk_clist_append(GTK_CLIST(gui_side->results), row);
387 g_free(row[1]);
390 /* Called when the child sends us a message */
391 static void message_from_child(gpointer data,
392 gint source,
393 GdkInputCondition condition)
395 char buf[5];
396 GUIside *gui_side = (GUIside *) data;
397 GtkWidget *log = gui_side->log;
399 if (read_exact(source, buf, 4))
401 ssize_t message_len;
402 char *buffer;
404 buf[4] = '\0';
405 message_len = strtol(buf, NULL, 16);
406 buffer = g_malloc(message_len + 1);
407 if (message_len > 0 && read_exact(source, buffer, message_len))
409 buffer[message_len] = '\0';
410 if (*buffer == '?')
412 /* Ask a question */
413 SENSITIVE_YESNO(gui_side, TRUE);
414 gtk_window_set_focus(
415 GTK_WINDOW(gui_side->window),
416 gui_side->entry ? gui_side->entry
417 : gui_side->yes);
419 else if (*buffer == '+')
421 /* Update/rescan this item */
422 refresh_dirs(buffer + 1);
423 g_free(buffer);
424 return;
426 else if (*buffer == '=')
428 /* Add to search results */
429 add_to_results(gui_side, buffer + 1);
430 g_free(buffer);
431 return;
433 else if (*buffer == '#')
435 /* Clear search results area */
436 gtk_clist_clear(GTK_CLIST(gui_side->results));
437 g_free(buffer);
438 return;
441 else if (*buffer == 'm' || *buffer == 'M')
443 /* Mount / major changes to this path */
444 if (*buffer == 'M')
445 mount_update(TRUE);
446 filer_check_mounted(buffer + 1);
447 g_free(buffer);
448 return;
450 else if (*buffer == '/')
452 /* Update the current object display */
453 if (gui_side->next_dir)
454 g_free(gui_side->next_dir);
455 else
456 gui_side->next_timer =
457 gtk_timeout_add(500,
458 display_dir,
459 gui_side);
460 gui_side->next_dir = buffer;
461 return;
463 else if (*buffer == 'o')
465 /* Open a filer window */
466 filer_opendir(buffer + 1);
467 g_free(buffer);
468 return;
470 else if (*buffer == '!')
471 gui_side->errors++;
473 gtk_text_insert(GTK_TEXT(log),
474 NULL,
475 *buffer == '!' ? &red : NULL,
476 NULL,
477 buffer + 1, message_len - 1);
478 g_free(buffer);
479 return;
481 g_printerr("Child died in the middle of a message.\n");
484 /* The child is dead */
485 gui_side->child = 0;
487 fclose(gui_side->to_child);
488 gui_side->to_child = NULL;
489 close(gui_side->from_child);
490 gdk_input_remove(gui_side->input_tag);
491 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
493 if (gui_side->errors)
495 guchar *report;
497 if (gui_side->errors == 1)
498 report = g_strdup(_("There was one error.\n"));
499 else
500 report = g_strdup_printf(_("There were %d errors.\n"),
501 gui_side->errors);
503 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
504 report, -1);
506 g_free(report);
508 else if (gui_side->show_info == FALSE)
509 gtk_widget_destroy(gui_side->window);
512 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
513 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
515 DIR *d;
516 struct dirent *ent;
517 GSList *list = NULL, *next;
519 d = mc_opendir(src_dir);
520 if (!d)
522 /* Message displayed is "ERROR reading 'path': message" */
523 g_string_sprintf(message, "!%s '%s': %s\n",
524 _("ERROR reading"),
525 src_dir, g_strerror(errno));
526 send();
527 return;
530 send_dir(src_dir);
532 while ((ent = mc_readdir(d)))
534 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
535 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
536 continue;
537 list = g_slist_append(list, g_strdup(make_path(src_dir,
538 ent->d_name)->str));
540 mc_closedir(d);
542 if (!list)
543 return;
545 next = list;
547 while (next)
549 if (cb((char *) next->data, dest_path))
551 g_string_sprintf(message, "+%s", dest_path);
552 send();
555 g_free(next->data);
556 next = next->next;
558 g_slist_free(list);
559 return;
562 /* Read this many bytes into the buffer. TRUE on success. */
563 static gboolean read_exact(int source, char *buffer, ssize_t len)
565 while (len > 0)
567 ssize_t got;
568 got = read(source, buffer, len);
569 if (got < 1)
570 return FALSE;
571 len -= got;
572 buffer += got;
574 return TRUE;
577 /* Send 'message' to our parent process. TRUE on success. */
578 static gboolean send(void)
580 char len_buffer[5];
581 ssize_t len;
583 g_return_val_if_fail(message->len < 0xffff, FALSE);
585 sprintf(len_buffer, "%04x", message->len);
586 fwrite(len_buffer, 1, 4, to_parent);
587 len = fwrite(message->str, 1, message->len, to_parent);
588 fflush(to_parent);
589 return len == message->len;
592 /* Set the directory indicator at the top of the window */
593 static gboolean send_dir(char *dir)
595 g_string_sprintf(message, "/%s", dir);
596 return send();
599 static gboolean send_error(void)
601 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
602 return send();
605 static void button_reply(GtkWidget *button, GUIside *gui_side)
607 char *text;
609 if (!gui_side->to_child)
610 return;
612 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
613 g_return_if_fail(text != NULL);
614 fputc(*text, gui_side->to_child);
615 fflush(gui_side->to_child);
617 if (*text == 'Y' || *text == 'N' || *text == 'Q')
618 SENSITIVE_YESNO(gui_side, FALSE);
621 static void process_flag(char flag)
623 switch (flag)
625 case 'Q':
626 quiet = !quiet;
627 break;
628 case 'F':
629 o_force = !o_force;
630 break;
631 case 'R':
632 o_recurse = !o_recurse;
633 break;
634 case 'B':
635 o_brief = !o_brief;
636 break;
637 default:
638 g_string_sprintf(message,
639 "!ERROR: Bad message '%c'\n", flag);
640 send();
641 break;
645 /* If the parent has sent any flag toggles, read them */
646 static void check_flags(void)
648 fd_set set;
649 int got;
650 char retval;
651 struct timeval tv;
653 FD_ZERO(&set);
655 while (1)
657 FD_SET(from_parent, &set);
658 tv.tv_sec = 0;
659 tv.tv_usec = 0;
660 got = select(from_parent + 1, &set, NULL, NULL, &tv);
662 if (got == -1)
663 g_error("select() failed: %s\n", g_strerror(errno));
664 else if (!got)
665 return;
667 got = read(from_parent, &retval, 1);
668 if (got != 1)
669 g_error("read() error: %s\n", g_strerror(errno));
671 process_flag(retval);
675 static void read_new_entry_text(void)
677 int len;
678 char c;
679 GString *new;
681 new = g_string_new(NULL);
683 for (;;)
685 len = read(from_parent, &c, 1);
686 if (len != 1)
688 fprintf(stderr, "read() error: %s\n",
689 g_strerror(errno));
690 _exit(1); /* Parent died? */
693 if (c == '\n')
694 break;
695 g_string_append_c(new, c);
698 g_free(new_entry_string);
699 new_entry_string = new->str;
700 g_string_free(new, FALSE);
703 /* Read until the user sends a reply. If ignore_quiet is TRUE then
704 * the user MUST click Yes or No, else treat quiet on as Yes.
705 * If the user needs prompting then does send().
707 static gboolean reply(int fd, gboolean ignore_quiet)
709 ssize_t len;
710 char retval;
711 gboolean asked = FALSE;
713 while (ignore_quiet || !quiet)
715 if (!asked)
717 send();
718 asked = TRUE;
721 len = read(fd, &retval, 1);
722 if (len != 1)
724 fprintf(stderr, "read() error: %s\n",
725 g_strerror(errno));
726 _exit(1); /* Parent died? */
729 switch (retval)
731 case 'Q':
732 quiet = !quiet;
733 if (ignore_quiet)
735 g_string_assign(message, "?");
736 send();
738 break;
739 case 'Y':
740 g_string_sprintf(message, "' %s\n", _("Yes"));
741 send();
742 return TRUE;
743 case 'N':
744 g_string_sprintf(message, "' %s\n", _("No"));
745 send();
746 return FALSE;
747 case 'E':
748 read_new_entry_text();
749 break;
750 default:
751 process_flag(retval);
752 break;
756 if (asked)
758 g_string_sprintf(message, "' %s\n", _("Quiet"));
759 send();
761 return TRUE;
764 static void destroy_action_window(GtkWidget *widget, gpointer data)
766 GUIside *gui_side = (GUIside *) data;
768 if (gui_side->child)
770 kill(gui_side->child, SIGTERM);
771 fclose(gui_side->to_child);
772 close(gui_side->from_child);
773 gdk_input_remove(gui_side->input_tag);
776 if (gui_side->next_dir)
778 gtk_timeout_remove(gui_side->next_timer);
779 g_free(gui_side->next_dir);
782 if (gui_side->preview)
784 gtk_signal_disconnect_by_data(
785 GTK_OBJECT(gui_side->preview->window),
786 gui_side);
787 gui_side->preview = NULL;
790 g_free(gui_side);
792 if (--number_of_windows < 1)
793 gtk_main_quit();
796 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
797 * (NULL on failure). The child calls func().
799 * If autoq then automatically selects 'Quiet'.
801 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
803 int filedes[4]; /* 0 and 2 are for reading */
804 GUIside *gui_side;
805 int child;
806 GtkWidget *vbox, *button, *scrollbar, *actions;
807 struct sigaction act;
809 if (pipe(filedes))
811 report_error(PROJECT, g_strerror(errno));
812 return NULL;
815 if (pipe(filedes + 2))
817 close(filedes[0]);
818 close(filedes[1]);
819 report_error(PROJECT, g_strerror(errno));
820 return NULL;
823 child = fork();
824 switch (child)
826 case -1:
827 report_error(PROJECT, g_strerror(errno));
828 return NULL;
829 case 0:
830 /* We are the child */
832 quiet = autoq;
834 /* Reset the SIGCHLD handler */
835 act.sa_handler = SIG_DFL;
836 sigemptyset(&act.sa_mask);
837 act.sa_flags = 0;
838 sigaction(SIGCHLD, &act, NULL);
840 message = g_string_new(NULL);
841 close(filedes[0]);
842 close(filedes[3]);
843 to_parent = fdopen(filedes[1], "wb");
844 from_parent = filedes[2];
845 func(data);
846 send_dir("");
847 _exit(0);
850 /* We are the parent */
851 close(filedes[1]);
852 close(filedes[2]);
853 gui_side = g_malloc(sizeof(GUIside));
854 gui_side->from_child = filedes[0];
855 gui_side->to_child = fdopen(filedes[3], "wb");
856 gui_side->log = NULL;
857 gui_side->child = child;
858 gui_side->errors = 0;
859 gui_side->show_info = FALSE;
860 gui_side->preview = NULL;
861 gui_side->results = NULL;
862 gui_side->entry = NULL;
863 gui_side->default_string = NULL;
864 gui_side->entry_string_func = NULL;
866 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
867 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
868 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
869 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
870 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
872 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
873 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
875 gui_side->dir = gtk_label_new(_("<dir>"));
876 gui_side->next_dir = NULL;
877 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
878 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
880 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
881 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
883 gui_side->log = gtk_text_new(NULL, NULL);
884 gtk_widget_set_usize(gui_side->log, 400, 100);
885 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
886 gui_side->log, TRUE, TRUE, 0);
887 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
888 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
889 scrollbar, FALSE, TRUE, 0);
891 actions = gtk_hbox_new(TRUE, 4);
892 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
894 gui_side->quiet = button = gtk_toggle_button_new_with_label(_("Quiet"));
895 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
896 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
897 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
898 gtk_signal_connect(GTK_OBJECT(button), "clicked",
899 button_reply, gui_side);
900 gui_side->yes = button = gtk_button_new_with_label(_("Yes"));
901 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
902 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
903 gtk_signal_connect(GTK_OBJECT(button), "clicked",
904 button_reply, gui_side);
905 gui_side->no = button = gtk_button_new_with_label(_("No"));
906 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
907 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
908 gtk_signal_connect(GTK_OBJECT(button), "clicked",
909 button_reply, gui_side);
910 SENSITIVE_YESNO(gui_side, FALSE);
912 button = gtk_button_new_with_label(_("Abort"));
913 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
914 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
915 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
917 gui_side->input_tag = gdk_input_add(gui_side->from_child,
918 GDK_INPUT_READ,
919 message_from_child,
920 gui_side);
922 return gui_side;
925 /* ACTIONS ON ONE ITEM */
927 /* These may call themselves recursively, or ask questions, etc.
928 * TRUE iff the directory containing dest_path needs to be rescanned.
931 /* dest_path is the dir containing src_path.
932 * Updates the global size_tally.
934 static gboolean do_usage(char *src_path, char *dest_path)
936 struct stat info;
938 check_flags();
940 if (mc_lstat(src_path, &info))
942 g_string_sprintf(message, "'%s:\n", src_path);
943 send();
944 send_error();
945 return FALSE;
948 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
949 size_tally += info.st_size;
950 else if (S_ISDIR(info.st_mode))
952 g_string_sprintf(message, _("?Count contents of %s?"),
953 src_path);
954 if (reply(from_parent, FALSE))
956 char *safe_path;
957 safe_path = g_strdup(src_path);
958 for_dir_contents(do_usage, safe_path, safe_path);
959 g_free(safe_path);
963 return FALSE;
966 /* dest_path is the dir containing src_path */
967 static gboolean do_delete(char *src_path, char *dest_path)
969 struct stat info;
970 gboolean write_prot;
972 check_flags();
974 if (mc_lstat(src_path, &info))
976 send_error();
977 return FALSE;
980 write_prot = S_ISLNK(info.st_mode) ? FALSE
981 : access(src_path, W_OK) != 0;
982 if (write_prot || !quiet)
984 g_string_sprintf(message, _("?Delete %s'%s'?"),
985 write_prot ? _("WRITE-PROTECTED ") : " ",
986 src_path);
987 if (!reply(from_parent, write_prot && !o_force))
988 return FALSE;
990 else if (!o_brief)
992 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
993 send();
996 if (S_ISDIR(info.st_mode))
998 char *safe_path;
999 safe_path = g_strdup(src_path);
1000 for_dir_contents(do_delete, safe_path, safe_path);
1001 if (rmdir(safe_path))
1003 g_free(safe_path);
1004 send_error();
1005 return FALSE;
1007 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
1008 safe_path);
1009 send();
1010 g_string_sprintf(message, "m%s", safe_path);
1011 send();
1012 g_free(safe_path);
1014 else if (unlink(src_path))
1016 send_error();
1017 return FALSE;
1020 return TRUE;
1023 /* path is the item to check. If is is a directory then we may recurse
1024 * (unless prune is used).
1026 static gboolean do_find(char *path, char *dummy)
1028 FindInfo info;
1029 char *slash;
1031 check_flags();
1033 if (!quiet)
1035 g_string_sprintf(message, _("?Check '%s'?"), path);
1036 if (!reply(from_parent, FALSE))
1037 return FALSE;
1040 for (;;)
1042 if (new_entry_string)
1044 if (find_condition)
1045 find_condition_free(find_condition);
1046 find_condition = find_compile(new_entry_string);
1047 g_free(new_entry_string);
1048 new_entry_string = NULL;
1051 if (find_condition)
1052 break;
1054 g_string_assign(message, _("!Invalid find condition - "
1055 "change it and try again\n"));
1056 send();
1057 g_string_sprintf(message, _("?Check '%s'?"), path);
1058 if (!reply(from_parent, TRUE))
1059 return FALSE;
1062 if (mc_lstat(path, &info.stats))
1064 send_error();
1065 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1066 send();
1067 return FALSE;
1070 info.fullpath = path;
1071 time(&info.now); /* XXX: Not for each check! */
1073 slash = strrchr(path, '/');
1074 info.leaf = slash ? slash + 1 : path;
1075 info.prune = FALSE;
1076 if (find_test_condition(find_condition, &info))
1078 g_string_sprintf(message, "=%s", path);
1079 send();
1082 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1084 char *safe_path;
1085 safe_path = g_strdup(path);
1086 for_dir_contents(do_find, safe_path, safe_path);
1087 g_free(safe_path);
1090 return FALSE;
1093 /* Like mode_compile(), but ignores spaces and bracketed bits */
1094 struct mode_change *nice_mode_compile(const char *mode_string,
1095 unsigned int masked_ops)
1097 GString *new;
1098 int brackets = 0;
1099 struct mode_change *retval = NULL;
1101 new = g_string_new(NULL);
1103 for (; *mode_string; mode_string++)
1105 if (*mode_string == '(')
1106 brackets++;
1107 if (*mode_string == ')')
1109 brackets--;
1110 if (brackets < 0)
1111 break;
1112 continue;
1115 if (brackets == 0 && *mode_string != ' ')
1116 g_string_append_c(new, *mode_string);
1119 if (brackets == 0)
1120 retval = mode_compile(new->str, masked_ops);
1121 g_string_free(new, TRUE);
1122 return retval;
1125 static gboolean do_chmod(char *path, char *dummy)
1127 struct stat info;
1128 mode_t new_mode;
1130 check_flags();
1132 if (mc_lstat(path, &info))
1134 send_error();
1135 return FALSE;
1137 if (S_ISLNK(info.st_mode))
1138 return FALSE;
1140 if (!quiet)
1142 g_string_sprintf(message,
1143 _("?Change permissions of '%s'?"), path);
1144 if (!reply(from_parent, FALSE))
1145 return FALSE;
1147 else if (!o_brief)
1149 g_string_sprintf(message,
1150 _("'Changing permissions of '%s'\n"),
1151 path);
1152 send();
1155 for (;;)
1157 if (new_entry_string)
1159 if (mode_change)
1160 mode_free(mode_change);
1161 mode_change = nice_mode_compile(new_entry_string,
1162 MODE_MASK_ALL);
1163 g_free(new_entry_string);
1164 new_entry_string = NULL;
1167 if (mode_change)
1168 break;
1170 g_string_assign(message,
1171 _("!Invalid mode command - change it and try again\n"));
1172 send();
1173 g_string_sprintf(message,
1174 _("?Change permissions of '%s'?"), path);
1175 if (!reply(from_parent, TRUE))
1176 return FALSE;
1179 if (mc_lstat(path, &info))
1181 send_error();
1182 return FALSE;
1184 if (S_ISLNK(info.st_mode))
1185 return FALSE;
1187 new_mode = mode_adjust(info.st_mode, mode_change);
1188 if (chmod(path, new_mode))
1190 send_error();
1191 return FALSE;
1194 if (o_recurse && S_ISDIR(info.st_mode))
1196 guchar *safe_path;
1197 safe_path = g_strdup(path);
1198 for_dir_contents(do_chmod, safe_path, safe_path);
1199 g_free(safe_path);
1202 return TRUE;
1205 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1206 * is set then that is the new leafname, otherwise the leafname stays
1207 * the same.
1209 static char *make_dest_path(char *object, char *dir)
1211 char *leaf;
1213 if (action_leaf)
1214 leaf = action_leaf;
1215 else
1217 leaf = strrchr(object, '/');
1218 if (!leaf)
1219 leaf = object; /* Error? */
1220 else
1221 leaf++;
1224 return make_path(dir, leaf)->str;
1227 /* If action_leaf is not NULL it specifies the new leaf name */
1228 static gboolean do_copy2(char *path, char *dest)
1230 char *dest_path;
1231 struct stat info;
1232 struct stat dest_info;
1233 gboolean retval = TRUE;
1235 check_flags();
1237 dest_path = make_dest_path(path, dest);
1239 if (mc_lstat(path, &info))
1241 send_error();
1242 return FALSE;
1245 if (mc_lstat(dest_path, &dest_info) == 0)
1247 int err;
1248 gboolean merge;
1250 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1252 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1253 dest_path,
1254 merge ? _("merge contents") : _("overwrite"));
1256 if (!reply(from_parent, TRUE))
1257 return FALSE;
1259 if (!merge)
1261 if (S_ISDIR(dest_info.st_mode))
1262 err = rmdir(dest_path);
1263 else
1264 err = unlink(dest_path);
1266 if (err)
1268 send_error();
1269 if (errno != ENOENT)
1270 return FALSE;
1271 g_string_sprintf(message,
1272 _("'Trying copy anyway...\n"));
1273 send();
1277 else if (!quiet)
1279 g_string_sprintf(message,
1280 _("?Copy %s as %s?"), path, dest_path);
1281 if (!reply(from_parent, FALSE))
1282 return FALSE;
1284 else
1286 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1287 dest_path);
1288 send();
1291 if (S_ISDIR(info.st_mode))
1293 mode_t mode = info.st_mode;
1294 char *safe_path, *safe_dest;
1295 struct stat dest_info;
1296 gboolean exists;
1298 /* (we will do the update ourselves now, rather than
1299 * afterwards)
1301 retval = FALSE;
1303 safe_path = g_strdup(path);
1304 safe_dest = g_strdup(dest_path);
1306 exists = !mc_lstat(dest_path, &dest_info);
1308 if (exists && !S_ISDIR(dest_info.st_mode))
1310 g_string_sprintf(message,
1311 _("!ERROR: Destination already exists, "
1312 "but is not a directory\n"));
1314 else if (exists == FALSE && mkdir(dest_path, 0700 | mode))
1315 send_error();
1316 else
1318 if (!exists)
1320 /* (just been created then) */
1321 g_string_sprintf(message, "+%s", dest);
1322 send();
1325 action_leaf = NULL;
1326 for_dir_contents(do_copy2, safe_path, safe_dest);
1327 /* Note: dest_path now invalid... */
1329 if (!exists)
1331 struct utimbuf utb;
1333 /* We may have created the directory with
1334 * more permissions than the source so that
1335 * we could write to it... change it back now.
1337 if (chmod(safe_dest, mode))
1338 send_error();
1340 /* Also, try to preserve the timestamps */
1341 utb.actime = info.st_atime;
1342 utb.modtime = info.st_mtime;
1344 utime(safe_dest, &utb);
1348 g_free(safe_path);
1349 g_free(safe_dest);
1351 else if (S_ISLNK(info.st_mode))
1353 char target[MAXPATHLEN + 1];
1354 int count;
1356 /* Not all versions of cp(1) can make symlinks,
1357 * so we special-case it.
1360 count = readlink(path, target, sizeof(target) - 1);
1361 if (count < 0)
1363 send_error();
1364 retval = FALSE;
1366 else
1368 target[count] = '\0';
1369 if (symlink(target, dest_path))
1371 send_error();
1372 retval = FALSE;
1376 else
1378 guchar *error;
1380 error = copy_file(path, dest_path);
1382 if (error)
1384 g_string_sprintf(message, _("!ERROR: %s\n"), error);
1385 send();
1386 retval = FALSE;
1390 return retval;
1393 /* If action_leaf is not NULL it specifies the new leaf name */
1394 static gboolean do_move2(char *path, char *dest)
1396 char *dest_path;
1397 gboolean retval = TRUE;
1398 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1399 struct stat info2;
1400 gboolean is_dir;
1402 check_flags();
1404 dest_path = make_dest_path(path, dest);
1406 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1408 if (access(dest_path, F_OK) == 0)
1410 struct stat info;
1411 int err;
1413 g_string_sprintf(message,
1414 _("?'%s' already exists - overwrite?"),
1415 dest_path);
1416 if (!reply(from_parent, TRUE))
1417 return FALSE;
1419 if (mc_lstat(dest_path, &info))
1421 send_error();
1422 return FALSE;
1425 if (S_ISDIR(info.st_mode))
1426 err = rmdir(dest_path);
1427 else
1428 err = unlink(dest_path);
1430 if (err)
1432 send_error();
1433 if (errno != ENOENT)
1434 return FALSE;
1435 g_string_sprintf(message,
1436 _("'Trying move anyway...\n"));
1437 send();
1440 else if (!quiet)
1442 g_string_sprintf(message,
1443 _("?Move %s as %s?"), path, dest_path);
1444 if (!reply(from_parent, FALSE))
1445 return FALSE;
1447 else
1449 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1450 dest_path);
1451 send();
1454 argv[2] = path;
1455 argv[3] = dest_path;
1457 if (fork_exec_wait(argv) == 0)
1459 char *leaf;
1461 leaf = strrchr(path, '/');
1462 if (!leaf)
1463 leaf = path; /* Error? */
1464 else
1465 leaf++;
1467 g_string_sprintf(message, "+%s", path);
1468 g_string_truncate(message, leaf - path + 1);
1469 send();
1470 if (is_dir) {
1471 g_string_sprintf(message, "m%s", path);
1472 send();
1475 else
1477 g_string_sprintf(message,
1478 _("!ERROR: Failed to move %s as %s\n"),
1479 path, dest_path);
1480 send();
1481 retval = FALSE;
1484 return retval;
1487 /* Copy path to dest.
1488 * Check that path not copied into itself.
1490 static gboolean do_copy(char *path, char *dest)
1492 if (is_sub_dir(make_dest_path(path, dest), path))
1494 g_string_sprintf(message,
1495 _("!ERROR: Can't copy object into itself\n"));
1496 send();
1497 return FALSE;
1499 return do_copy2(path, dest);
1502 /* Move path to dest.
1503 * Check that path not moved into itself.
1505 static gboolean do_move(char *path, char *dest)
1507 if (is_sub_dir(make_dest_path(path, dest), path))
1509 g_string_sprintf(message,
1510 _("!ERROR: Can't move/rename object into itself\n"));
1511 send();
1512 return FALSE;
1514 return do_move2(path, dest);
1517 static gboolean do_link(char *path, char *dest)
1519 char *dest_path;
1520 char *leaf;
1522 check_flags();
1524 leaf = strrchr(path, '/');
1525 if (!leaf)
1526 leaf = path; /* Error? */
1527 else
1528 leaf++;
1530 dest_path = make_path(dest, leaf)->str;
1532 if (quiet)
1534 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1535 dest_path);
1536 send();
1538 else
1540 g_string_sprintf(message,
1541 _("?Link %s as %s?"), path, dest_path);
1542 if (!reply(from_parent, FALSE))
1543 return FALSE;
1546 if (symlink(path, dest_path))
1548 send_error();
1549 return FALSE;
1552 return TRUE;
1555 /* Mount/umount this item (depending on 'mount') */
1556 static void do_mount(guchar *path, gboolean mount)
1558 char *argv[3] = {NULL, NULL, NULL};
1560 check_flags();
1562 argv[0] = mount ? "mount" : "umount";
1563 argv[1] = path;
1565 if (quiet)
1567 g_string_sprintf(message,
1568 mount ? _("'Mounting %s\n")
1569 : _("'Unmounting %s\n"),
1570 path);
1571 send();
1573 else
1575 g_string_sprintf(message,
1576 mount ? _("?Mount %s?\n")
1577 : _("?Unmount %s?\n"),
1578 path);
1579 if (!reply(from_parent, FALSE))
1580 return;
1583 if (fork_exec_wait(argv) == 0)
1585 g_string_sprintf(message, "M%s", path);
1586 send();
1587 if (mount && mount_open_dir)
1589 g_string_sprintf(message, "o%s", path);
1590 send();
1593 else
1595 g_string_sprintf(message, mount ?
1596 _("!ERROR: Mount failed\n") :
1597 _("!ERROR: Unmount failed\n"));
1598 send();
1602 /* CHILD MAIN LOOPS */
1604 /* After forking, the child calls one of these functions */
1606 static void usage_cb(gpointer data)
1608 FilerWindow *filer_window = (FilerWindow *) data;
1609 Collection *collection = filer_window->collection;
1610 DirItem *item;
1611 int left = collection->number_selected;
1612 int i = -1;
1613 off_t total_size = 0;
1615 send_dir(filer_window->path);
1617 while (left > 0)
1619 i++;
1620 if (!collection->items[i].selected)
1621 continue;
1622 item = (DirItem *) collection->items[i].data;
1623 size_tally = 0;
1624 do_usage(make_path(filer_window->path,
1625 item->leafname)->str,
1626 filer_window->path);
1627 g_string_sprintf(message, "'%s: %s\n",
1628 item->leafname,
1629 format_size((unsigned long) size_tally));
1630 send();
1631 total_size += size_tally;
1632 left--;
1635 g_string_sprintf(message, _("'\nTotal: %s\n"),
1636 format_size((unsigned long) total_size));
1637 send();
1640 #ifdef DO_MOUNT_POINTS
1641 static void mount_cb(gpointer data)
1643 GList *paths = (GList *) data;
1644 gboolean mount_points = FALSE;
1646 for (; paths; paths = paths->next)
1648 guchar *path = (guchar *) paths->data;
1650 if (mount_is_mounted(path))
1651 do_mount(path, FALSE); /* Unmount */
1652 else if (g_hash_table_lookup(fstab_mounts, path))
1653 do_mount(path, TRUE); /* Mount */
1654 else
1655 continue;
1657 mount_points = TRUE;
1660 g_string_sprintf(message,
1661 mount_points ? _("'\nDone\n")
1662 : _("!No mount points selected!\n"));
1663 send();
1665 #endif
1667 static guchar *dirname(guchar *path)
1669 guchar *slash;
1671 slash = strrchr(path, '/');
1672 g_return_val_if_fail(slash != NULL, g_strdup(path));
1674 if (slash != path)
1675 return g_strndup(path, slash - path);
1676 return g_strdup("/");
1679 static void delete_cb(gpointer data)
1681 GList *paths = (GList *) data;
1683 while (paths)
1685 guchar *path = (guchar *) paths->data;
1686 guchar *dir;
1688 dir = dirname(path);
1689 send_dir(dir);
1691 if (do_delete(path, dir))
1693 g_string_sprintf(message, "+%s", dir);
1694 send();
1697 g_free(dir);
1698 paths = paths->next;
1701 g_string_sprintf(message, _("'\nDone\n"));
1702 send();
1705 static void find_cb(gpointer data)
1707 FilerWindow *filer_window = (FilerWindow *) data;
1708 Collection *collection = filer_window->collection;
1709 DirItem *item;
1710 int left;
1711 int i;
1713 send_dir(filer_window->path);
1715 while (1)
1717 left = collection->number_selected;
1718 i = -1;
1720 while (left > 0)
1722 i++;
1723 if (!collection->items[i].selected)
1724 continue;
1725 item = (DirItem *) collection->items[i].data;
1726 do_find(make_path(filer_window->path,
1727 item->leafname)->str,
1728 NULL);
1729 left--;
1732 g_string_assign(message, _("?Another search?"));
1733 if (!reply(from_parent, TRUE))
1734 break;
1735 g_string_assign(message, "#");
1736 send();
1739 g_string_sprintf(message, _("'\nDone\n"));
1740 send();
1743 static void chmod_cb(gpointer data)
1745 FilerWindow *filer_window = (FilerWindow *) data;
1746 Collection *collection = filer_window->collection;
1747 DirItem *item;
1748 int left = collection->number_selected;
1749 int i = -1;
1751 send_dir(filer_window->path);
1753 while (left > 0)
1755 i++;
1756 if (!collection->items[i].selected)
1757 continue;
1758 item = (DirItem *) collection->items[i].data;
1759 if (item->flags & ITEM_FLAG_SYMLINK)
1761 g_string_sprintf(message,
1762 _("!'%s' is a symbolic link\n"),
1763 item->leafname);
1764 send();
1766 else if (do_chmod(make_path(filer_window->path,
1767 item->leafname)->str, NULL))
1769 g_string_sprintf(message, "+%s", filer_window->path);
1770 send();
1772 left--;
1775 g_string_sprintf(message, _("'\nDone\n"));
1776 send();
1779 static void list_cb(gpointer data)
1781 GSList *paths = (GSList *) data;
1783 while (paths)
1785 send_dir((char *) paths->data);
1787 if (action_do_func((char *) paths->data, action_dest))
1789 g_string_sprintf(message, "+%s", action_dest);
1790 send();
1793 paths = paths->next;
1796 g_string_sprintf(message, _("'\nDone\n"));
1797 send();
1800 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1802 GtkWidget *check;
1804 check = gtk_check_button_new_with_label(label);
1805 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1806 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1807 button_reply, gui_side);
1808 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1812 /* EXTERNAL INTERFACE */
1814 void action_find(FilerWindow *filer_window)
1816 GUIside *gui_side;
1817 Collection *collection;
1818 GtkWidget *hbox, *label, *scroller;
1819 gchar *titles[2];
1821 titles[0] = _("Name");
1822 titles[1] = _("Directory");
1824 collection = filer_window->collection;
1826 if (collection->number_selected < 1)
1828 report_error(PROJECT, _("You need to select some items "
1829 "to search through"));
1830 return;
1833 if (!last_find_string)
1834 last_find_string = g_strdup("'core'");
1836 new_entry_string = last_find_string;
1837 gui_side = start_action(filer_window, find_cb, FALSE);
1838 if (!gui_side)
1839 return;
1841 gui_side->show_info = TRUE;
1842 gui_side->entry_string_func = set_find_string_colour;
1844 scroller = gtk_scrolled_window_new(NULL, NULL);
1845 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1846 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1847 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1848 gui_side->results = gtk_clist_new_with_titles(
1849 sizeof(titles) / sizeof(*titles), titles);
1850 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1851 gtk_widget_set_usize(gui_side->results, 100, 100);
1852 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1853 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1854 GTK_SELECTION_SINGLE);
1855 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1856 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1857 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1858 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1859 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1861 hbox = gtk_hbox_new(FALSE, 0);
1862 label = gtk_label_new(_("Expression:"));
1863 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1864 gui_side->default_string = &last_find_string;
1865 gui_side->entry = gtk_entry_new();
1866 gtk_widget_set_style(gui_side->entry, fixed_style);
1867 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1868 set_find_string_colour(gui_side->entry, last_find_string);
1869 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1870 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1871 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1872 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1873 entry_changed, gui_side);
1874 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1875 gtk_box_pack_start(GTK_BOX(hbox),
1876 new_help_button(show_condition_help, NULL),
1877 FALSE, TRUE, 4);
1879 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
1880 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1881 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1882 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1883 number_of_windows++;
1884 gtk_widget_show_all(gui_side->window);
1887 /* Count disk space used by selected items */
1888 void action_usage(FilerWindow *filer_window)
1890 GUIside *gui_side;
1891 Collection *collection;
1893 collection = filer_window->collection;
1895 if (collection->number_selected < 1)
1897 report_error(PROJECT,
1898 _("You need to select some items to count"));
1899 return;
1902 gui_side = start_action(filer_window, usage_cb, TRUE);
1903 if (!gui_side)
1904 return;
1906 gui_side->show_info = TRUE;
1908 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
1909 number_of_windows++;
1910 gtk_widget_show_all(gui_side->window);
1913 /* Mount/unmount listed items (paths).
1914 * Free the list after this function returns.
1915 * If open_dir is TRUE and the dir is successfully mounted, open it.
1917 void action_mount(GList *paths, gboolean open_dir)
1919 #ifdef DO_MOUNT_POINTS
1920 GUIside *gui_side;
1922 mount_open_dir = open_dir;
1923 gui_side = start_action(paths, mount_cb,
1924 option_get_int("action_mount"));
1925 if (!gui_side)
1926 return;
1928 gtk_window_set_title(GTK_WINDOW(gui_side->window),
1929 _("Mount / Unmount"));
1930 number_of_windows++;
1931 gtk_widget_show_all(gui_side->window);
1932 #else
1933 report_error(PROJECT,
1934 _("ROX-Filer does not yet support mount points on your "
1935 "system. Sorry."));
1936 #endif /* DO_MOUNT_POINTS */
1939 /* Deletes all selected items in the window */
1940 void action_delete(FilerWindow *filer_window)
1942 GUIside *gui_side;
1943 GList *paths;
1945 paths = filer_selected_items(filer_window);
1947 if (!remove_pinned_ok(paths))
1948 goto out;
1950 gui_side = start_action(paths, delete_cb,
1951 option_get_int("action_delete"));
1952 if (!gui_side)
1953 return;
1955 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
1956 add_toggle(gui_side,
1957 _("Force - don't confirm deletion of non-writeable items"),
1958 "F");
1959 add_toggle(gui_side,
1960 _("Brief - only log directories being deleted"),
1961 "B");
1963 number_of_windows++;
1964 gtk_widget_show_all(gui_side->window);
1966 out:
1967 g_list_foreach(paths, (GFunc) g_free, NULL);
1968 g_list_free(paths);
1971 /* Change the permissions of the selected items */
1972 void action_chmod(FilerWindow *filer_window)
1974 GUIside *gui_side;
1975 Collection *collection;
1976 GtkWidget *hbox, *label, *combo;
1977 static GList *presets = NULL;
1979 collection = filer_window->collection;
1981 if (collection->number_selected < 1)
1983 report_error(PROJECT,
1984 _("You need to select the items "
1985 "whose permissions you want to change"));
1986 return;
1989 if (!presets)
1991 presets = g_list_append(presets,
1992 _("a+x (Make executable/searchable)"));
1993 presets = g_list_append(presets,
1994 _("a-x (Make non-executable/non-searchable)"));
1995 presets = g_list_append(presets,
1996 _("u+rw (Give owner read+write)"));
1997 presets = g_list_append(presets,
1998 _("go-rwx (Private - owner access only)"));
1999 presets = g_list_append(presets,
2000 _("go=u-w (Public access, not write)"));
2003 if (!last_chmod_string)
2004 last_chmod_string = g_strdup((guchar *) presets->data);
2005 new_entry_string = last_chmod_string;
2006 gui_side = start_action(filer_window, chmod_cb, FALSE);
2007 if (!gui_side)
2008 return;
2010 add_toggle(gui_side,
2011 _("Brief - don't list processed files"), "B");
2012 add_toggle(gui_side,
2013 _("Recurse - also change contents of subdirectories"), "R");
2015 hbox = gtk_hbox_new(FALSE, 0);
2016 label = gtk_label_new(_("Command:"));
2017 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
2018 gui_side->default_string = &last_chmod_string;
2020 combo = gtk_combo_new();
2021 gtk_combo_disable_activate(GTK_COMBO(combo));
2022 gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
2023 gtk_combo_set_popdown_strings(GTK_COMBO(combo), presets);
2025 gui_side->entry = GTK_COMBO(combo)->entry;
2026 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
2027 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
2028 gtk_widget_set_sensitive(gui_side->entry, FALSE);
2029 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 4);
2030 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
2031 entry_changed, gui_side);
2032 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
2033 gtk_box_pack_start(GTK_BOX(hbox),
2034 new_help_button(show_chmod_help, NULL),
2035 FALSE, TRUE, 4);
2037 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
2038 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
2039 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
2040 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
2042 number_of_windows++;
2043 gtk_widget_show_all(gui_side->window);
2046 /* If leaf is NULL then the copy has the same name as the original */
2047 void action_copy(GSList *paths, char *dest, char *leaf)
2049 GUIside *gui_side;
2051 action_dest = dest;
2052 action_leaf = leaf;
2053 action_do_func = do_copy;
2054 gui_side = start_action(paths, list_cb, option_get_int("action_copy"));
2055 if (!gui_side)
2056 return;
2058 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2059 number_of_windows++;
2060 gtk_widget_show_all(gui_side->window);
2063 /* If leaf is NULL then the file is not renamed */
2064 void action_move(GSList *paths, char *dest, char *leaf)
2066 GUIside *gui_side;
2068 action_dest = dest;
2069 action_leaf = leaf;
2070 action_do_func = do_move;
2071 gui_side = start_action(paths, list_cb, option_get_int("action_move"));
2072 if (!gui_side)
2073 return;
2075 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2076 number_of_windows++;
2077 gtk_widget_show_all(gui_side->window);
2080 void action_link(GSList *paths, char *dest)
2082 GUIside *gui_side;
2084 action_dest = dest;
2085 action_do_func = do_link;
2086 gui_side = start_action(paths, list_cb, option_get_int("action_link"));
2087 if (!gui_side)
2088 return;
2090 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2091 number_of_windows++;
2092 gtk_widget_show_all(gui_side->window);
2095 void action_init(void)
2097 option_add_int("action_copy", 1, NULL);
2098 option_add_int("action_move", 1, NULL);
2099 option_add_int("action_link", 1, NULL);
2100 option_add_int("action_delete", 0, NULL);
2101 option_add_int("action_mount", 1, NULL);
2104 #define MAX_ASK 4
2106 /* Check to see if any of the selected items (or their children) are
2107 * on the pinboard or panel. If so, ask for confirmation.
2109 * TRUE if it's OK to lose them.
2111 static gboolean remove_pinned_ok(GList *paths)
2113 GList *ask = NULL, *next;
2114 GString *message;
2115 int i, ask_n = 0;
2116 gboolean retval;
2118 while (paths)
2120 guchar *path = (guchar *) paths->data;
2122 if (icons_require(path))
2124 if (++ask_n > MAX_ASK)
2125 break;
2126 ask = g_list_append(ask, path);
2129 paths = paths->next;
2132 if (!ask)
2133 return TRUE;
2135 if (ask_n > MAX_ASK)
2137 message = g_string_new(_("Deleting items such as "));
2138 ask_n--;
2140 else if (ask_n == 1)
2141 message = g_string_new(_("Deleting the item "));
2142 else
2143 message = g_string_new(_("Deleting the items "));
2145 i = 0;
2146 for (next = ask; next; next = next->next)
2148 guchar *path = (guchar *) next->data;
2149 guchar *leaf;
2151 leaf = strrchr(path, '/');
2152 if (leaf)
2153 leaf++;
2154 else
2155 leaf = path;
2157 g_string_append_c(message, '`');
2158 g_string_append(message, leaf);
2159 g_string_append_c(message, '\'');
2160 i++;
2161 if (i == ask_n - 1 && i > 0)
2162 g_string_append(message, _(" and "));
2163 else if (i < ask_n)
2164 g_string_append(message, ", ");
2167 g_list_free(ask);
2169 if (ask_n == 1)
2170 message = g_string_append(message,
2171 _(" will affect some items on the pinboard "
2172 "or panel - really delete it?"));
2173 else
2175 if (ask_n > MAX_ASK)
2176 message = g_string_append_c(message, ',');
2177 message = g_string_append(message,
2178 _(" will affect some items on the pinboard "
2179 "or panel - really delete them?"));
2182 retval = get_choice(PROJECT, message->str,
2183 2, _("OK"), _("Cancel")) == 0;
2185 g_string_free(message, TRUE);
2187 return retval;
2190 void set_find_string_colour(GtkWidget *widget, guchar *string)
2192 static GtkStyle *error_style = NULL;
2193 FindCondition *cond;
2195 if (!error_style)
2197 error_style = gtk_style_copy(fixed_style);
2198 error_style->fg[GTK_STATE_NORMAL] = red;
2201 cond = find_compile(string);
2202 gtk_widget_set_style(widget, cond ? fixed_style : error_style);
2204 if (cond)
2205 find_condition_free(cond);