r196: Collection widget passes on unused Escape key press events.
[rox-filer.git] / ROX-Filer / src / action.c
blobb5fbee007d96c506bffbb50c58907505fa35360c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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 <unistd.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <signal.h>
37 #include <dirent.h>
38 #include <sys/time.h>
40 #include "action.h"
41 #include "string.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "filer.h"
45 #include "main.h"
46 #include "options.h"
48 /* Options bits */
49 static GtkWidget *create_options();
50 static void update_options();
51 static void set_options();
52 static void save_options();
53 static char *action_auto_quiet(char *data);
55 static OptionsSection options =
57 "Action window options",
58 create_options,
59 update_options,
60 set_options,
61 save_options
64 static gboolean o_auto_copy = TRUE;
65 static gboolean o_auto_move = TRUE;
66 static gboolean o_auto_link = TRUE;
67 static gboolean o_auto_delete = FALSE;
68 static gboolean o_auto_mount = TRUE;
70 static GtkWidget *w_auto_copy = NULL;
71 static GtkWidget *w_auto_move = NULL;
72 static GtkWidget *w_auto_link = NULL;
73 static GtkWidget *w_auto_delete = NULL;
74 static GtkWidget *w_auto_mount = NULL;
76 static GdkColor red = {0, 0xffff, 0, 0};
78 /* Parent->Child messages are one character each:
80 * Q/Y/N Quiet/Yes/No button clicked
81 * F Force deletion of non-writeable items
84 #define SENSITIVE_YESNO(gui_side, state) \
85 do { \
86 gtk_widget_set_sensitive((gui_side)->yes, state); \
87 gtk_widget_set_sensitive((gui_side)->no, state); \
88 } while (0)
90 #define ON(flag) ((flag) ? "on" : "off")
92 typedef struct _GUIside GUIside;
93 typedef void ActionChild(gpointer data);
94 typedef gboolean ForDirCB(char *path, char *dest_path);
96 struct _GUIside
98 int from_child; /* File descriptor */
99 FILE *to_child;
100 int input_tag; /* gdk_input_add() */
101 GtkWidget *vbox, *log, *window, *dir;
102 GtkWidget *quiet, *yes, *no;
103 int child; /* Process ID */
104 int errors;
105 gboolean show_info; /* For Disk Usage */
107 char *next_dir; /* NULL => no timer active */
108 gint next_timer;
111 /* These don't need to be in a structure because we fork() before
112 * using them again.
114 static int from_parent = 0;
115 static FILE *to_parent = NULL;
116 static gboolean quiet = FALSE;
117 static GString *message = NULL;
118 static char *action_dest = NULL;
119 static gboolean (*action_do_func)(char *source, char *dest);
120 static size_t size_tally; /* For Disk Usage */
121 static DirItem *mount_item;
123 static gboolean o_force = FALSE;
125 /* Static prototypes */
126 static gboolean send();
127 static gboolean send_error();
128 static gboolean send_dir(char *dir);
129 static gboolean read_exact(int source, char *buffer, ssize_t len);
130 static void do_mount(FilerWindow *filer_window, DirItem *item);
131 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
132 static gboolean reply(int fd, gboolean ignore_quiet);
134 /* OPTIONS */
136 /* Build up some option widgets to go in the options dialog, but don't
137 * fill them in yet.
139 static GtkWidget *create_options()
141 GtkWidget *vbox, *hbox, *label;
143 vbox = gtk_vbox_new(FALSE, 0);
144 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
146 label = gtk_label_new("Auto-start (Quiet) these actions:");
147 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
149 hbox = gtk_hbox_new(TRUE, 0);
150 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
152 w_auto_copy = gtk_check_button_new_with_label("Copy");
153 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
154 w_auto_move = gtk_check_button_new_with_label("Move");
155 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
156 w_auto_link = gtk_check_button_new_with_label("Link");
157 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
158 w_auto_delete = gtk_check_button_new_with_label("Delete");
159 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
160 w_auto_mount = gtk_check_button_new_with_label("Mount");
161 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
163 return vbox;
166 /* Reflect current state by changing the widgets in the options box */
167 static void update_options()
169 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
170 o_auto_copy);
171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
172 o_auto_move);
173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
174 o_auto_link);
175 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
176 o_auto_delete);
177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
178 o_auto_mount);
181 /* Set current values by reading the states of the widgets in the options box */
182 static void set_options()
184 o_auto_copy = gtk_toggle_button_get_active(
185 GTK_TOGGLE_BUTTON(w_auto_copy));
186 o_auto_move = gtk_toggle_button_get_active(
187 GTK_TOGGLE_BUTTON(w_auto_move));
188 o_auto_link = gtk_toggle_button_get_active(
189 GTK_TOGGLE_BUTTON(w_auto_link));
190 o_auto_delete = gtk_toggle_button_get_active(
191 GTK_TOGGLE_BUTTON(w_auto_delete));
192 o_auto_mount = gtk_toggle_button_get_active(
193 GTK_TOGGLE_BUTTON(w_auto_mount));
196 static void save_options()
198 guchar str[] = "cmldt";
200 if (o_auto_copy)
201 str[0] = 'C';
202 if (o_auto_move)
203 str[1] = 'M';
204 if (o_auto_link)
205 str[2] = 'L';
206 if (o_auto_delete)
207 str[3] = 'D';
208 if (o_auto_mount)
209 str[4] = 'T';
211 option_write("action_auto_quiet", str);
214 static char *action_auto_quiet(char *data)
216 while (*data)
218 char c = *data++;
219 gboolean state;
221 state = isupper(c);
223 switch (tolower(c))
225 case 'c':
226 o_auto_copy = state;
227 break;
228 case 'm':
229 o_auto_move = state;
230 break;
231 case 'l':
232 o_auto_link = state;
233 break;
234 case 'd':
235 o_auto_delete = state;
236 break;
237 case 't':
238 o_auto_mount = state;
239 break;
240 default:
241 return "Unknown flag";
245 return NULL;
248 /* SUPPORT */
250 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
251 * (or the two are the same directory)
253 static gboolean is_sub_dir(char *sub, char *parent)
255 int parent_len;
256 guchar *real_sub, *real_parent;
257 gboolean retval;
259 real_sub = pathdup(sub);
260 real_parent = pathdup(parent);
262 parent_len = strlen(real_parent);
263 if (strncmp(real_parent, real_sub, parent_len))
264 retval = FALSE;
265 else
267 /* real_sub is at least as long as real_parent and all
268 * characters upto real_parent's length match.
271 retval = real_sub[parent_len] == 0
272 || real_sub[parent_len] == '/';
275 g_free(real_sub);
276 g_free(real_parent);
278 return retval;
281 static gboolean display_dir(gpointer data)
283 GUIside *gui_side = (GUIside *) data;
285 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
286 g_free(gui_side->next_dir);
287 gui_side->next_dir = NULL;
289 return FALSE;
292 /* Called when the child sends us a message */
293 static void message_from_child(gpointer data,
294 gint source,
295 GdkInputCondition condition)
297 char buf[5];
298 GUIside *gui_side = (GUIside *) data;
299 GtkWidget *log = gui_side->log;
301 if (read_exact(source, buf, 4))
303 ssize_t message_len;
304 char *buffer;
306 buf[4] = '\0';
307 message_len = strtol(buf, NULL, 16);
308 buffer = g_malloc(message_len + 1);
309 if (message_len > 0 && read_exact(source, buffer, message_len))
311 buffer[message_len] = '\0';
312 if (*buffer == '?')
313 SENSITIVE_YESNO(gui_side, TRUE);
314 else if (*buffer == '+')
316 refresh_dirs(buffer + 1);
317 g_free(buffer);
318 return;
320 else if (*buffer == 'm')
322 filer_check_mounted(buffer + 1);
323 g_free(buffer);
324 return;
326 else if (*buffer == '/')
328 if (gui_side->next_dir)
329 g_free(gui_side->next_dir);
330 else
331 gui_side->next_timer =
332 gtk_timeout_add(500,
333 display_dir,
334 gui_side);
335 gui_side->next_dir = buffer;
336 return;
338 else if (*buffer == '!')
339 gui_side->errors++;
341 gtk_text_insert(GTK_TEXT(log),
342 NULL,
343 *buffer == '!' ? &red : NULL,
344 NULL,
345 buffer + 1, message_len - 1);
346 g_free(buffer);
347 return;
349 g_print("Child died in the middle of a message.\n");
352 /* The child is dead */
353 gui_side->child = 0;
355 fclose(gui_side->to_child);
356 gui_side->to_child = NULL;
357 close(gui_side->from_child);
358 gdk_input_remove(gui_side->input_tag);
359 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
361 if (gui_side->errors)
363 GString *report;
364 report = g_string_new(NULL);
365 g_string_sprintf(report, "There %s %d error%s.\n",
366 gui_side->errors == 1 ? "was" : "were",
367 gui_side->errors,
368 gui_side->errors == 1 ? "" : "s");
369 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
370 report->str, report->len);
372 g_string_free(report, TRUE);
374 else if (gui_side->show_info == FALSE)
375 gtk_widget_destroy(gui_side->window);
378 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
379 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
381 DIR *d;
382 struct dirent *ent;
383 GSList *list = NULL, *next;
385 d = opendir(src_dir);
386 if (!d)
388 send_error();
389 return;
392 send_dir(src_dir);
394 while ((ent = readdir(d)))
396 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
397 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
398 continue;
399 list = g_slist_append(list, g_strdup(make_path(src_dir,
400 ent->d_name)->str));
402 closedir(d);
404 if (!list)
405 return;
407 next = list;
409 while (next)
411 if (cb((char *) next->data, dest_path))
413 g_string_sprintf(message, "+%s", dest_path);
414 send();
417 g_free(next->data);
418 next = next->next;
420 g_slist_free(list);
421 return;
424 /* Read this many bytes into the buffer. TRUE on success. */
425 static gboolean read_exact(int source, char *buffer, ssize_t len)
427 while (len > 0)
429 ssize_t got;
430 got = read(source, buffer, len);
431 if (got < 1)
432 return FALSE;
433 len -= got;
434 buffer += got;
436 return TRUE;
439 /* Send 'message' to our parent process. TRUE on success. */
440 static gboolean send()
442 char len_buffer[5];
443 ssize_t len;
445 g_return_val_if_fail(message->len < 0xffff, FALSE);
447 sprintf(len_buffer, "%04x", message->len);
448 fwrite(len_buffer, 1, 4, to_parent);
449 len = fwrite(message->str, 1, message->len, to_parent);
450 fflush(to_parent);
451 return len == message->len;
454 /* Set the directory indicator at the top of the window */
455 static gboolean send_dir(char *dir)
457 g_string_sprintf(message, "/%s", dir);
458 return send();
461 static gboolean send_error()
463 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
464 return send();
467 static void button_reply(GtkWidget *button, GUIside *gui_side)
469 char *text;
471 if (!gui_side->to_child)
472 return;
474 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
475 g_return_if_fail(text != NULL);
476 fputc(*text, gui_side->to_child);
477 fflush(gui_side->to_child);
479 if (*text == 'Y' || *text == 'N' || *text == 'Q')
480 SENSITIVE_YESNO(gui_side, FALSE);
483 static void process_flag(char flag)
485 switch (flag)
487 case 'Q':
488 quiet = !quiet;
489 break;
490 case 'F':
491 o_force = !o_force;
492 break;
493 default:
494 g_string_sprintf(message,
495 "!ERROR: Bad message '%c'\n", flag);
496 send();
497 break;
501 /* If the parent has sent any flag toggles, read them */
502 static void check_flags(void)
504 fd_set set;
505 int got;
506 char retval;
507 struct timeval tv;
509 FD_ZERO(&set);
511 while (1)
513 FD_SET(from_parent, &set);
514 tv.tv_sec = 0;
515 tv.tv_usec = 0;
516 got = select(from_parent + 1, &set, NULL, NULL, &tv);
518 if (got == -1)
519 g_error("select() failed: %s\n", g_strerror(errno));
520 else if (!got)
521 return;
523 got = read(from_parent, &retval, 1);
524 if (got != 1)
525 g_error("read() error: %s\n", g_strerror(errno));
527 process_flag(retval);
531 /* Read until the user sends a reply. If ignore_quiet is TRUE then
532 * the user MUST click Yes or No, else treat quiet on as Yes.
533 * If the user needs prompting then does send().
535 static gboolean reply(int fd, gboolean ignore_quiet)
537 ssize_t len;
538 char retval;
539 gboolean asked = FALSE;
541 while (ignore_quiet || !quiet)
543 if (!asked)
545 send();
546 asked = TRUE;
549 len = read(fd, &retval, 1);
550 if (len != 1)
552 fprintf(stderr, "read() error: %s\n",
553 g_strerror(errno));
554 _exit(1); /* Parent died? */
557 switch (retval)
559 case 'Q':
560 quiet = !quiet;
561 if (ignore_quiet)
563 g_string_assign(message, "?");
564 send();
566 break;
567 case 'Y':
568 g_string_assign(message, "' Yes\n");
569 send();
570 return TRUE;
571 case 'N':
572 g_string_assign(message, "' No\n");
573 send();
574 return FALSE;
575 default:
576 process_flag(retval);
577 break;
581 if (asked)
583 g_string_assign(message, "' Quiet\n");
584 send();
586 return TRUE;
589 static void destroy_action_window(GtkWidget *widget, gpointer data)
591 GUIside *gui_side = (GUIside *) data;
593 if (gui_side->child)
595 kill(gui_side->child, SIGTERM);
596 fclose(gui_side->to_child);
597 close(gui_side->from_child);
598 gdk_input_remove(gui_side->input_tag);
601 if (gui_side->next_dir)
603 gtk_timeout_remove(gui_side->next_timer);
604 g_free(gui_side->next_dir);
606 g_free(gui_side);
608 if (--number_of_windows < 1)
609 gtk_main_quit();
612 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
613 * (NULL on failure). The child calls func().
615 * If autoq then automatically selects 'Quiet'.
617 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
619 int filedes[4]; /* 0 and 2 are for reading */
620 GUIside *gui_side;
621 int child;
622 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
623 struct sigaction act;
625 if (pipe(filedes))
627 report_error("ROX-Filer", g_strerror(errno));
628 return NULL;
631 if (pipe(filedes + 2))
633 close(filedes[0]);
634 close(filedes[1]);
635 report_error("ROX-Filer", g_strerror(errno));
636 return NULL;
639 child = fork();
640 switch (child)
642 case -1:
643 report_error("ROX-Filer", g_strerror(errno));
644 return NULL;
645 case 0:
646 /* We are the child */
648 quiet = autoq;
650 /* Reset the SIGCHLD handler */
651 act.sa_handler = SIG_DFL;
652 sigemptyset(&act.sa_mask);
653 act.sa_flags = 0;
654 sigaction(SIGCHLD, &act, NULL);
656 message = g_string_new(NULL);
657 close(filedes[0]);
658 close(filedes[3]);
659 to_parent = fdopen(filedes[1], "wb");
660 from_parent = filedes[2];
661 func(data);
662 _exit(0);
665 /* We are the parent */
666 close(filedes[1]);
667 close(filedes[2]);
668 gui_side = g_malloc(sizeof(GUIside));
669 gui_side->from_child = filedes[0];
670 gui_side->to_child = fdopen(filedes[3], "wb");
671 gui_side->log = NULL;
672 gui_side->child = child;
673 gui_side->errors = 0;
674 gui_side->show_info = FALSE;
676 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
677 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
678 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
679 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
680 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
682 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 4);
683 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
685 gui_side->dir = gtk_label_new("<dir>");
686 gui_side->next_dir = NULL;
687 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
688 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
690 hbox = gtk_hbox_new(FALSE, 0);
691 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
693 gui_side->log = gtk_text_new(NULL, NULL);
694 gtk_widget_set_usize(gui_side->log, 400, 100);
695 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
696 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
697 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
699 actions = gtk_hbox_new(TRUE, 4);
700 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
702 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
703 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
704 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
705 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
706 gtk_signal_connect(GTK_OBJECT(button), "clicked",
707 button_reply, gui_side);
708 gui_side->yes = button = gtk_button_new_with_label("Yes");
709 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
710 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
711 gtk_signal_connect(GTK_OBJECT(button), "clicked",
712 button_reply, gui_side);
713 gui_side->no = button = gtk_button_new_with_label("No");
714 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
715 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
716 gtk_signal_connect(GTK_OBJECT(button), "clicked",
717 button_reply, gui_side);
718 SENSITIVE_YESNO(gui_side, FALSE);
720 button = gtk_button_new_with_label("Abort");
721 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
722 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
723 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
725 gui_side->input_tag = gdk_input_add(gui_side->from_child,
726 GDK_INPUT_READ,
727 message_from_child,
728 gui_side);
730 return gui_side;
733 /* ACTIONS ON ONE ITEM */
735 /* These may call themselves recursively, or ask questions, etc.
736 * TRUE iff the directory containing dest_path needs to be rescanned.
739 /* dest_path is the dir containing src_path.
740 * Updates the global size_tally.
742 static gboolean do_usage(char *src_path, char *dest_path)
744 struct stat info;
746 check_flags();
748 if (lstat(src_path, &info))
750 g_string_sprintf(message, "'%s:\n", src_path);
751 send();
752 send_error();
753 return FALSE;
756 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
757 size_tally += info.st_size;
758 else if (S_ISDIR(info.st_mode))
760 g_string_sprintf(message, "?Count contents of %s?",
761 src_path);
762 if (reply(from_parent, FALSE))
764 char *safe_path;
765 safe_path = g_strdup(src_path);
766 for_dir_contents(do_usage, safe_path, safe_path);
767 g_free(safe_path);
771 return FALSE;
774 /* dest_path is the dir containing src_path */
775 static gboolean do_delete(char *src_path, char *dest_path)
777 struct stat info;
778 gboolean write_prot;
780 check_flags();
782 if (lstat(src_path, &info))
784 send_error();
785 return FALSE;
788 write_prot = S_ISLNK(info.st_mode) ? FALSE
789 : access(src_path, W_OK) != 0;
790 if (write_prot || !quiet)
792 g_string_sprintf(message, "?Delete %s'%s'?",
793 write_prot ? "WRITE-PROTECTED " : " ",
794 src_path);
795 if (!reply(from_parent, write_prot && !o_force))
796 return FALSE;
798 else
800 g_string_sprintf(message, "'Removing '%s'\n", src_path);
801 send();
804 if (S_ISDIR(info.st_mode))
806 char *safe_path;
807 safe_path = g_strdup(src_path);
808 for_dir_contents(do_delete, safe_path, safe_path);
809 if (rmdir(safe_path))
811 g_free(safe_path);
812 send_error();
813 return FALSE;
815 g_string_assign(message, "'Directory deleted\n");
816 send();
817 g_free(safe_path);
819 else if (unlink(src_path))
821 send_error();
822 return FALSE;
825 return TRUE;
828 static gboolean do_copy2(char *path, char *dest)
830 char *dest_path;
831 char *leaf;
832 struct stat info;
833 struct stat dest_info;
834 gboolean retval = TRUE;
836 check_flags();
838 leaf = strrchr(path, '/');
839 if (!leaf)
840 leaf = path; /* Error? */
841 else
842 leaf++;
844 dest_path = make_path(dest, leaf)->str;
846 if (lstat(path, &info))
848 send_error();
849 return FALSE;
852 if (lstat(dest_path, &dest_info) == 0)
854 int err;
855 gboolean merge;
857 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
859 g_string_sprintf(message, "?'%s' already exists - %s?",
860 dest_path,
861 merge ? "merge contents" : "overwrite");
863 if (!reply(from_parent, TRUE))
864 return FALSE;
866 if (!merge)
868 if (S_ISDIR(dest_info.st_mode))
869 err = rmdir(dest_path);
870 else
871 err = unlink(dest_path);
873 if (err)
875 send_error();
876 if (errno != ENOENT)
877 return FALSE;
878 g_string_sprintf(message,
879 "'Trying copy anyway...\n");
880 send();
884 else if (!quiet)
886 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
887 if (!reply(from_parent, FALSE))
888 return FALSE;
890 else
892 g_string_sprintf(message, "'Copying %s as %s\n", path,
893 dest_path);
894 send();
897 if (S_ISDIR(info.st_mode))
899 char *safe_path, *safe_dest;
900 struct stat dest_info;
901 gboolean exists;
903 /* (we will do the update ourselves now, rather than
904 * afterwards)
906 retval = FALSE;
908 safe_path = g_strdup(path);
909 safe_dest = g_strdup(dest_path);
911 exists = !lstat(dest_path, &dest_info);
913 if (exists && !S_ISDIR(dest_info.st_mode))
915 g_string_sprintf(message,
916 "!ERROR: Destination already exists, "
917 "but is not a directory\n");
919 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
920 send_error();
921 else
923 if (!exists)
925 /* (just been created then) */
926 g_string_sprintf(message, "+%s", dest);
927 send();
930 for_dir_contents(do_copy2, safe_path, safe_dest);
931 /* Note: dest_path now invalid... */
934 g_free(safe_path);
935 g_free(safe_dest);
937 else if (S_ISLNK(info.st_mode))
939 char target[MAXPATHLEN + 1];
940 int count;
942 /* Not all versions of cp(1) can make symlinks,
943 * so we special-case it.
946 count = readlink(path, target, sizeof(target) - 1);
947 if (count < 0)
949 send_error();
950 retval = FALSE;
952 else
954 target[count] = '\0';
955 if (symlink(target, dest_path))
957 send_error();
958 retval = FALSE;
962 else
964 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
966 argv[2] = path;
967 argv[3] = dest_path;
969 if (fork_exec_wait(argv))
971 g_string_sprintf(message, "!ERROR: Copy failed\n");
972 send();
973 retval = FALSE;
977 return retval;
980 /* Copy path to dest.
981 * Check that path not copied into itself.
983 static gboolean do_copy(char *path, char *dest)
985 if (is_sub_dir(dest, path))
987 g_string_sprintf(message,
988 "!ERROR: Can't copy directory into itself\n");
989 send();
990 return FALSE;
992 return do_copy2(path, dest);
995 /* Move path to dest.
996 * Check that path not moved into itself.
998 static gboolean do_move(char *path, char *dest)
1000 char *dest_path;
1001 char *leaf;
1002 gboolean retval = TRUE;
1003 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1005 if (is_sub_dir(dest, path))
1007 g_string_sprintf(message,
1008 "!ERROR: Can't move directory into itself\n");
1009 send();
1010 return FALSE;
1013 check_flags();
1015 leaf = strrchr(path, '/');
1016 if (!leaf)
1017 leaf = path; /* Error? */
1018 else
1019 leaf++;
1021 dest_path = make_path(dest, leaf)->str;
1023 if (access(dest_path, F_OK) == 0)
1025 struct stat info;
1026 int err;
1028 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1029 dest_path);
1030 if (!reply(from_parent, TRUE))
1031 return FALSE;
1033 if (lstat(dest_path, &info))
1035 send_error();
1036 return FALSE;
1039 if (S_ISDIR(info.st_mode))
1040 err = rmdir(dest_path);
1041 else
1042 err = unlink(dest_path);
1044 if (err)
1046 send_error();
1047 if (errno != ENOENT)
1048 return FALSE;
1049 g_string_sprintf(message,
1050 "'Trying move anyway...\n");
1051 send();
1054 else if (!quiet)
1056 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1057 if (!reply(from_parent, FALSE))
1058 return FALSE;
1060 else
1062 g_string_sprintf(message, "'Moving %s as %s\n", path,
1063 dest_path);
1064 send();
1067 argv[2] = path;
1068 argv[3] = dest;
1070 if (fork_exec_wait(argv) == 0)
1072 g_string_sprintf(message, "+%s", path);
1073 g_string_truncate(message, leaf - path);
1074 send();
1076 else
1078 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1079 path, dest_path);
1080 retval = FALSE;
1082 send();
1084 return retval;
1087 static gboolean do_link(char *path, char *dest)
1089 char *dest_path;
1090 char *leaf;
1092 check_flags();
1094 leaf = strrchr(path, '/');
1095 if (!leaf)
1096 leaf = path; /* Error? */
1097 else
1098 leaf++;
1100 dest_path = make_path(dest, leaf)->str;
1102 if (quiet)
1104 g_string_sprintf(message, "'Linking %s as %s\n", path,
1105 dest_path);
1106 send();
1108 else
1110 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1111 if (!reply(from_parent, FALSE))
1112 return FALSE;
1115 if (symlink(path, dest_path))
1117 send_error();
1118 return FALSE;
1121 return TRUE;
1124 /* Mount/umount this item */
1125 static void do_mount(FilerWindow *filer_window, DirItem *item)
1127 char *argv[3] = {NULL, NULL, NULL};
1129 check_flags();
1131 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1132 argv[1] = make_path(filer_window->path, item->leafname)->str;
1134 if (quiet)
1136 g_string_sprintf(message, "'%sing %s\n", argv[0], argv[1]);
1137 send();
1139 else
1141 g_string_sprintf(message, "?%s %s?", argv[0], argv[1]);
1142 if (!reply(from_parent, FALSE))
1143 return;
1146 if (fork_exec_wait(argv) == 0)
1148 g_string_sprintf(message, "+%s", filer_window->path);
1149 send();
1150 g_string_sprintf(message, "m%s", argv[1]);
1151 send();
1153 else
1155 g_string_sprintf(message, "!ERROR: %s failed\n", argv[0]);
1156 send();
1160 /* CHILD MAIN LOOPS */
1162 /* After forking, the child calls one of these functions */
1164 static void usage_cb(gpointer data)
1166 FilerWindow *filer_window = (FilerWindow *) data;
1167 Collection *collection = filer_window->collection;
1168 DirItem *item;
1169 int left = collection->number_selected;
1170 int i = -1;
1171 off_t total_size = 0;
1173 send_dir(filer_window->path);
1175 while (left > 0)
1177 i++;
1178 if (!collection->items[i].selected)
1179 continue;
1180 item = (DirItem *) collection->items[i].data;
1181 size_tally = 0;
1182 do_usage(make_path(filer_window->path,
1183 item->leafname)->str,
1184 filer_window->path);
1185 g_string_sprintf(message, "'%s: %s\n",
1186 item->leafname,
1187 format_size((unsigned long) size_tally));
1188 send();
1189 total_size += size_tally;
1190 left--;
1193 g_string_sprintf(message, "'\nTotal: %s\n",
1194 format_size((unsigned long) total_size));
1195 send();
1198 #ifdef DO_MOUNT_POINTS
1199 static void mount_cb(gpointer data)
1201 FilerWindow *filer_window = (FilerWindow *) data;
1202 Collection *collection = filer_window->collection;
1203 DirItem *item;
1204 int i;
1205 gboolean mount_points = FALSE;
1207 send_dir(filer_window->path);
1209 if (mount_item)
1210 do_mount(filer_window, mount_item);
1211 else
1213 for (i = 0; i < collection->number_of_items; i++)
1215 if (!collection->items[i].selected)
1216 continue;
1217 item = (DirItem *) collection->items[i].data;
1218 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1219 continue;
1220 mount_points = TRUE;
1222 do_mount(filer_window, item);
1225 if (!mount_points)
1227 g_string_sprintf(message,
1228 "!No mount points selected!\n");
1229 send();
1233 g_string_sprintf(message, "'\nDone\n");
1234 send();
1236 #endif
1238 static void delete_cb(gpointer data)
1240 FilerWindow *filer_window = (FilerWindow *) data;
1241 Collection *collection = filer_window->collection;
1242 DirItem *item;
1243 int left = collection->number_selected;
1244 int i = -1;
1246 send_dir(filer_window->path);
1248 while (left > 0)
1250 i++;
1251 if (!collection->items[i].selected)
1252 continue;
1253 item = (DirItem *) collection->items[i].data;
1254 if (do_delete(make_path(filer_window->path,
1255 item->leafname)->str,
1256 filer_window->path))
1258 g_string_sprintf(message, "+%s", filer_window->path);
1259 send();
1261 left--;
1264 g_string_sprintf(message, "'\nDone\n");
1265 send();
1268 static void list_cb(gpointer data)
1270 GSList *paths = (GSList *) data;
1272 while (paths)
1274 send_dir((char *) paths->data);
1276 if (action_do_func((char *) paths->data, action_dest))
1278 g_string_sprintf(message, "+%s", action_dest);
1279 send();
1282 paths = paths->next;
1285 g_string_sprintf(message, "'\nDone\n");
1286 send();
1289 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1291 GtkWidget *check;
1293 check = gtk_check_button_new_with_label(label);
1294 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1295 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1296 button_reply, gui_side);
1297 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1301 /* EXTERNAL INTERFACE */
1303 /* Count disk space used by selected items */
1304 void action_usage(FilerWindow *filer_window)
1306 GUIside *gui_side;
1307 Collection *collection;
1309 collection = filer_window->collection;
1311 if (collection->number_selected < 1)
1313 report_error("ROX-Filer", "You need to select some items "
1314 "to count");
1315 return;
1318 gui_side = start_action(filer_window, usage_cb, TRUE);
1319 if (!gui_side)
1320 return;
1322 gui_side->show_info = TRUE;
1324 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1325 number_of_windows++;
1326 gtk_widget_show_all(gui_side->window);
1329 /* Mount/unmount 'item', or all selected mount points if NULL. */
1330 void action_mount(FilerWindow *filer_window, DirItem *item)
1332 #ifdef DO_MOUNT_POINTS
1333 GUIside *gui_side;
1334 Collection *collection;
1336 collection = filer_window->collection;
1338 if (item == NULL && collection->number_selected < 1)
1340 report_error("ROX-Filer", "You need to select some items "
1341 "to mount or unmount");
1342 return;
1345 mount_item = item;
1346 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1347 if (!gui_side)
1348 return;
1350 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1351 number_of_windows++;
1352 gtk_widget_show_all(gui_side->window);
1353 #else
1354 report_error("ROX-Filer",
1355 "ROX-Filer does not yet support mount points on your "
1356 "system. Sorry.");
1357 #endif /* DO_MOUNT_POINTS */
1360 /* Deletes all selected items in the window */
1361 void action_delete(FilerWindow *filer_window)
1363 GUIside *gui_side;
1364 Collection *collection;
1366 collection = filer_window->collection;
1368 if (collection->number_selected < 1)
1370 report_error("ROX-Filer", "You need to select some items "
1371 "to delete");
1372 return;
1375 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1376 if (!gui_side)
1377 return;
1379 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1380 add_toggle(gui_side,
1381 "Don't confirm deletion of non-writeable items", "F");
1383 number_of_windows++;
1384 gtk_widget_show_all(gui_side->window);
1387 void action_copy(GSList *paths, char *dest)
1389 GUIside *gui_side;
1391 action_dest = dest;
1392 action_do_func = do_copy;
1393 gui_side = start_action(paths, list_cb, o_auto_copy);
1394 if (!gui_side)
1395 return;
1397 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1398 number_of_windows++;
1399 gtk_widget_show_all(gui_side->window);
1402 void action_move(GSList *paths, char *dest)
1404 GUIside *gui_side;
1406 action_dest = dest;
1407 action_do_func = do_move;
1408 gui_side = start_action(paths, list_cb, o_auto_move);
1409 if (!gui_side)
1410 return;
1412 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1413 number_of_windows++;
1414 gtk_widget_show_all(gui_side->window);
1417 void action_link(GSList *paths, char *dest)
1419 GUIside *gui_side;
1421 action_dest = dest;
1422 action_do_func = do_link;
1423 gui_side = start_action(paths, list_cb, o_auto_link);
1424 if (!gui_side)
1425 return;
1427 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1428 number_of_windows++;
1429 gtk_widget_show_all(gui_side->window);
1432 void action_init(void)
1434 options_sections = g_slist_prepend(options_sections, &options);
1435 option_register("action_auto_quiet", action_auto_quiet);